<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>FunWithLinux.net</title>
	<atom:link href="http://funwithlinux.net/feed/" rel="self" type="application/rss+xml" />
	<link>http://funwithlinux.net</link>
	<description>Get Your Linux On!</description>
	<lastBuildDate>Wed, 22 May 2013 19:25:09 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>Centos 6 Apache Kerberos AD SSO</title>
		<link>http://funwithlinux.net/2013/05/centos-6-apache-kerberos-ad-sso/</link>
		<comments>http://funwithlinux.net/2013/05/centos-6-apache-kerberos-ad-sso/#comments</comments>
		<pubDate>Tue, 21 May 2013 19:22:08 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
				<category><![CDATA[CentOS]]></category>
		<category><![CDATA[How-To]]></category>
		<category><![CDATA[Red Hat]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[guide]]></category>
		<category><![CDATA[kerberos]]></category>
		<category><![CDATA[ldap]]></category>
		<category><![CDATA[reference]]></category>
		<category><![CDATA[sso]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://funwithlinux.net/?p=593</guid>
		<description><![CDATA[I recently setup a RHEL / Centos 6 Apache websever at work that integrates with Active Directory (AD) and Kerberos for a single sign on (SSO) web resource.  This took me a lot more time than I thought it would, but that&#8217;s because the tutorials I was reading were either wrong, or didn&#8217;t apply to ...]]></description>
				<content:encoded><![CDATA[<p>I recently setup a RHEL / Centos 6 Apache websever at work that integrates with Active Directory (AD) and Kerberos for a single sign on (SSO) web resource.  This took me a lot more time than I thought it would, but that&#8217;s because the tutorials I was reading were either wrong, or didn&#8217;t apply to my situation.  I am outlining the steps I took below to help others who may wish to have a similar setup.</p>
<p><span id="more-593"></span></p>
<p>For my setup, I also added the Red Hat VM to the Windows 2008 r2 Active Directory Domain.  You may choose not to do this, but your steps may be different that what I outline here.  I have NOT setup any UNIX schema in Active Directory or installed any 3rd party software on the Domain Controllers.  The AD schema is how it comes out of the box (for the purposes of this article)</p>
<p>Please note, this tutorial assumes you have root privileges and that SELinux is NOT in enforcing mode.</p>
<h3>Packages</h3>
<p>Here are the packages that I installed during this process.  I am not 100% sure they are all required, but I&#8217;m pretty sure they are <img src='http://funwithlinux.net/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />   Feel free to test.  This setup is currently in testing, and if I move it to production, I will definitely outline which packages are actually required.</p>
<ul>
<li>httpd-2.2.15-15.el6_2.1.x86_64</li>
<li>mod_auth_kerb-5.4-9.el6.x86_64</li>
<li>mod_authz_ldap-0.26-15.el6.x86_64</li>
<li>openldap-clients-2.4.23-26.el6_3.2.x86_64</li>
<li>openldap-2.4.23-26.el6_3.2.x86_64</li>
</ul>
<ul>
<li>samba-winbind-clients-3.5.10-125.el6.x86_64</li>
<li>samba-client-3.5.10-125.el6.x86_64</li>
<li>samba-common-3.5.10-125.el6.x86_64</li>
<li>samba-winbind-3.5.10-125.el6.x86_64</li>
<li>samba-3.5.10-125.el6.x86_64</li>
</ul>
<h3>Step 1</h3>
<p>After we have all of our packages install, we need to update the <strong>[global</strong><strong>] </strong>section of <strong>/etc/samba/smb.conf</strong></p>
<pre class="brush: plain; title: ; notranslate">
client ntlmv2 auth = yes
client use spnego principal = no
kerberos method = dedicated keytab
dedicated keytab file = /etc/krb5.keytab
</pre>
<p>The first two lines are for Network Level Authentication, which is the more secure / modern way of connection to Windows 2008 server systems.  This is required for adding the webserver to the domain.  If your domain does not implement ntlvm2 authentication, then you should probably leave this line out.</p>
<p>The second two lines are for designating a keytab file for kerberos.  I&#8217;m not 100% sure this step is required, or is the best setup, but seems to work.</p>
<p>Also, we want to add the following to <strong>/etc/openldap/ldap.conf</strong></p>
<pre class="brush: plain; title: ; notranslate">

REFERRALS off

</pre>
<p>This is required to ignore referral results from our ldap searches, which is how Apache will determine if a user is authorized or not.</p>
<h3>Step 2</h3>
<p>Next, let&#8217;s add our system to Active Directory using the <strong>authconfig-tui </strong>command.  Be mindful of the choices and how they may affect your setup.  Naturally, you must have an account on the domain that has permissions to add hosts.  Also, DNS must be setup properly on your CentOS / RHEL 6 machine.</p>
<dl>
<dd>Options:</dd>
</dl>
<ol>
<li>User Information
<ol>
<li>Use Winbind</li>
</ol>
</li>
<li>Authentication
<ol>
<li>Use Shadow Passwords</li>
<li>Use Winbind Authentication</li>
<li>Local Authorization is sufficient</li>
</ol>
</li>
<li>Security Model: ads</li>
<li>Domain: EXAMPLE</li>
<li>Domain Controllers: DC1.EXAMPLE.COM</li>
<li>ADS Realm: EXAMPLE.COM</li>
<li>Template Shell: /sbin/nologin</li>
<li>Select Join Domain
<ol>
<li>Enter just username, no domain</li>
<li>Enter Password</li>
<li>Save</li>
<li>Select OK</li>
</ol>
</li>
</ol>
<p>You can and should enter more than one domain controller if available.  Unlike a Windows machine, Linux will only use whichever Domain Controller(s) you specify.  Please note the ALL CAPS for steps 4-6.</p>
<p>After the machine has been added to the domain, you should run the following command to tell <strong>winbind</strong> to use our default domain for identify / authenticating AD users:</p>
<p><strong>authconfig &#8211;update &#8211;enablewinbindusedefaultdomain</strong></p>
<h3>Step 3</h3>
<p>Setup Kerberos keytab for Apache&#8217;s user.</p>
<p>Here&#8217;s the deal:  Apache will need an AD user that has read access to directory structure in order to use the LDAP search function to limit users by id or group (or whatever else).  So, we must setup a new (or use an existing) user in AD.  I&#8217;m going to leave that part up to you to figure out, as I write Linux tutorials, not Windows <img src='http://funwithlinux.net/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> .  Anyway, note the new username and password.</p>
<p>Now, we need get a kerberos ticket for our AD user:</p>
<p><strong>kinit mydomainuser</strong></p>
<p>Next, create the keytab entry:</p>
<p><strong>net ads keytab add HTTP -U mydomainuser</strong></p>
<p>And finally, let&#8217;s give our apache user read privileges to our keytab file:</p>
<p><strong>chmod g+r /etc/krb5.keytab &amp;&amp; chgrp apache /etc/krb5.keytab</strong></p>
<h3>Step 4</h3>
<p>Finally, it&#8217;s time to setup our Apache config.  This is the trickiest part, and may take some patience to get nailed down correctly.</p>
<p>Add the following to a VirtualHost or your default config for Apache:</p>
<pre class="brush: plain; title: ; notranslate">

LogLevel debug
 &lt;Directory /&gt;
 AuthType Kerberos
 KrbMethodNegotiate On
 AuthName &quot;EXAMPLE.COM Domain Login&quot;
 KrbMethodK5Passwd On
 KrbAuthRealms EXAMPLE.COM
 Krb5KeyTab /etc/krb5.keytab
 KrbLocalUserMapping on
 require valid-user

 AuthLDAPURL &quot;ldap://dc1.example.com/dc=example,dc=com?sAMAccountName?sub?(objectClass=*)&quot;
 AuthLDAPBindDN mydomainuser@example.com
 AuthLDAPBindPassword myPassW0rd
 require ldap-group CN=Domain Users,CN=Users,DC=example,DC=com
 &lt;/Directory&gt;

</pre>
<p>Okay, a little about this section.  The first line <strong>LogLevel debug</strong> is an apache directive that will give us more useful information in our error log for troubleshooting.  I recommend using this as it will tell you where the problem may be during this ironing-out process.</p>
<p>Next, the <strong>Directory / </strong>line in html brackets tells us which directory our authentication statements apply to.  &#8221;/&#8221; is equal to the document root of the webserver, default /var/www/html or of the virtual host.</p>
<p>The next section is our Kerberos section.  This are the settings for EXAMPLE.COM (note the CAPS).  Of special note, the <strong>KrbLocalUserMapping on </strong>line tells apache that we&#8217;re going to trim the <strong>@EXAMPLE.COM</strong> portion of the name Kerberos gives us, which is important for the next section.</p>
<p>Finally, we have our LDAP section.  This is where things get sticky.  The first line is our ldap search string.  Here, we&#8217;re connecting to our domain controller, and searching for <strong>sAMAccountName</strong> in the specified location.  To the best of my knowledge, sAMAccountName is what our Kerberos section above gives us.</p>
<p>The next two lines should be self explanatory.</p>
<p>The last line tells Apache that we need to require a group from the ldap search output.  In our example, we want only users that are part of the &#8220;Domain Users&#8221; group to be able to access the webpage.  This can be any group, but it must be the exact dn from AD.  Note, spaces don&#8217;t seem to affect this line.  How do you get this line?  We&#8217;ll, that&#8217;s the tricky part.</p>
<p>I recommond using the following command</p>
<p><strong>ldapsearch -H ldap://dc1.example.com -b &#8220;dc=example,dc=com&#8221; -W &#8220;domain users&#8221; &gt; /root/ldapsearch.out</strong></p>
<p>The console will appear to just sit there after entering that command, but it&#8217;s actually waiting for the password of the domain user we requested the ticket for earlier.  After you enter your password, press return.  The search should return all objects that have a reference of domain users.  Then, you should be able to search the output results (using <strong>vi </strong> or <strong>less</strong>) for Domain Users, using the &#8216;/&#8217; key <img src='http://funwithlinux.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Want to have Apache use LDAP over TLS?  Check here:  <a href="http://people.adams.edu/~cdmiller/posts/Apache2-mod-authnz-ldap-TLS/">http://people.adams.edu/~cdmiller/posts/Apache2-mod-authnz-ldap-TLS/</a></p>
<h3>Sources:</h3>
<p>Big thank you to the following pages that I used to sort through this madness:</p>
<p><a href="http://blogs.freebsdish.org/tmclaugh/2010/07/15/mod_auth_kerb-ad-and-ldap-authorization/">http://blogs.freebsdish.org/tmclaugh/2010/07/15/mod_auth_kerb-ad-and-ldap-authorization/</a></p>
<p><a href="http://www.tuxevara.de/2012/06/apache-authnz_ldap-and-active-directory/">http://www.tuxevara.de/2012/06/apache-authnz_ldap-and-active-directory/</a></p>
<p><a href="http://www.microhowto.info/howto/configure_apache_to_use_kerberos_authentication.html">http://www.microhowto.info/howto/configure_apache_to_use_kerberos_authentication.html</a></p>
<p><a href="http://acksyn.org/?p=460">http://acksyn.org/?p=460</a></p>
<p><a href="http://sl.mvps.org/docs/LinuxApacheKerberosAD.htm">http://sl.mvps.org/docs/LinuxApacheKerberosAD.htm</a></p>
<p>Resources:</p>
<p><a href="http://web.mit.edu/kerberos/krb5-1.5/krb5-1.5.4/doc/krb5-install/The-Keytab-File.html">http://web.mit.edu/kerberos/krb5-1.5/krb5-1.5.4/doc/krb5-install/The-Keytab-File.html</a></p>
<p><a href="http://kb.iu.edu/data/aumh.html">http://kb.iu.edu/data/aumh.html</a></p>
]]></content:encoded>
			<wfw:commentRss>http://funwithlinux.net/2013/05/centos-6-apache-kerberos-ad-sso/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Install vSphere CLI in CentOS 6</title>
		<link>http://funwithlinux.net/2013/05/install-vsphere-cli-in-centos-6/</link>
		<comments>http://funwithlinux.net/2013/05/install-vsphere-cli-in-centos-6/#comments</comments>
		<pubDate>Tue, 07 May 2013 16:04:19 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
				<category><![CDATA[CentOS]]></category>
		<category><![CDATA[How-To]]></category>
		<category><![CDATA[Red Hat]]></category>
		<category><![CDATA[Virtualization]]></category>
		<category><![CDATA[command line]]></category>
		<category><![CDATA[virtualization]]></category>
		<category><![CDATA[VMware]]></category>
		<category><![CDATA[vSphere]]></category>

		<guid isPermaLink="false">http://funwithlinux.net/?p=584</guid>
		<description><![CDATA[I recently installed VMware&#8217;s vSphere CLI 5.1 tools on a CentOS 6 x86_64 VM.  Despite the rather reassuring documentation released by VMware, installing these tools was no simple task.  I will outline the steps that I took for a successful installation of the vSphere CLI software, as some of it is not very obvious, especially ...]]></description>
				<content:encoded><![CDATA[<p>I recently installed VMware&#8217;s vSphere CLI 5.1 tools on a CentOS 6 x86_64 VM.  Despite the rather reassuring documentation released by VMware, installing these tools was no simple task.  I will outline the steps that I took for a successful installation of the vSphere CLI software, as some of it is not very obvious, especially for beginners.</p>
<p><span id="more-584"></span></p>
<p>After registering and downloading the appropriate file from VMWare, you&#8217;re left with a file that ends in .gz.  No, this is not an executable, this is a gzipped tar file, named outside of the regular conventions.  This should have been my first indication I was headed for a mild amount of hair pulling.</p>
<h3>Step 1</h3>
<p>Untar the archive:  <strong>tar zxvf &lt;filename&gt;</strong></p>
<p>Now, cd into the directory that was just created.</p>
<h3>Step 2 (optional)</h3>
<p>I was installing this software in a test environment, and while I had internet connection, ping was disabled on my network.  This is the case for many production networks, so it baffles my mind why VMware would include a provision to test network connectivity with ping inside their install script.  If you environment does not have ICMP ping enabled, but your CentOS machine does indeed have internet access, you should modify the <strong>vmware-install.pl </strong>as follows:</p>
<pre class="brush: perl; collapse: false; title: ; wrap-lines: false; notranslate">
# Determine if internet connection is available.
  if ( ( ! $install_rhel55_local ) &amp;&amp; ( scalar(@install) &gt; 0 ) ) {
      my $internetConnect = `ping -c 10 -W 4 www.vmware.com | grep -c &quot;64 bytes&quot;`;
      if ( $internetConnect ne '') {
        $internet_available = 1;
      }
  }
</pre>
<h3>Step 3</h3>
<p>Here&#8217;s where I&#8217;m going to deviate from what I actually do in hopes to save you some time.  The install script attempts to download a bunch of packages from cpan, which is some sort of perl repository.  The install script will most likely download the needed sources from cpan to meet it&#8217;s own dependencies, but cpan has no ability to resolve it&#8217;s own dependencies.  Trying to make and install these sources in cpan itself also won&#8217;t tell you why they won&#8217;t compile and install.  Also, even though VMware released an x86_64 file for vSphere CLI, it actually requires quite a few .i686 packages to run the applications.</p>
<p>I submit to you my list of dependencies to both build the cpan source files, and actually run the compiled vSphere CLI binaries:</p>
<p><strong>yum install openssl-devel gcc libxml2-dev libxml2-devel perl-UUID e2fsprogs make uuid-dev uuid libuuid e2fsprogs-devel libuuid-devel glibc.i686 zlib.i686 zlib-devel.i686 ncurses-libs.i686 libstdc++.i686 libxml2.i686</strong></p>
<p>Please note, not all of these may be required, as I more or less haphazardly picked a couple of them.  In-fact, I&#8217;m not sure they&#8217;re all actually packages, just some stuff I typed in <img src='http://funwithlinux.net/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<h3>Step 4</h3>
<p>Run the install script.  This may or may not actually complete for you, as I described above, this is a little out of order from what I did.  Since all of the dependencies have been met at this point, it should proceed without issue.</p>
<h3>Step 5 (optional)</h3>
<p>Okay, let&#8217;s say that the install still didn&#8217;t complete because cpan still decided it couldn&#8217;t build those sources into libraries / binaries.  If that is the case, go into each directory in <strong>/root/.cpan/build/</strong> and run the following command</p>
<p><strong>perl Makefile.PL &amp;&amp; make &amp;&amp; make install</strong></p>
<p>Please note, if you have used cpan before, there may be other libraries that you don&#8217;t need to do this for.  Just like at the date stamp for each, and it should tell you which ones you need.  I only had to do this for like 6 or 7 libraries, so it shouldn&#8217;t take that long.  Also, if you run the install script multiple times, you&#8217;ll have multiple instances of each library source with a bunch of random characters at the end of the directory name.  You only need to run the command for each library once.</p>
<p>Also, make sure each library actually installs.  If you hit one that gives you an error, skip along to the next and come back to it.  I believe some of the libraries are dependent upon some others that you will have also downloaded.</p>
<h3>Step 6 (optional)</h3>
<p>If you had to perform step 5, then you just need to re-run VMware&#8217;s install script.</p>
<p>Okay, that should be it.  Let me know if there are any issues or questions, and I&#8217;ll try to help you out.  Thanks for reading.</p>
]]></content:encoded>
			<wfw:commentRss>http://funwithlinux.net/2013/05/install-vsphere-cli-in-centos-6/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Top 10 useful linux cli tools</title>
		<link>http://funwithlinux.net/2013/04/top10-useful-linux-cli-tools/</link>
		<comments>http://funwithlinux.net/2013/04/top10-useful-linux-cli-tools/#comments</comments>
		<pubDate>Thu, 11 Apr 2013 15:42:34 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://funwithlinux.net/?p=575</guid>
		<description><![CDATA[Here are some tools I use on a fairly frequent basis, which may or may not be installed by default in your distribution.  I highly recommend installing these tools during the provisioning stage of your system/environment because repositories aren&#8217;t always reachable on production systems.  Furthermore, installing software on a running critical production system is often ...]]></description>
				<content:encoded><![CDATA[<p>Here are some tools I use on a fairly frequent basis, which may or may not be installed by default in your distribution.  I highly recommend installing these tools during the provisioning stage of your system/environment because repositories aren&#8217;t always reachable on production systems.  Furthermore, installing software on a running critical production system is often a tightly controlled process.</p>
<p><span id="more-575"></span></p>
<h3>nmap</h3>
<p>This is a very robust yet lightweight network tool.  I typically use this tool for port and subnet scanning, but it packs a ton of features and is worth looking into if you&#8217;ve never heard of it.  I use it on a weekly basis.  Please note, this tool may not be appropriate for environments that have very tight security policies as it has the potential to be used maliciously.</p>
<h3>telnet</h3>
<p>This tool is fairly common, but is not installed by default on many distros, especially if you work from minimal installs like I do.  It&#8217;s great for testing TCP connectivity to certain ports on remote hosts.  My favorite usage is <strong>telnet google.com 80</strong></p>
<p>This will tell you if you&#8217;re able to connect to webpages on the internet.  Typing<strong> GET</strong> at the blank prompt will confirm a valid connection by downloading the html response from the page.  Very useful for checking port connectivity inside your firewall as well.</p>
<h3>ethtool</h3>
<p>Use this to mine information from your network interfaces.  Most common usage is <strong>ethtool eth0 </strong>which will show you the physical link-state of the adapter and various other settings.  This tool can also be used to perform operations such as flashing a device&#8217;s firmware or having an interface blink so a technician in the data center can positively ID it.</p>
<h3>logwatch</h3>
<p>This is a great utility that will parse your log files for you, aggregate information, and display the information in an easy to read format.</p>
<h3>mlocate</h3>
<p>mlocate is the name of the package which installs<strong> locate</strong> and<strong> updatedb</strong>.  The updatedb command indexes the filenames and paths of all files on your system.  The locate command lets you quickly search that index for a file string.  Much faster than using find, and much less complicated syntax for beginners.</p>
<h3>mutt</h3>
<p>This is a simple command line mail client.  It can be used to compose email much like a desktop mail client, however I mainly use it to email attachments to remote systems as the mail command does not allow for such an operation.</p>
<p>Common usage: <strong>echo &#8220;this is the body of the email&#8221; | mutt -s &#8220;Subject goes here&#8221; -a myfile.txt &#8212; user@example.com</strong></p>
<p>This should work as long as you have a MTA running, such as postfix or sendmail.</p>
<h3>vim</h3>
<p>vim is an enhanced version of the vi editor.  It allows for color highlighting as well as using your arrow keys in insert mode.  I recommend using vi / vim because it&#8217;s available on nearly any *nix system by default.  That Solaris box you have to log into once a year might not have NANO when you need to edit some configuration file.  Do yourself a favor and learn the use vi / vim.</p>
<h3>system-config-network (RHEL/CentOS/Fedora)</h3>
<p>This is a handy tool for configuring networking on the above mentioned distros.  It&#8217;s a little more beginner friendly than editing the network config files manually.</p>
<h3>system-config-firewall-tui (RHEL/CentOS/Fedora)</h3>
<p>Same idea as the system-config-network command above, but in this case is used for configuring your system&#8217;s firewall.  Can make complicated firewall rules much easier to implement for beginners.</p>
<h3>policycoreutils-python (RHEL/CentOS/Fedora)</h3>
<p>I frankly don&#8217;t know why this software isn&#8217;t installed by default, even on standard spins of these distributions.  It&#8217;s essential for managing basic SELinux contexts and policies.  If you&#8217;re running a system with SELinux enabled in enforcing mode, you need this tool to make permanent changes to SELinux contexts.  The chcon command, which works will in a pinch, will not enable your filesystem&#8217;s contexts to survive a filesystem relabel.  This can occur after booting a system into rescue mode or disabling / reenabling SELinux.  The time to find out your SELinux contexts weren&#8217;t permanent isn&#8217;t after rescuing a broken production system.</p>
]]></content:encoded>
			<wfw:commentRss>http://funwithlinux.net/2013/04/top10-useful-linux-cli-tools/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Python Backup Script</title>
		<link>http://funwithlinux.net/2013/02/python-backup-script/</link>
		<comments>http://funwithlinux.net/2013/02/python-backup-script/#comments</comments>
		<pubDate>Thu, 21 Feb 2013 22:23:37 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[backups]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[script]]></category>
		<category><![CDATA[scripting]]></category>
		<category><![CDATA[storage]]></category>

		<guid isPermaLink="false">http://funwithlinux.net/?p=462</guid>
		<description><![CDATA[Part one, take the first full backup. At the present time, this code is still under development and should not be used on a production machine. However, I am posting it here for reference. Eventually, this code is going to be included in a backup client I am developing that will interface with glusterfs and ...]]></description>
				<content:encoded><![CDATA[<p>Part one, take the first full backup. At the present time, this code is still under development and should not be used on a production machine. However, I am posting it here for reference.</p>
<p>Eventually, this code is going to be included in a backup client I am developing that will interface with glusterfs and Amazon S3 storage.</p>
<p>Currently, this code is tested to run on Python v2.7.4 on a Fedora 18 machine.  With all three python files, and any number of properly defined job xml files in the jobs.d/ directory, these scripts are currently functional.</p>
<p><span id="more-462"></span></p>
<p><strong>seed_files.py</strong></p>
<p>This is the controller file for taking the first backup.</p>
<pre class="brush: python; collapse: false; title: ; wrap-lines: false; notranslate">
#!/usr/bin/python

# Create first full backup

import os, stat, time, seed_functions

def printHello():
  print &quot;hello&quot;;

#--- This part of the script does the heavy lifting.
def seedMain(myFindPath,myJobId,myFullTempPath,myTargetMetaPath,myTargetTarFilePath,myExcludeFiles):
  #---execute the first backup for the job.
  seed_functions.findFiles(myFullTempPath,myFindPath,myExcludeFiles);
  seed_functions.storeMetaData(myFullTempPath,myTargetMetaPath);
  seed_functions.mkTarFile(myFullTempPath,myTargetTarFilePath);
  print &quot;Job completed successfully&quot;;

#--- If this is being run as a script, set temporary variables
if __name__ == '__main__':
  myFindPath = '/home/myuser/findfiles';
  myJobId = str('106');
  myTempPath = '/tmp/';
  myTempFileList = 'files.tmp';
  myFullTempPath = os.path.join(myTempPath,myTempFileList); 
  myTargetPath = '/home/target1/';
  myTargetMeta = 'job'+myJobId+'.meta';
  myTargetTarFile = 'job'+myJobId+'.tar';
  myTargetMetaPath = myTargetPath+myTargetMeta;
  myTargetTarFilePath = myTargetPath+myTargetTarFile;

  #---Path names below should be absolute path names (start with / ) and should not end with '/'
  myExlcudeFiles = list();
  myExlcudeFiles.append('/home/myuser/findfiles/dontbackup');
  myExlcudeFiles.append('/home/myuser/findfiles/somebigfiles');
  myExlcudeFiles.append('*.adf');
  myExlcudeFiles.append('badfilez*');
  
  seedMain(myFindPath,myJobId,myFullTempPath,myTargetMetaPath,myTargetTarFilePath,myExcludeFiles);

#myJdate = seed_functions.getJulianDate();
</pre>
<p><strong>seed_functions.py</strong><br />
This is the file that does the actual real work <img src='http://funwithlinux.net/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<pre class="brush: python; collapse: false; title: ; wrap-lines: false; notranslate">
#!/usr/bin/python

#Shared Functions and Classes
import os, sys, stat, time, glob
from datetime import datetime

def getJulianDate():
#--- This function returns an interger value of today's Julian Date 
#--- preceded by two digit year.  IE: 01JAN2013 -&gt; 13001
  nowtime =  str(datetime.now());
  (year, month, day) = nowtime.split('-');
  day = int(day[:2]);
  month = int(month);
  year = int(year);
  t = time.mktime((year, month, day, 0, 0, 0, 0, 0, 0));
  jdate = (year % 2000) * 1000;
  jdate = jdate + time.gmtime(t)[7];
  return jdate;


import re
def findFiles(filepath,myFindPath,excludeFiles):
#--- Variables: fileListPath = string, myFindPath = string, excludeFiles = list() of strings)
#--- This function writes/overwrites the file @ 'fileListPath' which should be absolute path.
#--- The file @ fileListPath is a list of files found within myFindPath.
#--- myFindPath is desgined to be an absolute directory path.
#--- excludeFiles is a list() of absolute paths which should be excluded during the finding operation.
  with open(filepath, 'w') as ftemp:
    #--- Generate list of files that shouldn't be added
    #removeExcludedFiles();
    for dirname, dirnames, filenames in os.walk(myFindPath):
      #--- Remove exlcuded directories
      for badfile in dirnames:
        if os.path.join(myFindPath,dirname,badfile) in excludeFiles:
          dirnames.remove(badfile);

      #--- Gather the other directories
      for subdirname in dirnames:
	  #--- we just want to add empty directories
	  if os.path.islink(os.path.join(dirname, subdirname)) or not os.listdir(os.path.join(dirname, subdirname)):
	    ftemp.write(os.path.join(dirname, subdirname)+'\n');

      #--- Add files to the list.
      for filename in filenames:
	  #--- check to see if the file is a regular file or a link:
	  if os.path.islink(os.path.join(dirname, filename)) or os.path.isfile(os.path.join(dirname, filename)):
            #if not filename in myWildList:
	      ftemp.write(os.path.join(dirname, filename)+'\n');

  ftemp.closed;	

def storeMetaData(fileListPath, fileMetaPath):
#---This function creates an output file with
#---Filename and absolute path of fileMetaPath
#---File format is:
#---  /path/to/file ::: modified datetime ::: seconds since 1970 ::: md5 hash
  with open(fileMetaPath, 'w') as fmeta:
    myFileList = open(fileListPath, 'r');
    for filez in myFileList:
      #--- create an array to append information.
      myFileMeta = list();
      #--- Append absolute path and file name as first item
      myFileMeta.append(os.path.abspath(filez.strip()));
      #--- Get file meta information from os.stat()
      myStat = os.stat(filez.strip());
      #---Append human readable date/time stamp.
      myFileMeta.append(time.ctime(myStat.st_mtime));
      #---Append unix timestamp for easy comparison in the future.
      myFileMeta.append(myStat.st_mtime);
      if not os.path.isdir(filez.strip()):
        myHash = md5(filez.strip());
      else:
        myHash = &quot;---None: directory&quot;;
      myFileMeta.append(myHash);
      metaDataString = str(myFileMeta[0]+&quot;:::&quot;+myFileMeta[1]+&quot;:::&quot;+str(myFileMeta[2])+&quot;:::&quot;+str(myFileMeta[3]));
      fmeta.write(metaDataString+'\n');
    myFileList.closed;
  fmeta.closed;

import hashlib,os
def md5(filename):
    ''' function to get md5 of file '''
    d = hashlib.md5();
    try:
        d.update(open(filename).read());
    except Exception,e:
        print e;
    else:
        return d.hexdigest();

import tarfile
def mkTarFile(fileList, tarOutPath):
    thisTarOut = tarOutPath+&quot;.lzo&quot;
    thisFileList = &quot;-T &quot;+fileList
    os.system(&quot;tar {options} {tarfile} {filex} &amp;&gt; /dev/null&quot;.format(options=&quot;cpvfa&quot;, tarfile=thisTarOut, filex=thisFileList));

#--------
#--- Not yet impemented functions below:
#--------

def findFiles2(fileListPath,myFindPath,excludeFiles):
#--- This function is for testing purposes only
  with open(fileListPath, 'w') as ftemp:
   for dirname, dirnames, filenames in os.walk(myFindPath):
    for subdirname in dirnames:
	#--- we just want to add empty directories
	if not os.listdir(os.path.join(dirname, subdirname)):
		ftemp.write(os.path.join(dirname, subdirname)+'\n');
    for filename in filenames:
	#--- check to see if the file is a regular file or a link:
	if os.path.islink(os.path.join(dirname, filename)) or os.path.isfile(os.path.join(dirname, filename)):
		ftemp.write(os.path.join(dirname, filename)+'\n');
  ftemp.closed;

def removeExcludedFiles():
    #--- Not implemented yet.
    myWildList = list();
    wildMatch = re.compile(&quot;^\*&quot;);
    wildMatch2 = re.compile(&quot;.*\*&quot;);
    print &quot;Excluding the following&quot;
    for badfile in excludeFiles:
      print badfile;
      result = wildMatch.match(badfile);
      if not result:
        result2 = wildMatch2.match(badfile);
        print result2;
      if result or result2:
        myWildList.append(badfile);
    print myWildList;

</pre>
<p><strong>start_seeds.py</strong><br />
This is near completion; it parses the jobs in jobs.d/, verifies them, and runs them.</p>
<pre class="brush: python; collapse: false; title: ; wrap-lines: false; notranslate">
#!/usr/bin/python
import os, re
import xml.etree.ElementTree as ET
import seed_files



def readConfFile():
  #--- Future feature to read a specified jobs.d from config file.
  jobdir = 'jobs.d';
  return jobdir;

def findJobs(jobdir):
  myJobList = list();
  confMatch = re.compile(&quot;.*\.xml$&quot;);
  for dirname, dirnames, filenames in os.walk(jobdir):
    for jobid in filenames:
      result = confMatch.match(jobid);
      if result:
        myJobList.append(os.path.join(jobdir,jobid));
  return myJobList;

def checkPath(pathText):
  confMatch = re.compile(&quot;^\/&quot;);
  result = confMatch.match(pathText);
  confMatch2 = re.compile(&quot;^\/$&quot;);
  result2 = confMatch2.match(pathText);
  if result2:
    exit('Path cannot be / ');
  if not result:
    exit('Directory path must be absolute path: '+pathText);
  if os.path.isdir(pathText) or os.path.ismount(pathText):
    print 'Path seems valid: ',pathText;
  else:
    exit('Invalid path: '+pathText);

def parseJobs(myFoundJobs):
  print &quot;Found the following config files: &quot;,myFoundJobs;
  print &quot;------------------------------------------------&quot;;
  myJobList = list();
  for myJob in myFoundJobs:
    mySubList = list();
    print &quot;Parsing and testing: &quot;,myJob;
    tree = ET.parse(myJob);
    root = tree.getroot();

    for child in root:
      #--- Validate backup path is valide.
      #----future feature: master excludes in config file
      if child.tag == 'backupdir':
        print &quot;Checking Backup Directory&quot;;
        checkPath(child.text);
      if child.tag == 'backuptarget':
        print &quot;Checking Backup Target&quot;
        checkPath(child.text);

      #--- Create another sublist for excluded directories.
      if child.tag == 'exclude':
        myExcludeList = list();
        for subchild in child:
	  myExcludeList.append(subchild.text);
        mySubList.append(myExcludeList);

      #--- Since it's not a sublist, we append directly
      elif not child.tag == 'exclude':
        mySubList.append(child.text);
    print &quot;------------------------------------------------&quot;;
    myJobList.append(mySubList);

  #--- Ensure some jobs were actually found.
  if myJobList.__len__() == 0:
    exit('Exit on Error: No Jobs Found!');
  #--- If we didn't exit above, we returned the parsed job list
  return myJobList;

def performBackup(myJobList):
  for job in myJobList:
    #--- job[0]:  JobID
    #--- job[1];  Backup Path
    #--- job[2]:  Backup Target
    #--- job[3]:  temp directory
    #--- job[4]:  Excluded directories
    myJobId = job[0];
    myFindPath = job[1];
    myTarget = job[2];
    myTempPath = job[3];
    myExcludes = job[4];
    myTargetPath = os.path.join(myTarget,str('job'+myJobId),'master');
    myTempFileList = 'backup_job'+myJobId+'.tmp';
    myFullTempPath = os.path.join(myTargetPath,myTempFileList); 
    myTargetMeta = 'job'+myJobId+'_master.meta';
    myTargetTarFile = 'job'+myJobId+'_master_seed.tar';
    myTargetMetaPath = os.path.join(myTargetPath,myTargetMeta);
    myTargetTarFilePath = os.path.join(myTargetPath,myTargetTarFile);

    if os.path.exists(os.path.join(myTarget,str('job'+myJobId))):
      print os.path.join(myFindPath,str('job'+myJobId));
      exit('Critical Error on JobID: '+myJobId+'\n This job directory already exists!  Exiting to preserve data!');
    else:
      os.makedirs(os.path.join(myTarget,str('job'+myJobId)));
    if os.path.exists(myTargetPath):
      exit('Critical Error on JobID: '+myJobId+'\n This job directory already exists!  Exiting to preserve data!');
    else:
      os.makedirs(myTargetPath);

    myExcludeFiles = list();
    for excludes in myExcludes:
      myExcludeFiles.append(excludes);
    myExcludeFiles.append(myTarget);
    print &quot;------------------------------------------------&quot;;
    print &quot;Starting Job: &quot;,myJobId;
    seed_files.seedMain(myFindPath,myJobId,myFullTempPath,myTargetMetaPath,myTargetTarFilePath,myExcludeFiles);



#--- Execute the script.
myJobList = list();
jobdir = readConfFile();
myFoundJobs = findJobs(jobdir);
myJobList = parseJobs(myFoundJobs);
print &quot;Number of jobs: &quot;,myJobList.__len__();
#promptContinue() #--- Let user review backup jobs, prompt for continue.
performBackup(myJobList);

</pre>
<p><strong>job103.xml</strong><br />
Job file in jobs.d/ directory</p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;data&gt;
  &lt;jobid&gt;103&lt;/jobid&gt;
  &lt;backupdir&gt;/home/myuser/files&lt;/backupdir&gt;
  &lt;backuptarget&gt;/offsitenfs/client1/target2&lt;/backuptarget&gt;
  &lt;temppath&gt;/tmp&lt;/temppath&gt;
  &lt;exclude&gt;
    &lt;directory&gt;/home/myuser/files/badfolder1&lt;/directory&gt;
    &lt;directory&gt;/home/myuser/files/music/badfolder2&lt;/directory&gt;
  &lt;/exclude&gt;
&lt;/data&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://funwithlinux.net/2013/02/python-backup-script/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Start iptables in Debian Automatically</title>
		<link>http://funwithlinux.net/2013/02/start-iptables-in-debian-automatically/</link>
		<comments>http://funwithlinux.net/2013/02/start-iptables-in-debian-automatically/#comments</comments>
		<pubDate>Thu, 21 Feb 2013 21:47:54 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
				<category><![CDATA[Debian]]></category>
		<category><![CDATA[How-To]]></category>
		<category><![CDATA[guide]]></category>
		<category><![CDATA[iptables]]></category>
		<category><![CDATA[script]]></category>
		<category><![CDATA[scripting]]></category>

		<guid isPermaLink="false">http://funwithlinux.net/?p=455</guid>
		<description><![CDATA[If you are trying to make iptables survive a reboot in Debian Squeeze or Wheezy, you may find the following of use.  After you get your iptables all squared away, save them to a text file with the incredibly handy /sbin/iptables-save command: /sbin/iptables-save &#62; /root/iptables.saved This will create a text file in the /root directory containing lines ...]]></description>
				<content:encoded><![CDATA[<p>If you are trying to make iptables survive a reboot in Debian Squeeze or Wheezy, you may find the following of use.  After you get your iptables all squared away, save them to a text file with the incredibly handy <strong>/sbin/iptables-save </strong>command:</p>
<p><strong>/sbin/iptables-save &gt; /root/iptables.saved</strong></p>
<p>This will create a text file in the /root directory containing lines that will be parsed by iptables when used with the <strong> iptables-restore</strong> command.</p>
<p>Next, add the following script to your system:</p>
<p><span id="more-455"></span></p>
<pre class="brush: bash; title: ; notranslate">
#!/bin/sh
### BEGIN INIT INFO
# Provides: iptables
# Required-Start: mountkernfs $local_fs
# Required-Stop: $local_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Set up iptables rules
### END INIT INFO
/sbin/iptables-restore &amp;lt; /root/iptables.saved
case &quot;$1&quot; in
 *)
 echo &quot;iptables loaded from file&quot;
 ;;
esac
exit 0
</pre>
<p>Save that script to <strong>/etc/init.d/iptables</strong></p>
<p>Next, run <strong>update-rc.d iptables defaults</strong></p>
<p>You should now see SXXiptables in /etc/rc2.d/ and other run level directories, where XX is a number.</p>
]]></content:encoded>
			<wfw:commentRss>http://funwithlinux.net/2013/02/start-iptables-in-debian-automatically/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The Next Generation of Open Source Smart Grid</title>
		<link>http://feedproxy.google.com/~r/linuxtoday/linux/~3/wO19WVqc4y8/the-next-generation-of-open-source-smart-grid.html</link>
		<comments>http://feedproxy.google.com/~r/linuxtoday/linux/~3/wO19WVqc4y8/the-next-generation-of-open-source-smart-grid.html#comments</comments>
		<pubDate>Mon, 11 Feb 2013 10:00:00 +0000</pubDate>
		<dc:creator>Linuxtoday.com</dc:creator>
				<category><![CDATA[External News]]></category>
		<category><![CDATA[Linux News]]></category>

		<guid isPermaLink="false">http://funwithlinux.net/?guid=ecd21e3881bebe8867f1da687e096b38</guid>
		<description><![CDATA[GreenGridTech: Of course, the open-source software developed by Department of Energy labs, federal power authorities like TVA, and other public entities tends to start out as a way to model experiments, store data or other such research functions.]]></description>
				<content:encoded><![CDATA[<p><strong>GreenGridTech:</strong> Of course, the open-source software developed by Department of Energy labs, federal power authorities like TVA, and other public entities tends to start out as a way to model experiments, store data or other such research functions.</p>]]></content:encoded>
			<wfw:commentRss>http://funwithlinux.net/2013/02/the-next-generation-of-open-source-smart-grid/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Open source pioneers next generation chat and forums</title>
		<link>http://feedproxy.google.com/~r/linuxtoday/linux/~3/oAApT3y-YHQ/open-source-pioneers-next-generation-chat-and-forums.html</link>
		<comments>http://feedproxy.google.com/~r/linuxtoday/linux/~3/oAApT3y-YHQ/open-source-pioneers-next-generation-chat-and-forums.html#comments</comments>
		<pubDate>Mon, 11 Feb 2013 06:00:00 +0000</pubDate>
		<dc:creator>Linuxtoday.com</dc:creator>
				<category><![CDATA[External News]]></category>
		<category><![CDATA[Linux News]]></category>

		<guid isPermaLink="false">http://funwithlinux.net/?guid=3ce617005313fda8c00212a781d2e519</guid>
		<description><![CDATA[The H Open: Not satisfied with the experience on current forum software packages, Stack Exchange co-founder Jeff Atwood founded Civilized Discourse Construction Kit Inc to come up with a software package to replace them.]]></description>
				<content:encoded><![CDATA[<p><strong>The H Open:</strong> Not satisfied with the experience on current forum software packages, Stack Exchange co-founder Jeff Atwood founded Civilized Discourse Construction Kit Inc to come up with a software package to replace them.</p>]]></content:encoded>
			<wfw:commentRss>http://funwithlinux.net/2013/02/open-source-pioneers-next-generation-chat-and-forums/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Fedora 18 won&#8217;t boot</title>
		<link>http://funwithlinux.net/2013/02/fedora-18-wont-boot/</link>
		<comments>http://funwithlinux.net/2013/02/fedora-18-wont-boot/#comments</comments>
		<pubDate>Mon, 11 Feb 2013 03:27:58 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
				<category><![CDATA[Fedora]]></category>
		<category><![CDATA[How-To]]></category>
		<category><![CDATA[rescue]]></category>

		<guid isPermaLink="false">http://funwithlinux.net/?p=420</guid>
		<description><![CDATA[Recently I filed a bug report with bugzilla regarding Fedora 18&#8242;s inability to boot after successful installation from all types of installation methods. Check the comment section of the Fedora 18 Review post for a direct link to the bug report. Fedora 18, like many other fresh distros, utilizes GRUB2.  However, Fedora 18 is the ...]]></description>
				<content:encoded><![CDATA[<p>Recently I filed a bug report with <strong>bugzilla</strong> regarding Fedora 18&#8242;s inability to boot after successful installation from all types of installation methods. Check the comment section of the <span style="color: #000080;"><a title="Fedora 18 Review" href="http://funwithlinux.net/2013/02/fedora-18-review/"><span style="color: #000080;">Fedora 18 Review</span></a></span> post for a direct link to the bug report.</p>
<p>Fedora 18, like many other fresh distros, utilizes GRUB2.  However, Fedora 18 is the only distro I have personally encountered this problem of not booting after a successful install.  This problem seems to be related to older hardware, or devices that lack the ability / video memory to use the highly graphical GRUB2 boot screen.  There is no science in my previous statement, just an educated guess.</p>
<p><span id="more-420"></span></p>
<p>Most particularly, I have encountered this problem on HP Proliant G4 device, as well as in Virtual Box on a Windows host.</p>
<p>Symptoms:  After successful installation, GRUB2 will briefly flash after post, and then the video output will go black or stop.  The system will never continue booting.</p>
<p>Resolution:  Boot the system with<strong> installation media</strong> and instead of selecting &#8220;Install,&#8221; select &#8220;Troubleshooting&#8221;.  On the following screen, select &#8220;Rescue System.&#8221;</p>
<p>Continue with the default options, which includes automatically detecting and mounting the system&#8217;s root directory to <strong>/mnt/sysimage/</strong></p>
<p>After you a dropped to a shell, run <strong>chroot /mnt/sysimage </strong>to change the root of the system to the freshly installed system.  Next, we need to update our GRUB2 config file to disable the graphical mode.  That is achieve by peforming the following:</p>
<p><strong>echo &#8220;GRUB_TERMINAL=console&#8221; &gt;&gt; /etc/default/grub<br />
grub2-mkconfig -o /boot/grub2/grub.cfg<br />
</strong></p>
<p>These two commands will reinstall GRUB2 with the new argument GRUB_TERMINAL=console; unlike legacy GRUB, simply editing a text file is not enough, you must run the grub2-mkconfig command as described above.</p>
<p>Now, exit out of the chroot environment with the simple &#8220;exit&#8221; command, and reboot the system.  If everything went correctly, you should now see the GRUB2 menu in solid black and white (how Linux on a server <em>should</em> be).  Unfortunately, it&#8217;s now time to make yourself a cup of tea, because we have to wait for SELinux to relabel the filesystem.  While it doesn&#8217;t take forever, it does take about 3 or 4 minutes.</p>
]]></content:encoded>
			<wfw:commentRss>http://funwithlinux.net/2013/02/fedora-18-wont-boot/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Set up a local Linux installation and update server with Kickstart</title>
		<link>http://feedproxy.google.com/~r/linuxtoday/linux/~3/4KyqMBGtFVI/set-up-a-local-linux-installation-and-update-server-with-kickstart-130207101004.html</link>
		<comments>http://feedproxy.google.com/~r/linuxtoday/linux/~3/4KyqMBGtFVI/set-up-a-local-linux-installation-and-update-server-with-kickstart-130207101004.html#comments</comments>
		<pubDate>Mon, 11 Feb 2013 02:00:00 +0000</pubDate>
		<dc:creator>Linuxtoday.com</dc:creator>
				<category><![CDATA[External News]]></category>
		<category><![CDATA[Linux News]]></category>

		<guid isPermaLink="false">http://funwithlinux.net/?guid=6495fadd00ac2dc377f4daf02eb929b9</guid>
		<description><![CDATA[Wazi: Setting up Linux on multiple machines can take a lot of time and effort, but it doesn't have to; with Kickstart, you can customize automatic installation of any Fedora-based distribution.]]></description>
				<content:encoded><![CDATA[<p><strong>Wazi:</strong> Setting up Linux on multiple machines can take a lot of time and effort, but it doesn't have to; with Kickstart, you can customize automatic installation of any Fedora-based distribution.</p>]]></content:encoded>
			<wfw:commentRss>http://funwithlinux.net/2013/02/set-up-a-local-linux-installation-and-update-server-with-kickstart/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>LibreOffice 4: A new, better open-source office suite</title>
		<link>http://feedproxy.google.com/~r/linuxtoday/linux/~3/zu-2uywn1Ek/libreoffice-4-a-new-better-open-source-office-suite-130207152009.html</link>
		<comments>http://feedproxy.google.com/~r/linuxtoday/linux/~3/zu-2uywn1Ek/libreoffice-4-a-new-better-open-source-office-suite-130207152009.html#comments</comments>
		<pubDate>Sun, 10 Feb 2013 22:00:00 +0000</pubDate>
		<dc:creator>Linuxtoday.com</dc:creator>
				<category><![CDATA[External News]]></category>
		<category><![CDATA[Linux News]]></category>

		<guid isPermaLink="false">http://funwithlinux.net/?guid=87fa4a495ed72c7885228bf150f0d296</guid>
		<description><![CDATA[&#160;ZDnet: LibreOffice 4 has just arrived and, at first glance, this popular open-source office suite looks really good.]]></description>
				<content:encoded><![CDATA[<p><strong>&nbsp;ZDnet:</strong> LibreOffice 4 has just arrived and, at first glance, this popular open-source office suite looks really good.</p>]]></content:encoded>
			<wfw:commentRss>http://funwithlinux.net/2013/02/libreoffice-4-a-new-better-open-source-office-suite/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
