<?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"
	>

<channel>
	<title>In the box</title>
	<atom:link href="http://inthebox.webmin.com/feed" rel="self" type="application/rss+xml" />
	<link>http://inthebox.webmin.com</link>
	<description>Automating the data center</description>
	<pubDate>Fri, 25 Jul 2008 09:08:35 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.5.1</generator>
	<language>en</language>
			<item>
		<title>Old School to New School: Refactoring Perl</title>
		<link>http://inthebox.webmin.com/old-school-to-new-school-refactoring-perl</link>
		<comments>http://inthebox.webmin.com/old-school-to-new-school-refactoring-perl#comments</comments>
		<pubDate>Fri, 25 Jul 2008 08:57:02 +0000</pubDate>
		<dc:creator>Joe Cooper</dc:creator>
		
		<category><![CDATA[Design]]></category>

		<category><![CDATA[Development]]></category>

		<category><![CDATA[Features]]></category>

		<category><![CDATA[News]]></category>

		<guid isPermaLink="false">http://inthebox.webmin.com/?p=27</guid>
		<description><![CDATA[At YAPC::NA I sat in on lots of great talks (I also won Randal Schwartz in the charity auction, and so got to be beaten soundly at pool by him, and learn a few things about Smalltalk and Seaside).  In particular, Michael Schwern gave a fantastic talk entitled Skimmable Code:  Fast to Read, [...]]]></description>
			<content:encoded><![CDATA[<p>At YAPC::NA I sat in on lots of great talks (I also won Randal Schwartz in the charity auction, and so got to be beaten soundly at pool by him, and learn a few things about Smalltalk and Seaside).  In particular, Michael Schwern gave a fantastic talk entitled <a href="http://conferences.mongueurs.net/yn2008/talk/1131">Skimmable Code:  Fast to Read, Fast to Change‎</a>.  This got me thinking about our own code.  Webmin is an old codebase, approaching 11 years old, and thus has some pretty old school Perl practices throughout.  Coding standards sort of stick to projects over a few years, and as new code comes in, it tends to look like the old code.  And, to add to that momentum, Jamie has religiously kept compatibility for module authors throughout the entire life of the project.  Modules written ten years ago can, astonishingly, be expected to work identically in todays Webmin, though they might not participate in logging or advanced ACLs or other nifty features that have come to exist in the framework in that time.</p>
<p>So, when I found myself needing to make a modification to <em>oschooser.pl</em>, a small program for detecting the operating system on which Webmin is running (sounds trivial, but when you realize that Webmin runs on <em>hundreds</em> of operating systems and versions, it turns out to be a rather complex problem), I decided to take the opportunity to put into practice some of the niceties of modern Perl.  This article is a little different than what I usually write for <em>In the Box</em>, in that it covers a lot of ground fast, and most of it is probably pretty mundane stuff for folks already writing modern Perl.  But, I think there&#8217;s enough old Perl code running around out there, running the Internet and such, that it&#8217;s worth talking about modernization work.</p>
<p>So, let&#8217;s go spelunking!</p>
<p><strong>Introduction to oschooser.pl</strong></p>
<p>The code we&#8217;ll be picking apart, and putting back together, is probably one of the more heavily used pieces of Perl code, and certainly one of the oldest, in the wild.  It&#8217;s the OS detection code that Webmin and Usermin use to figure out what system they&#8217;re running on during installation.  With Webmin having 12 million (give or take several million) downloads over its ten year history, this equals a <em>lot</em> of operating systems successfully detected.  Perhaps I should have picked something a little less important for my first stab at modernization, but I&#8217;ve rarely been accused of being smart about making sweeping changes!  (Jamie will reel me in, before I break actual Webmin code.  I manage to break Virtualmin every now and then&#8230;but he&#8217;s more suspicious when I check code into Webmin, since it happens quite rarely.)</p>
<p>The <em>oschooser.pl</em> program actually loads up a rather complex definitions file called <em>os_list.txt</em> (by default, though it&#8217;s configurable, and we use different lists for Virtualmin and Webmin, since they have different requirements for version identification).  The definitions file can contain snippets of Perl code, which will be executed via <em>eval</em>, when appropriate.  Most of the updates to OS detection over the years have happened in <em>os_list.txt</em>, so <em>oschooser.pl</em> hasn&#8217;t seen a lot of grooming over the years, which makes it a prime candidate for modernization.  Assuming, of course, that it works identically when I&#8217;m done with it.</p>
<p><strong>Where to start?</strong></p>
<p>My end goal with this project is to make <em>oschooser.pl</em> usable as a library from Perl programs, since our new product installer is written in Perl rather than POSIX shell.  I also figured it&#8217;d be nice to make it testable, since I&#8217;ve made several mistakes in the detection code (in <em>os_list.txt</em>, specifically) over the past few years that led to our product being uninstallable on some systems until the bug was tracked down.  But, first things first.  Almost nothing in Webmin is strict compatible, and even warnings can cause some complaints, so that seems like a good starting point.</p>
<p>The code we&#8217;re starting with can be found <a href="http://inthebox.webmin.com/src/oschooser.pl">here</a>, so you can follow along at home.</p>
<p>Enabling warnings reveals the following (don&#8217;t worry about the arguments for now):</p>

<div class="wp_syntax"><div class="code"><pre class="bash">$ <span style="color: #c20cb9; font-weight: bold;">perl</span> -<span style="color: #c20cb9; font-weight: bold;">w</span> oschooser.pl os_list.txt outfile <span style="color: #000000;">1</span>
Name <span style="color: #ff0000;">&quot;main::uname&quot;</span> used only once: possible typo at oschooser.pl line <span style="color: #000000;">31</span>.
Name <span style="color: #ff0000;">&quot;main::donename&quot;</span> used only once: possible typo at oschooser.pl line <span style="color: #000000;">17</span>.</pre></div></div>

<p>Not too bad, actually.  Just a couple of variables that are only seen once, easy enough to fix by giving them a<em> my</em> declaration.  Though, in this case, it looks like enabling warnings turns up some unused code.  While <em>donename</em> is actually keeping track of what names we&#8217;ve seen, so far, and it&#8217;s one of several idiomatic ways to build an array of unique values, the <em>uname </em>variable seems to have no purpose.  So I&#8217;m going to kill that whole line rather than declare it.</p>
<p>Next up in our &#8220;low-hanging fruit&#8221; exercise is enabling <em>use strict.</em> Turns out this is quite a lot more intimidating:</p>

<div class="wp_syntax"><div class="code"><pre class="bash">$ <span style="color: #c20cb9; font-weight: bold;">perl</span> -c oschooser.pl
Global symbol <span style="color: #ff0000;">&quot;$oslist&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">15</span>.
Global symbol <span style="color: #ff0000;">&quot;$out&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">15</span>.
Global symbol <span style="color: #ff0000;">&quot;$auto&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">15</span>.
Global symbol <span style="color: #ff0000;">&quot;$oslist&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">16</span>.
Global symbol <span style="color: #ff0000;">&quot;$oslist&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">16</span>.
Global symbol <span style="color: #ff0000;">&quot;@list&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">20</span>.
Global symbol <span style="color: #ff0000;">&quot;@names&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">21</span>.
Global symbol <span style="color: #ff0000;">&quot;%names_to_real&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">22</span>.
Global symbol <span style="color: #ff0000;">&quot;$auto&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">27</span>.
Global symbol <span style="color: #ff0000;">&quot;$etc_issue&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">30</span>.
Global symbol <span style="color: #ff0000;">&quot;$etc_issue&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">33</span>.
Global symbol <span style="color: #ff0000;">&quot;$o&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">36</span>.
Global symbol <span style="color: #ff0000;">&quot;@list&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">36</span>.
Global symbol <span style="color: #ff0000;">&quot;$o&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">37</span>.
Global symbol <span style="color: #ff0000;">&quot;$o&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">37</span>.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">39</span>.
Global symbol <span style="color: #ff0000;">&quot;$o&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">39</span>.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">40</span>.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">41</span>.
Global symbol <span style="color: #ff0000;">&quot;$o&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">41</span>.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">41</span>.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">43</span>.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">44</span>.
Global symbol <span style="color: #ff0000;">&quot;$o&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">44</span>.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">44</span>.
Global symbol <span style="color: #ff0000;">&quot;$o&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">49</span>.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">53</span>.
Global symbol <span style="color: #ff0000;">&quot;$auto&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">54</span>.
Global symbol <span style="color: #ff0000;">&quot;$auto&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">59</span>.
Global symbol <span style="color: #ff0000;">&quot;$rv&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">61</span>.
Global symbol <span style="color: #ff0000;">&quot;$auto&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">67</span>.
Global symbol <span style="color: #ff0000;">&quot;$auto&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">72</span>.
Global symbol <span style="color: #ff0000;">&quot;$auto&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">77</span>.
Global symbol <span style="color: #ff0000;">&quot;$cmd&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">80</span>.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">81</span>.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">81</span>.
Global symbol <span style="color: #ff0000;">&quot;@names&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">81</span>.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">81</span>.
Global symbol <span style="color: #ff0000;">&quot;$cmd&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">82</span>.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">82</span>.
Global symbol <span style="color: #ff0000;">&quot;@names&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">82</span>.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">82</span>.
Global symbol <span style="color: #ff0000;">&quot;$tmp_base&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">84</span>.
Global symbol <span style="color: #ff0000;">&quot;$temp&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">85</span>.
Global symbol <span style="color: #ff0000;">&quot;$tmp_base&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">85</span>.
Global symbol <span style="color: #ff0000;">&quot;$cmd&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">86</span>.
Global symbol <span style="color: #ff0000;">&quot;$temp&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">86</span>.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">87</span>.
Global symbol <span style="color: #ff0000;">&quot;$temp&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">87</span>.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">88</span>.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">88</span>.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">89</span>.
Global symbol <span style="color: #ff0000;">&quot;$name&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">96</span>.
Global symbol <span style="color: #ff0000;">&quot;@names&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">96</span>.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">96</span>.
Global symbol <span style="color: #ff0000;">&quot;@vers&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">97</span>.
Global symbol <span style="color: #ff0000;">&quot;$name&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">97</span>.
Global symbol <span style="color: #ff0000;">&quot;@list&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">97</span>.
Global symbol <span style="color: #ff0000;">&quot;$cmd&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">98</span>.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">99</span>.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">99</span>.
Global symbol <span style="color: #ff0000;">&quot;@vers&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">99</span>.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">99</span>.
Global symbol <span style="color: #ff0000;">&quot;$cmd&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">100</span>.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">100</span>.
Global symbol <span style="color: #ff0000;">&quot;$name&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">100</span>.
Global symbol <span style="color: #ff0000;">&quot;@vers&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">100</span>.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">100</span>.
Global symbol <span style="color: #ff0000;">&quot;$cmd&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">102</span>.
Global symbol <span style="color: #ff0000;">&quot;$temp&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">102</span>.
Global symbol <span style="color: #ff0000;">&quot;$vnum&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">103</span>.
Global symbol <span style="color: #ff0000;">&quot;$temp&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">103</span>.
Global symbol <span style="color: #ff0000;">&quot;$vnum&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">104</span>.
Global symbol <span style="color: #ff0000;">&quot;$vnum&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">104</span>.
Global symbol <span style="color: #ff0000;">&quot;$temp&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">105</span>.
Global symbol <span style="color: #ff0000;">&quot;$vnum&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">106</span>.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">110</span>.
Global symbol <span style="color: #ff0000;">&quot;@vers&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">110</span>.
Global symbol <span style="color: #ff0000;">&quot;$vnum&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">110</span>.
Global symbol <span style="color: #ff0000;">&quot;$dashes&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">114</span>.
Global symbol <span style="color: #ff0000;">&quot;$dashes&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">115</span>.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">121</span>.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">121</span>.
Global symbol <span style="color: #ff0000;">&quot;@names&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">121</span>.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">121</span>.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">122</span>.
Global symbol <span style="color: #ff0000;">&quot;@names&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">122</span>.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">122</span>.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">123</span>.
Global symbol <span style="color: #ff0000;">&quot;$i&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">125</span>.
Global symbol <span style="color: #ff0000;">&quot;$dashes&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">126</span>.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">128</span>.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">129</span>.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">134</span>.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">134</span>.
Global symbol <span style="color: #ff0000;">&quot;@names&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">134</span>.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">135</span>.
Global symbol <span style="color: #ff0000;">&quot;$name&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">141</span>.
Global symbol <span style="color: #ff0000;">&quot;@names&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">141</span>.
Global symbol <span style="color: #ff0000;">&quot;$osnum&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">141</span>.
Global symbol <span style="color: #ff0000;">&quot;$name&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">142</span>.
Global symbol <span style="color: #ff0000;">&quot;$vnum&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">146</span>.
Global symbol <span style="color: #ff0000;">&quot;$vnum&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">147</span>.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">153</span>.
Global symbol <span style="color: #ff0000;">&quot;$name&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">153</span>.
Global symbol <span style="color: #ff0000;">&quot;$vnum&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">153</span>.
Global symbol <span style="color: #ff0000;">&quot;%names_to_real&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">154</span>.
Global symbol <span style="color: #ff0000;">&quot;$name&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">154</span>.
Global symbol <span style="color: #ff0000;">&quot;$vnum&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">154</span>.
Global symbol <span style="color: #ff0000;">&quot;$out&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">159</span>.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">160</span>.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">161</span>.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">162</span>.
Global symbol <span style="color: #ff0000;">&quot;$ver&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">163</span>.
Global symbol <span style="color: #ff0000;">&quot;$d&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">170</span>.
Global symbol <span style="color: #ff0000;">&quot;$rv&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">172</span>.
Global symbol <span style="color: #ff0000;">&quot;$rv&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">174</span>.
Global symbol <span style="color: #ff0000;">&quot;$d&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">177</span>.
Global symbol <span style="color: #ff0000;">&quot;$d&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">178</span>.
Global symbol <span style="color: #ff0000;">&quot;$rv&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">178</span>.
Global symbol <span style="color: #ff0000;">&quot;$d&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">178</span>.
Global symbol <span style="color: #ff0000;">&quot;$rv&quot;</span> requires explicit package name at oschooser.pl line <span style="color: #000000;">181</span>.
oschooser.pl had compilation errors.</pre></div></div>

<p>Wow!  I think that might be more lines than the program itself.  Luckily, it&#8217;s almost entirely unscoped variables.  A quick pass over the code, adding <em>my</em> declarations to the obvious candidates, gets things looking a little better.  One tricky bit is the <em>$i</em> loop variables used in <em>for</em> loops.  We don&#8217;t want those to be declared several times in the code, and we don&#8217;t want them to leak out into the scope of the rest of the program.  In modern Perl, this is no problem, as you can use the following:</p>

<div class="wp_syntax"><div class="code"><pre class="perl">    <span style="color: #b1b100;">for</span><span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$i</span>=<span style="color: #cc66cc;">0</span>; <span style="color: #0000ff;">$i</span>&lt;<span style="color: #0000ff;">@names</span>; <span style="color: #0000ff;">$i</span>++<span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
      <span style="color: #0000ff;">$cmd</span> .= <span style="color: #ff0000;">&quot; &quot;</span>.<span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$i</span><span style="color: #cc66cc;">+1</span><span style="color: #66cc66;">&#41;</span>.<span style="color: #ff0000;">&quot; '$names[$i]'&quot;</span>;
      <span style="color: #66cc66;">&#125;</span></pre></div></div>

<p>And <em>$i</em> will be local to the <em>for</em> loop.  I momentarily feared that I&#8217;d need to use an outer block to accomplish this, as Webmin needs to be compatible with quite old Perl versions (5.005, for core Webmin, unless Unicode support is needed, in which case 5.8.1 is required), but after downloading and installing Perl 5.005_4, I found that was an unnecessary precaution.  The <em>foreach</em> loops can also make use of this convenient feature.  If you do happen to be stuck with an even more ancient version that 5.005 (but still higher than 4)&#8211;though I can&#8217;t imagine how you could, as 5.005 is over nine years old&#8211;you can use the following:</p>

<div class="wp_syntax"><div class="code"><pre class="perl">  <span style="color: #66cc66;">&#123;</span>
  <span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$i</span>;
    <span style="color: #b1b100;">for</span><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$i</span>=<span style="color: #cc66cc;">0</span>; <span style="color: #0000ff;">$i</span>&lt;<span style="color: #0000ff;">@names</span>; <span style="color: #0000ff;">$i</span>++<span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
      <span style="color: #0000ff;">$cmd</span> .= <span style="color: #ff0000;">&quot; &quot;</span>.<span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$i</span><span style="color: #cc66cc;">+1</span><span style="color: #66cc66;">&#41;</span>.<span style="color: #ff0000;">&quot; '$names[$i]'&quot;</span>;
      <span style="color: #66cc66;">&#125;</span>
  <span style="color: #66cc66;">&#125;</span></pre></div></div>

<p>Which provides similar private scope for the <em>$i</em> variable, at the cost of three extra lines.</p>
<p><strong>Making it Testable</strong></p>
<p>So far, I haven&#8217;t made any changes that are likely to break the code.  It&#8217;s merely been cleanup and syntax tweaks.  But, to accomplish everything I&#8217;d like in this exercise, we&#8217;ll be doing some refactoring and refining the code.  To do that with confidence, it&#8217;d be nice to have some tests to insure the code works the same before and after any changes.</p>
<p>Since this is not historically a library, it&#8217;s not particularly easy to test.  One could write a custom test harness, or use Test::Command, and test its behavior as a whole, but since it&#8217;s written in Perl and one of my goals is to make it useful as a library from Perl scripts, I decided instead to make it loadable as a module and use Test::More.  A trick that&#8217;s very common in the Python world, but doesn&#8217;t seem as well-known amongst Perlmongers is a <em>main</em> function which is called if the script is executed independently rather than via <em>use</em> or <em>require</em>.  The <em>main</em> function then calls whatever the script would normally do, optionally setting up variables or parsing command line arguments.</p>
<p>So, I added the following near the beginning of the file:</p>

<div class="wp_syntax"><div class="code"><pre class="perl"><span style="color: #808080; font-style: italic;"># main</span>
<span style="color: #000000; font-weight: bold;">sub</span> main<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
<span style="color: #b1b100;">if</span> <span style="color: #66cc66;">&#40;</span>$<span style="color: #808080; font-style: italic;">#ARGV &lt; 1) { die &quot;Usage: $0 os_list.txt outfile [0|1|2|3]\n&quot;; }</span>
<span style="color: #b1b100;">my</span> <span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$oslist</span>, <span style="color: #0000ff;">$out</span>, <span style="color: #0000ff;">$auto</span><span style="color: #66cc66;">&#41;</span> = <span style="color: #0000ff;">@ARGV</span>;
oschooser<span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$oslist</span>, <span style="color: #0000ff;">$out</span>, <span style="color: #0000ff;">$auto</span><span style="color: #66cc66;">&#41;</span>;
<span style="color: #66cc66;">&#125;</span>
main<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span> <span style="color: #b1b100;">unless</span> <span style="color: #000066;">caller</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;  <span style="color: #808080; font-style: italic;"># make it testable and usable as a library</span></pre></div></div>

<p>I also took this opportunity to add a simple usage message if the command is executed with fewer than two arguments (@ARGV, like all Perl arrays starts counting at 0).  I also needed to wrap the main function of the script in a <em>sub</em> block, so that the script doesn&#8217;t do anything immediately if loaded as a library.</p>
<p><strong>Make it a Module</strong></p>
<p>Since I want to use this code as a library, I face a choice.  The <em>use</em> statement is functionally equivalent to:</p>

<div class="wp_syntax"><div class="code"><pre class="perl"><span style="color: #000000; font-weight: bold;">BEGIN</span> <span style="color: #66cc66;">&#123;</span> <span style="color: #000066;">require</span> Module; Module-&gt;<span style="color: #006600;">import</span><span style="color: #66cc66;">&#40;</span> LIST <span style="color: #66cc66;">&#41;</span>; <span style="color: #66cc66;">&#125;</span></pre></div></div>

<p>Which means, I suppose, I could keep the name oschooser.pl and use:</p>

<div class="wp_syntax"><div class="code"><pre class="perl"><span style="color: #000066;">require</span> <span style="color: #ff0000;">'oschooser.pl'</span>;</pre></div></div>

<p>We don&#8217;t need BEGIN level assurance, since we have no prototypes in this library and only use simple subroutines.  But, I find this a bit unsatisfying, since it&#8217;s no longer in common use amongst Perl developers, and <em>use</em> provides the ability to export functions explicitly.  Test::More has both a <em>use_ok</em> and a <em>require_ok</em> function, so it&#8217;s irrelevant from a testing perspective.  It&#8217;ll probably remain <em>oschooser.pl</em> in Webmin proper, and <em>OsChooser.pm</em> in my Virtualmin installer library, at least for the foreseeable future.  Not really a lot of difference between the two.</p>
<p><strong>Some Tests</strong></p>
<p>So, now that we can call the library roughly the way we want, using <em>use</em>, it&#8217;s time to write a few tests to be sure things actually work after we begin making more sweeping changes.</p>
<p>We can start with simple compile tests (I usually call these types of tests <em>t/return.t</em>, as they just check to be sure the module returns without error on load and the functions within return the data type that is expected):</p>

<div class="wp_syntax"><div class="code"><pre class="perl"><span style="color: #808080; font-style: italic;">#!/usr/bin/perl -w</span>
<span style="color: #808080; font-style: italic;"># These tests just check to be sure all functions return something</span>
<span style="color: #808080; font-style: italic;"># It doesn't care what it is returned...so garbage can still pass,</span>
<span style="color: #808080; font-style: italic;"># as long as the garbage is the right data type.</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">use</span> strict;
<span style="color: #000000; font-weight: bold;">use</span> Test::<span style="color: #006600;">More</span> <span style="color: #000066;">qw</span><span style="color: #66cc66;">&#40;</span>no_plan<span style="color: #66cc66;">&#41;</span>;
&nbsp;
use_ok<span style="color: #66cc66;">&#40;</span> <span style="color: #ff0000;">'OsChooser'</span> <span style="color: #66cc66;">&#41;</span>;
&nbsp;
isa_ok<span style="color: #66cc66;">&#40;</span>\OsChooser::<span style="color: #006600;">have_tty</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>, <span style="color: #ff0000;">'SCALAR'</span><span style="color: #66cc66;">&#41;</span>;
isa_ok<span style="color: #66cc66;">&#40;</span>\OsChooser::<span style="color: #006600;">has_command</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;cp&quot;</span><span style="color: #66cc66;">&#41;</span>, <span style="color: #ff0000;">'SCALAR'</span><span style="color: #66cc66;">&#41;</span>;</pre></div></div>

<p>Hmm&#8230;OK, so we don&#8217;t actually have a lot to test yet, just a couple of utility functions (and I&#8217;ve even cheated a little and looked ahead to where I introduced a <em>have_tty</em> function, or this would be an even shorter set of tests).  The most important function, <em>oschooser</em>, doesn&#8217;t know how to return anything very useful yet.  It can only write out its findings to a file.  But, since we&#8217;re always going to be creating that file, regardless of how nice the module usage becomes, we need to figure out how to test it anyway.</p>
<p>Unsurprisingly, there is already a full-featured module on CPAN for testing the contents of files, called, unlikely though it may seem, <em>Test::Files</em>.  So, we&#8217;ll just grab that:</p>

<div class="wp_syntax"><div class="code"><pre class="bash">$ <span style="color: #c20cb9; font-weight: bold;">sudo</span> <span style="color: #c20cb9; font-weight: bold;">perl</span> -MCPAN -e shell
&nbsp;
cpan shell -- CPAN exploration and modules installation <span style="color: #7a0874; font-weight: bold;">&#40;</span>v1<span style="color: #000000;">.7602</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>
ReadLine support enabled
&nbsp;
cpan&gt; <span style="color: #c20cb9; font-weight: bold;">install</span> Test::Files
...</pre></div></div>

<p>And then create as many Operating System definition files as we want in the <em>t</em> directory.  We&#8217;ll just name them for the OS they represent.  This is the kind of testing I love, because the actual test file will be extremely simple, no matter how many operating systems I want to test on:</p>

<div class="wp_syntax"><div class="code"><pre class="perl"><span style="color: #808080; font-style: italic;">#!/usr/bin/perl -w</span>
<span style="color: #000000; font-weight: bold;">use</span> strict;
<span style="color: #000000; font-weight: bold;">use</span> OsChooser;
&nbsp;
<span style="color: #808080; font-style: italic;"># Get a list of the example OS definition files</span>
<span style="color: #000066;">opendir</span><span style="color: #66cc66;">&#40;</span>DIR, <span style="color: #ff0000;">&quot;t/&quot;</span><span style="color: #66cc66;">&#41;</span> || <span style="color: #000066;">die</span> <span style="color: #ff0000;">&quot;can't opendir t/ $!&quot;</span>;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">@files</span> = <span style="color: #000066;">grep</span> <span style="color: #66cc66;">&#123;</span> /\.os/ <span style="color: #66cc66;">&#125;</span> <span style="color: #000066;">readdir</span><span style="color: #66cc66;">&#40;</span>DIR<span style="color: #66cc66;">&#41;</span>;
<span style="color: #000066;">closedir</span> DIR;
<span style="color: #000000; font-weight: bold;">use</span> Test::<span style="color: #006600;">More</span> <span style="color: #000066;">qw</span><span style="color: #66cc66;">&#40;</span>no_plan<span style="color: #66cc66;">&#41;</span>;
<span style="color: #000000; font-weight: bold;">use</span> Test::<span style="color: #006600;">Files</span>;
&nbsp;
<span style="color: #b1b100;">foreach</span> <span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$file</span> <span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">@files</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
  <span style="color: #0000ff;">$file</span> =~ /<span style="color: #66cc66;">&#40;</span>.*<span style="color: #66cc66;">&#41;</span>\.os$/;
  <span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$osname</span> = $<span style="color: #cc66cc;">1</span>;
  <span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$outfile</span> = <span style="color: #ff0000;">&quot;t/outfile&quot;</span>;
  OsChooser::<span style="color: #006600;">oschooser</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;os_list.txt&quot;</span>, <span style="color: #0000ff;">$outfile</span>, <span style="color: #cc66cc;">1</span><span style="color: #66cc66;">&#41;</span>;
  compare_ok<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;t/$file&quot;</span>, <span style="color: #0000ff;">$outfile</span>, <span style="color: #0000ff;">$osname</span><span style="color: #66cc66;">&#41;</span>;
&nbsp;
  <span style="color: #808080; font-style: italic;"># Cleanup</span>
  <span style="color: #000066;">unlink</span> <span style="color: #0000ff;">$outfile</span>;
<span style="color: #66cc66;">&#125;</span></pre></div></div>

<p>I love data-driven software, and this is a fun little example of it.  We can run as many tests as we want, merely by adding more OS data files&#8211;one with the &#8220;os&#8221; suffix to provide what <em>should</em> be output by <em>oschooser</em> and one to contain the file that <em>oschooser</em> would normally use to identify the OS (<em>/etc/issue</em>, among others), which isn&#8217;t yet supported, but I&#8217;ll talk about it in the next post.  Speaking of being data-driven, I think it&#8217;d also be pretty nifty to get the test count from the <em>@files</em> array, rather than using no_plan, but because modules loaded with <em>use</em> are loaded early during compile time (in a BEGIN block, effectively) we don&#8217;t actually have anything in <em>@files</em> yet.</p>
<p>However, as mentioned, the <em>oschooser</em> function doesn&#8217;t yet allow one to specify the <em>issue</em> file to look at, so no matter how many definitions I provide, it&#8217;ll never be able to test anything but the OS the test is running on.  Oh, well, for now we&#8217;ll just create one OS definition file that matches my current OS, and make it a priority to make the function more testable somehow, possibly via an optional parameter to <em>oschooser</em>.</p>
<p>Alright, so now that we have some rudimentary tests in place, we can break stuff with confidence!  We&#8217;ll come back to testing again in the near future, since we&#8217;re leaving so much untested right now.</p>
<p><strong>Plain Old Documentation</strong></p>
<p>I&#8217;m going to take a quick detour now that we&#8217;ve got some basic tests in place.  Testing is one practice that most developers agree makes for great code, and the other practice that most folks can agree on is documentation.</p>
<p>Since this is such a simple piece of code, and was intended exclusively for use during installation of Webmin and Usermin, Jamie never really documented it.  Now that I&#8217;m forcing it to be useful in other locations, and having some fun giving it a modern Perl face lift, it&#8217;s as good a time as any to add some documentation.  POD isn&#8217;t the only documentation format usable within Perl code, but it is, by far, the most popular, and it has lots of great tools for processing and testing coverage, so that&#8217;s what Jamie recently chose for use in documenting the Virtualmin API.  It&#8217;s also easy to learn, and results in text that is pretty readable even before processing.</p>
<p>I&#8217;m not sure of the recommended practices for documenting scripts that work on both the command line and as a module, but here&#8217;s what I came up with:</p>

<div class="wp_syntax"><div class="code"><pre class="perl">=head1 OsChooser.pm
&nbsp;
Attempt to detect operating <span style="color: #000066;">system</span> <span style="color: #b1b100;">and</span> version, <span style="color: #b1b100;">or</span> ask the user to <span style="color: #000066;">select</span>
from a list.  Works from the command line, <span style="color: #b1b100;">for</span> usage from shell scripts,
<span style="color: #b1b100;">or</span> as a library <span style="color: #b1b100;">for</span> <span style="color: #000000; font-weight: bold;">use</span> within Perl scripts.
&nbsp;
=head2 COMMAND LINE USE
&nbsp;
OsChooser.pm os_list.txt outfile <span style="color: #66cc66;">&#91;</span>auto<span style="color: #66cc66;">&#93;</span>
&nbsp;
Where <span style="color: #ff0000;">&quot;auto&quot;</span> can be the following <span style="color: #000066;">values</span>:
&nbsp;
=over <span style="color: #cc66cc;">4</span>
&nbsp;
=item <span style="color: #cc66cc;">0</span>
&nbsp;
always ask user
&nbsp;
=item <span style="color: #cc66cc;">1</span>
&nbsp;
automatic, give up <span style="color: #b1b100;">if</span> fails
&nbsp;
=item <span style="color: #cc66cc;">2</span>
&nbsp;
automatic, ask user <span style="color: #b1b100;">if</span> fails
&nbsp;
=item <span style="color: #cc66cc;">3</span>
&nbsp;
automatic, ask user <span style="color: #b1b100;">if</span> fails <span style="color: #b1b100;">and</span> <span style="color: #b1b100;">if</span> a TTY
&nbsp;
=back
&nbsp;
=head2 SYNOPSIS
&nbsp;
    <span style="color: #000000; font-weight: bold;">use</span> OsChooser;
    <span style="color: #b1b100;">my</span> <span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$os_type</span>, <span style="color: #0000ff;">$version</span>, <span style="color: #0000ff;">$real_os_type</span>, <span style="color: #0000ff;">$real_os_version</span><span style="color: #66cc66;">&#41;</span> =
       OsChooser-&gt;<span style="color: #006600;">oschooser</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;os_list.txt&quot;</span>, <span style="color: #ff0000;">&quot;outfile&quot;</span>, <span style="color: #0000ff;">$auto</span>, <span style="color: #66cc66;">&#91;</span><span style="color: #0000ff;">$issue</span><span style="color: #66cc66;">&#93;</span><span style="color: #66cc66;">&#41;</span>;
&nbsp;
=cut</pre></div></div>

<p>Pretty simple, but covers the basics.</p>
<p><strong>Next Time</strong></p>
<p>Unfortunately, the code is now longer and probably a little less readable than before!  It&#8217;s probably more robust to changes, since it now has reasonably scoped variables.  And it&#8217;s more friendly to others who might want to use it, due to the new documentation and the ability to use it as a library in Perl or as a command in shell scripts.</p>
<p>Next time we&#8217;ll start in on the refactoring, and we&#8217;ll also write some more tests.  This is turning into a real challenge, due to the data-driven nature of the script, and the fact that it&#8217;s somewhat hardcoded to look for OS data in very specific locations.  Since, a big part of what I want to test is in the <em>os_list.txt</em> file, we don&#8217;t have the luxury of just saying, &#8220;It&#8217;s configuration&#8230;we&#8217;ll just make a special version for testing purposes.&#8221;  We&#8217;ll have to get far more clever.</p>
]]></content:encoded>
			<wfw:commentRss>http://inthebox.webmin.com/old-school-to-new-school-refactoring-perl/feed</wfw:commentRss>
		</item>
		<item>
		<title>YAPC::NA</title>
		<link>http://inthebox.webmin.com/yapcna</link>
		<comments>http://inthebox.webmin.com/yapcna#comments</comments>
		<pubDate>Sat, 31 May 2008 01:53:13 +0000</pubDate>
		<dc:creator>Joe Cooper</dc:creator>
		
		<category><![CDATA[News]]></category>

		<guid isPermaLink="false">http://inthebox.webmin.com/?p=26</guid>
		<description><![CDATA[
Virtualmin, Inc. is sponsoring this years YAPC::NA, and Jamie and I will both be in attendance throughout the event.  If you&#8217;re attending, drop us a line.  We&#8217;ll have T-shirts (Virtualmin and VM2 logos in all sizes and a multitude of sweet colors!) and a bunch of Virtualmin products to give away.
]]></description>
			<content:encoded><![CDATA[<p><a href="http://conferences.mongueurs.net/yn2008/index.html"><img src="http://inthebox.webmin.com/images/yapcna_2008_im_going.gif" border="0" alt="" /></a></p>
<p>Virtualmin, Inc. is sponsoring this years YAPC::NA, and Jamie and I will both be in attendance throughout the event.  If you&#8217;re attending, drop us a line.  We&#8217;ll have T-shirts (Virtualmin <em>and</em> VM2 logos in all sizes and a multitude of sweet colors!) and a bunch of Virtualmin products to give away.</p>
]]></content:encoded>
			<wfw:commentRss>http://inthebox.webmin.com/yapcna/feed</wfw:commentRss>
		</item>
		<item>
		<title>Community, or not community</title>
		<link>http://inthebox.webmin.com/community-or-not-community</link>
		<comments>http://inthebox.webmin.com/community-or-not-community#comments</comments>
		<pubDate>Tue, 08 Apr 2008 22:11:43 +0000</pubDate>
		<dc:creator>Joe Cooper</dc:creator>
		
		<category><![CDATA[Business]]></category>

		<category><![CDATA[Snippets]]></category>

		<category><![CDATA[Group of men at Ellis IslandFarmer turning sod]]></category>

		<guid isPermaLink="false">http://inthebox.webmin.com/community-or-not-community</guid>
		<description><![CDATA[I chatted with an applicant to the Y Combinator Summer Founders Program this afternoon, and he brought up a really interesting question that I&#8217;d never really thought about before.  His question was, roughly, &#8220;Do community-based or value-based Y Combinator backed businesses do better?&#8221;  Let&#8217;s assume that a &#8220;community-based&#8221; business is one where the user base [...]]]></description>
			<content:encoded><![CDATA[<p>I chatted with an applicant to the <a href="http://www.ycombinator.com">Y Combinator</a> Summer Founders Program this afternoon, and he brought up a really interesting question that I&#8217;d never really thought about before.  His question was, roughly, &#8220;Do community-based or value-based Y Combinator backed businesses do better?&#8221;  Let&#8217;s assume that a &#8220;community-based&#8221; business is one where the user base creates the vast majority of the value (like reddit), and a &#8220;value-based&#8221; business is one where there is value even with only one user (like Zenter).  So, by this definition, a community-based site must reach a certain level of popularity before random folks on the Internet want to use it, while a value-based site can make life easier for a single person and that single person will keep coming back.</p>
<p>The reason this is interesting, to me, is that Y Combinator <em>likes</em> community-based companies, and my impression (admittedly supported by merely anecdotal and circumstantial evidence) is that they prefer them to value-based businesses, though this preference may be changing over time.  So, the question it raises is whether having a preference (even a mild one) for community-based businesses results in better outcomes for Y Combinator.  I suspect that it has a negative impact, but I don&#8217;t know that for sure.</p>
<p>We <em>can</em> break it down by exit, though:</p>
<p><strong>Community-based YC exits</strong></p>
<ul>
<li>Reddit - Rumored 12-ish million dollar acquisition by Condé Nast</li>
</ul>
<p><img src="/images/ellis-island-group.jpg" alt="Group of men at Ellis Island" width="465" height="335" /></p>
<p><strong>Value-based YC exits</strong></p>
<ul>
<li>Zenter - Undisclosed acquisition by Google</li>
<li>Parakey - Undisclosed acquisition by Facebook</li>
<li>Auctomatic - 5 million dollar cash+stock acquisition by Live Communicate</li>
<li>TextPayMe - Undisclosed acquisition by Amazon</li>
<li>Anywhere.fm* - Undisclosed acquisition by Imeem</li>
</ul>
<p><img src="/images/turning-sod.jpg" alt="Farmer turning sod" width="465" height="326" /></p>
<p>I&#8217;ve put an asterisk by Anywhere.fm above, as I&#8217;m certain some folks would consider it a community-based business&#8230;but it provides most of its value even with only one user, so it fits my requirements for a value-based business, and I think it&#8217;s fair to say they were not acquired based predominantly on the size of their community or traffic growth but on the strength of the application they&#8217;d built.</p>
<p>Of course, since all but one of those acquisitions in the &#8220;value-based&#8221; category are for an undisclosed sum, and I&#8217;m basing the reddit acquisition price on rumors (though rumors from disparate sources, I&#8217;ve never heard the numbers from any of the people actually involved), it&#8217;s hard to say with any confidence that the five exits in the value-based group are worth more to YC than the one community-based exit.  But, one can safely say that value-based businesses have made a lot more young YC-backed entrepreneurs rich than community-based businesses.</p>
<p>This isn&#8217;t the whole picture, of course.  Some of the biggest potential YC success stories have yet to have an exit.   It&#8217;s hard to argue that Loopt isn&#8217;t going to  be a big success story for YC, and it&#8217;s pretty much purely community-based, though they have the added hurdle of having to sell to mobile providers to attain their customer base.  Scribd has seen tremendous traffic growth and it&#8217;s a community-based site with a small amount of value independent of the community.  Obviously, &#8220;going viral&#8221; is a fantastic marketing opportunity that is only generally available to community-based businesses, but historically it seems that building a useful application is more likely to result in a successful exit than building a product that relies on a large community to have value.</p>
<p>I found this result surprising when I began listing off the exits and thinking about what kind of business they were.  I&#8217;ve always thought of Virtualmin as an outlier among YC companies, since we build a traditional installable web-based application, but when broken down into value vs. community, we&#8217;re not so far out.</p>
<p>Anyway, I don&#8217;t think I&#8217;ve stumbled onto anything earth-shaking in this little exercise, of course, but I do think it would be interesting to break down <em>all</em> YC companies (and perhaps even everything in the <a href="http://www.crunchbase.com/">TechCrunch Crunchbase</a>) into this categorization and see how things shake out with regard to who is still around and growing.  Maybe later&#8230;right now I&#8217;ve gotta go build some value.</p>
]]></content:encoded>
			<wfw:commentRss>http://inthebox.webmin.com/community-or-not-community/feed</wfw:commentRss>
		</item>
		<item>
		<title>Extending Virtualmin with plugins</title>
		<link>http://inthebox.webmin.com/extending-virtualmin-with-plugins</link>
		<comments>http://inthebox.webmin.com/extending-virtualmin-with-plugins#comments</comments>
		<pubDate>Mon, 03 Mar 2008 23:51:22 +0000</pubDate>
		<dc:creator>Jamie</dc:creator>
		
		<category><![CDATA[Development]]></category>

		<category><![CDATA[Snippets]]></category>

		<category><![CDATA[development]]></category>

		<category><![CDATA[plugins]]></category>

		<category><![CDATA[virtualmin]]></category>

		<guid isPermaLink="false">http://inthebox.webmin.com/extending-virtualmin-with-plugins</guid>
		<description><![CDATA[
Not many people know that Virtualmin&#8217;s already extensive list of built-in features can be extended by writing plugins, which are basically Webmin modules that export a special API. Why would you want to do this, you may ask? Let&#8217;s say their is a mailing list application, log analyzer, database or source code control system that [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/images/Runner-for-37500-HP-turbine.jpeg" alt="Plugins can be big" width="496" height="519" /></p>
<p>Not many people know that Virtualmin&#8217;s already extensive list of built-in features can be extended by writing plugins, which are basically Webmin modules that export a special API. Why would you want to do this, you may ask? Let&#8217;s say their is a mailing list application, log analyzer, database or source code control system that you want to make available on a per-domain basis .. if so, a plugin is the way to do it.</p>
<p>A plugin is typically used to a new <strong>feature </strong>to Virtualmin. In it&#8217;s parlance, a feature is something that is enabled on a per-domain basis, such as a website, DNS domain or MySQL database. Let&#8217;s say you have discovered an awesome new log analysis program that you want run on each domain&#8217;s log files - a plugin would be the way to implement it.</p>
<p>A plugin can also add options to mailbox users. The most common use of this is to grant access on a per-user basis to some resource, such as statistics, an application or database. Plugins can also create new database types, add links to the left menu in the Virtualmin framed theme, and add sections to it&#8217;s system information page.</p>
<p>Some of the existing plugins give you an idea of what&#8217;s possible :</p>
<ol>
<li>The <strong>DAV </strong>plugin adds a feature which makes a virtual server&#8217;s web pages editable from applications that support the protocol, such as Windows and OSX. It also lets you enable DAV logins for each mailbox in the domain.</li>
<li>The <strong>Bootup Actions</strong> plugin allows domain owners to have their long-running server processes started when the system boots.</li>
<li>The <strong>Mail Relay</strong> plugin lets you forward email for a domain to another server, which can be configured by the domain owner.</li>
<li>The <strong>Admin Notes</strong> feature adds a new section to the right-hand frame for entering comments about the system, for sharing status between master admins.</li>
</ol>
<p>To see a full list of plugins that exist, check out the <a href="http://www.webmin.com/cgi-bin/search_third.cgi?category=Virtualmin">third-party modules database</a>.</p>
<p>If you know Perl, have written a regular Webmin module, and want to write your own plugin, check out the <a href="http://www.virtualmin.com/documentation/id,writing_virtualmin_plugins/">extensive documentation</a> on the API.</p>
]]></content:encoded>
			<wfw:commentRss>http://inthebox.webmin.com/extending-virtualmin-with-plugins/feed</wfw:commentRss>
		</item>
		<item>
		<title>Webmin::API: Using Webmin as a library</title>
		<link>http://inthebox.webmin.com/webminapi-using-webmin-as-a-library</link>
		<comments>http://inthebox.webmin.com/webminapi-using-webmin-as-a-library#comments</comments>
		<pubDate>Wed, 12 Dec 2007 03:04:14 +0000</pubDate>
		<dc:creator>Joe Cooper</dc:creator>
		
		<category><![CDATA[Development]]></category>

		<category><![CDATA[Features]]></category>

		<guid isPermaLink="false">http://inthebox.webmin.com/webminapi-using-webmin-as-a-library</guid>
		<description><![CDATA[
Webmin is perhaps the largest bundle of system administration related Perl code in existence (outside of CPAN, of course), much of which is unavailable anywhere else.  I often find myself wishing for a function or two from Webmin in my day-to-day Perl scripting.  Historically, one could use Webmin functions by first pulling in all of [...]]]></description>
			<content:encoded><![CDATA[<p><img src="/images/man-and-wrench.jpg" height="506" width="496" /></p>
<p>Webmin is perhaps the largest bundle of system administration related Perl code in existence (outside of CPAN, of course), much of which is unavailable anywhere else.  I often find myself wishing for a function or two from Webmin in my day-to-day Perl scripting.  Historically, one could use Webmin functions by first pulling in all of the bits and pieces manually, and running a few of the helper functions.  For example, at Virtualmin, Inc. we use this bit of code to start up the configuration stage of our install scripts:</p>

<div class="wp_syntax"><div class="code"><pre class="perl"><span style="color: #808080; font-style: italic;">#!/usr/bin/perl</span>
$|=<span style="color: #cc66cc;">1</span>;
<span style="color: #808080; font-style: italic;"># Setup Webmin environment</span>
<span style="color: #0000ff;">$no_acl_check</span>++;
<span style="color: #0000ff;">$ENV</span><span style="color: #66cc66;">&#123;</span><span style="color: #ff0000;">'WEBMIN_CONFIG'</span><span style="color: #66cc66;">&#125;</span> ||= <span style="color: #ff0000;">&quot;/etc/webmin&quot;</span>;
<span style="color: #0000ff;">$ENV</span><span style="color: #66cc66;">&#123;</span><span style="color: #ff0000;">'WEBMIN_VAR'</span><span style="color: #66cc66;">&#125;</span> ||= <span style="color: #ff0000;">&quot;/var/webmin&quot;</span>;
<span style="color: #0000ff;">$ENV</span><span style="color: #66cc66;">&#123;</span><span style="color: #ff0000;">'MINISERV_CONFIG'</span><span style="color: #66cc66;">&#125;</span> = <span style="color: #0000ff;">$ENV</span><span style="color: #66cc66;">&#123;</span><span style="color: #ff0000;">'WEBMIN_CONFIG'</span><span style="color: #66cc66;">&#125;</span>.<span style="color: #ff0000;">&quot;/miniserv.conf&quot;</span>;
<span style="color: #000066;">open</span><span style="color: #66cc66;">&#40;</span>CONF, <span style="color: #ff0000;">&quot;$ENV{'WEBMIN_CONFIG'}/miniserv.conf&quot;</span><span style="color: #66cc66;">&#41;</span> || <span style="color: #000066;">die</span> <span style="color: #ff0000;">&quot;Failed to open miniserv.conf&quot;</span>;
<span style="color: #b1b100;">while</span><span style="color: #66cc66;">&#40;</span><span style="color: #009999;">&lt;CONF&gt;</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
  <span style="color: #b1b100;">if</span> <span style="color: #66cc66;">&#40;</span>/^root=<span style="color: #66cc66;">&#40;</span>.*<span style="color: #66cc66;">&#41;</span>/<span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
    <span style="color: #0000ff;">$root</span> = $<span style="color: #cc66cc;">1</span>;
    <span style="color: #66cc66;">&#125;</span>
  <span style="color: #66cc66;">&#125;</span>
<span style="color: #000066;">close</span><span style="color: #66cc66;">&#40;</span>CONF<span style="color: #66cc66;">&#41;</span>;
<span style="color: #0000ff;">$root</span> ||= <span style="color: #ff0000;">&quot;/usr/libexec/webmin&quot;</span>;
<span style="color: #000066;">chdir</span><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$root</span><span style="color: #66cc66;">&#41;</span>;
<span style="color: #000066;">require</span> <span style="color: #ff0000;">'./web-lib.pl'</span>;
init_config<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;</pre></div></div>

<p>Wow.  That&#8217;s a lot of extraneous crap just to make use of Webmin functions.  Not all of that is necessary in every script that wants to use Webmin functions, but it&#8217;s always something I have to refer to the documentation for.</p>
<p>So, I&#8217;ve been bugging Jamie for some time to make a simpler way to get at the Webmin API, and he&#8217;s just released the <a href="http://www.webmin.com/modules-commandline.html">Webmin::API Perl module</a>.   To use it, you&#8217;ll first need Webmin installed.  There&#8217;s an RPM, deb, tarball, and Solaris pkg, so it&#8217;s easy no matter what UNIX-like OS you run (it&#8217;ll also run on Windows, but only in relatively limited fashion), and then you can install it like any other Perl module:</p>
<pre># tar xvzf Webmin-API-1.0.tar.gz
# cd Webmin-API
# perl Makefile.PL
# make install</pre>
<p>Once that&#8217;s done, you can make use of the entirety of the web-lib.pl, plus the libraries for all of the Webmin modules.  For example, one could access all of the Webmin variables, like <em>%gconfig</em>, as well as all of the web-lib.pl functions, such as <tt>ftp_download (pure Perl FTP client), </tt><tt>kill_byname (like killall), </tt><tt>nice_size (return a number in GB, MB, etc.), </tt><tt>running_in_zone (detects whether it&#8217;s running in a Solaris Zone), etc.</tt></p>
<p>So, making an application that downloads and does something with remote files is trivial, for example.  But, probably more interesting, is that once Webmin::API has been loaded, you can make use of the foreign_require function, which is used to access any available Webmin module function library.</p>
<p>For example, if I wanted to make sure Postfix was configured to use Maildir mail spools, I could do the following:</p>

<div class="wp_syntax"><div class="code"><pre class="perl">foreign_require<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;postfix&quot;</span>, <span style="color: #ff0000;">&quot;postfix-lib.pl&quot;</span><span style="color: #66cc66;">&#41;</span>;
postfix::<span style="color: #006600;">set_current_value</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;home_mailbox&quot;</span>, <span style="color: #ff0000;">&quot;Maildir/&quot;</span>, <span style="color: #cc66cc;">1</span><span style="color: #66cc66;">&#41;</span>;
postfix::<span style="color: #006600;">reload_postfix</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;</pre></div></div>

<p>That&#8217;s it.  No need to worry about parsing the file and no regex needed.  You don&#8217;t need to figure out where the Postfix main.cf is located (assuming Webmin is configured correctly), or what the proper way to restart the service is.</p>
<p>One common, and surprisingly complicated, task is setting up initscripts to start on boot.  It seems like every Linux distribution uses a slightly different directory layout, slightly different scripts, and different tools for managing the rc directories and files.  Webmin knows about the vast majority of those quirks, and provides a uniform interface to all of them, and this functionality is exposed to scripts via the init module.  For example, I could enable Postfix on boot with the following:</p>

<div class="wp_syntax"><div class="code"><pre class="perl">foreign_require<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;init&quot;</span>, <span style="color: #ff0000;">&quot;init-lib.pl&quot;</span><span style="color: #66cc66;">&#41;</span>;
init::<span style="color: #006600;">enable_at_boot</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;postfix&quot;</span><span style="color: #66cc66;">&#41;</span>;</pre></div></div>

<p>There is one unfortunate caveat to this: You have to know the name of the initscript.  On all of the systems I work with, this is pretty consistent across most services, with the exception of Apache.  One Red Hat based systems the Apache services is called <em>httpd</em>, while on Debian/Ubuntu systems it is <em>apache2</em>.  Some systems also call it <em>apache.</em></p>
<h4>Working With the Linux Firewall</h4>
<p>One of the most powerful Webmin modules is the Linux Firewall module, which manages an iptables firewall.  It is nearly comprehensive, covering many of the advanced stateful capabilities, as well as logging and creation of and management of arbitrary chains.  We can make use of the basic functionality of the module by importing the firewall library.</p>

<div class="wp_syntax"><div class="code"><pre class="perl">foreign_require<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;firewall&quot;</span>, <span style="color: #ff0000;">&quot;firewall-lib.pl&quot;</span><span style="color: #66cc66;">&#41;</span>;</pre></div></div>

<p>Once imported, we have access to the get_iptables_save function, which imports any existing rules from the system default iptables save file into an array.  You can then work with them using standard Perl data management tools like push and splice.</p>
<p><img src="/images/shovel-and-ice.jpg" height="348" width="462" /></p>
<p>Say you want to open ports 10000 and 20000 (for Webmin and Usermin, of course).  Maybe you also want to make sure ssh (port 22) is available for those times when you need to hit the command line.  The simplest is probably to drop them into an array (so you can add new ports later without having to read code):</p>

<div class="wp_syntax"><div class="code"><pre class="perl"><span style="color: #808080; font-style: italic;">#!/usr/bin/perl</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">use</span> Webmin::<span style="color: #006600;">API</span>;
foreign_require<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;firewall&quot;</span>, <span style="color: #ff0000;">&quot;firewall-lib.pl&quot;</span><span style="color: #66cc66;">&#41;</span>;
<span style="color: #000000; font-weight: bold;">use</span> warnings;
&nbsp;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">@tcpports</span> = <span style="color: #000066;">qw</span><span style="color: #66cc66;">&#40;</span>ssh <span style="color: #cc66cc;">10000</span> <span style="color: #cc66cc;">20000</span><span style="color: #66cc66;">&#41;</span>;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">@tables</span> = &amp;amp;firewall::<span style="color: #006600;">get_iptables_save</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;
<span style="color: #66cc66;">&#40;</span><span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$filter</span><span style="color: #66cc66;">&#41;</span> = <span style="color: #000066;">grep</span> <span style="color: #66cc66;">&#123;</span> <span style="color: #0000ff;">$_</span>-&gt;<span style="color: #66cc66;">&#123;</span><span style="color: #ff0000;">'name'</span><span style="color: #66cc66;">&#125;</span> eq <span style="color: #ff0000;">'filter'</span> <span style="color: #66cc66;">&#125;</span> <span style="color: #0000ff;">@tables</span>;
<span style="color: #b1b100;">if</span> <span style="color: #66cc66;">&#40;</span>!<span style="color: #0000ff;">$filter</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
  <span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$filter</span> = <span style="color: #66cc66;">&#123;</span> <span style="color: #ff0000;">'name'</span> =&gt; <span style="color: #ff0000;">'filter'</span>,
              <span style="color: #ff0000;">'rules'</span> =&gt; <span style="color: #66cc66;">&#91;</span> <span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#125;</span>;
<span style="color: #66cc66;">&#125;</span>
&nbsp;
<span style="color: #b1b100;">foreach</span> <span style="color: #66cc66;">&#40;</span> <span style="color: #0000ff;">@tcpports</span> <span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
  <span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;  Allowing traffic on TCP port: $_<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
  <span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$newrule</span> = <span style="color: #66cc66;">&#123;</span> <span style="color: #ff0000;">'chain'</span> =&gt; <span style="color: #ff0000;">'INPUT'</span>,
               <span style="color: #ff0000;">'p'</span> =&gt; <span style="color: #66cc66;">&#91;</span> <span style="color: #66cc66;">&#91;</span> <span style="color: #ff0000;">''</span>, <span style="color: #ff0000;">'tcp'</span> <span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#93;</span>,
               <span style="color: #ff0000;">'dport'</span> =&gt; <span style="color: #66cc66;">&#91;</span> <span style="color: #66cc66;">&#91;</span> <span style="color: #ff0000;">''</span>, <span style="color: #0000ff;">$_</span> <span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#93;</span>,
               <span style="color: #ff0000;">'j'</span> =&gt; <span style="color: #66cc66;">&#91;</span> <span style="color: #66cc66;">&#91;</span> <span style="color: #ff0000;">''</span>, <span style="color: #ff0000;">'ACCEPT'</span> <span style="color: #66cc66;">&#93;</span> <span style="color: #66cc66;">&#93;</span>,
             <span style="color: #66cc66;">&#125;</span>;
  <span style="color: #000066;">splice</span><span style="color: #66cc66;">&#40;</span>@<span style="color: #66cc66;">&#123;</span><span style="color: #0000ff;">$filter</span>-&gt;<span style="color: #66cc66;">&#123;</span><span style="color: #ff0000;">'rules'</span><span style="color: #66cc66;">&#125;</span><span style="color: #66cc66;">&#125;</span>, <span style="color: #cc66cc;">0</span>, <span style="color: #cc66cc;">0</span>, <span style="color: #0000ff;">$newrule</span><span style="color: #66cc66;">&#41;</span>;
<span style="color: #66cc66;">&#125;</span>
firewall::<span style="color: #006600;">save_table</span><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$filter</span><span style="color: #66cc66;">&#41;</span>;
firewall::<span style="color: #006600;">apply_configuration</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;</pre></div></div>

<p>This reads the existing rules, and adds new ones, saves it out, and applies the new rules. The rules that this creates are identical to what you would get if you&#8217;d entered the following on the command line on a Red Hat based system:</p>
<pre>iptables -I INPUT -p tcp --dport ssh -j ACCEPT</pre>
<pre>iptables -I INPUT -p tcp --dport 10000 -j ACCEPT</pre>
<pre>iptables -I INPUT -p tcp --dport 20000 -j ACCEPT</pre>
<pre>service iptables save</pre>
<p>Now, of course you could do all of that with backticks and subsitution, but you&#8217;d have to add a bunch of additional logic to figure out whether to use <em>iptables-save</em>, <em>service iptables save</em>, or some variant of the former with an option or two (Debian and Ubuntu have a rather complex set of firewall configuration files, and thus the appropriate iptables save file may not be immediately obvious).  And, dealing with things programmatically is more difficult, if you want to do something interesting like &#8220;only add a rule if these two other rules already exist, otherwise add the following two rules&#8221;.   And, reading and parsing the rather complex save file and writing it back out yourself can be a challenge (feel free to steal the Webmin code for it, if you prefer not to need all of Webmin).</p>
<h4>Known Issues</h4>
<p>This Perl module is new, so it&#8217;s pretty safe to say there is room for improvement.  The biggest is that only the core Webmin web-lib.pl and ui-lib.pl functions are documented, and thus the vast majority of functionality found in Webmin you&#8217;ll have to parse out from the relevant modules yourself.  I plan to spend some time adding POD documentation to each of those libraries in the not too distant future, but in the meantime, the best documentation is the source itself.  Luckily, every library has an accompanying working example application in the form of the module that it is part of.</p>
<p>Another issue is that Webmin is full of old code.  It&#8217;s a ten year old codebase&#8230;and much of it isn&#8217;t &#8220;use strict&#8221; or even &#8220;use warnings&#8221; compliant.   You can, of course, trigger warnings after &#8220;use Webmin::API&#8221; and it works fine.  See my final iptables example for that kind of usage.  Strict is only usable, even after the import of Webmin, if you disable many types of check.  This is another issue I&#8217;ll spend some time on in the future.</p>
<p>In the meantime, there&#8217;s a lot of great functionality that&#8217;s just been made a little easier to make use of.  I&#8217;ll be writing several more articles with examples of using this API in the near future.  Specifically, the next installment of my series on <a href="http://inthebox.webmin.com/analysis-and-reporting-of-system-data">Analysis and Reporting of System Data</a>  will make use of the Webmin System and Server Status module to build a flexible ping monitoring and reporting tool in just a few lines of code.</p>
]]></content:encoded>
			<wfw:commentRss>http://inthebox.webmin.com/webminapi-using-webmin-as-a-library/feed</wfw:commentRss>
		</item>
		<item>
		<title>Sharing JavaScript Code in Webmin</title>
		<link>http://inthebox.webmin.com/sharing-javascript-code-in-webmin</link>
		<comments>http://inthebox.webmin.com/sharing-javascript-code-in-webmin#comments</comments>
		<pubDate>Wed, 31 Oct 2007 22:00:43 +0000</pubDate>
		<dc:creator>Joe Cooper</dc:creator>
		
		<category><![CDATA[Development]]></category>

		<category><![CDATA[Snippets]]></category>

		<guid isPermaLink="false">http://inthebox.webmin.com/sharing-javascript-code-in-webmin</guid>
		<description><![CDATA[I posted a while back on my personal blog about some UI enhancement work that I&#8217;ve been doing in Webmin using the ExtJS JavaScript toolkit.  Several folks had questions about whether Webmin was getting a new &#8220;official&#8221; JavaScript toolkit (it has some ancient and ugly API calls to generate a few JavaScript helpers for [...]]]></description>
			<content:encoded><![CDATA[<p>I posted a while back on my personal blog about some UI enhancement work that I&#8217;ve been doing in Webmin using the ExtJS JavaScript toolkit.  Several folks had questions about whether Webmin was getting a new &#8220;official&#8221; JavaScript toolkit (it has some ancient and ugly API calls to generate a few JavaScript helpers for things like field graying and validation and such, but they aint got that AJAX religion), and, if not, how one could add a JavaScript library to Webmin to cleanly share it across modules and themes.</p>
<p>So, the answer to the first question is that Webmin is not getting an &#8220;official&#8221; JavaScript toolkit at this time.  Webmin has as one of its core goals that it can be used by anyone anywhere with any browser.  AJAX and heavy JavaScript usage makes that goal far more complicated.  For example, we consider it a serious bug if a blind user using a screen reader can&#8217;t use Webmin.  That said, we also recognize that AJAX is the best way to handle huge classes of user interaction problems, and with our commercial offering we have a strong interest in having the best looking, and most pleasant to use, UI in the field.  So, I&#8217;ve begun to build a &#8220;semi-official&#8221; Webmin module that contains ExtJS and some helper functions and classes.  The first example usage of this will be our new TheJAX Virtualmin theme, and soon after a few new modules.</p>
<p>For the second question, I&#8217;d just like to show how I&#8217;ve created this new ExtJS module for Webmin, and how one can use it.  It only takes a few minutes to wrap something up into a module, and since most AJAX frameworks are making use of good JavaScript design practices and using their own namespaces, you can actually mix and match without too much pain.</p>
<p><strong>Hidden Modules</strong></p>
<p>So, Webmin has a very powerful module system, that allows you to package code for easy distribution and installation.  A Webmin module is simply a directory with some files in it.  Only one file is mandatory to make the directory into a &#8220;module&#8221;: <em>module.info</em></p>
<p>So, we create a directory named <em>extjs</em> within the Webmin directory (<em>/usr/libexec/webmin</em> on my system), and make a file called <em>module.info</em> with the following contents:</p>

<div class="wp_syntax"><div class="code"><pre class="bash"><span style="color: #007800;">name=</span>ExtJS
<span style="color: #007800;">desc=</span>ExtJS AJAX Toolkit
<span style="color: #007800;">depends=</span><span style="color: #000000;">1.360</span>
<span style="color: #007800;">version=</span><span style="color: #000000;">0.1</span>
<span style="color: #007800;">hidden=</span><span style="color: #000000;">1</span></pre></div></div>

<p>Here I&#8217;ve given it a name, and a short description, noted the version of Webmin it depends on, given it a version (I&#8217;m going to stat it at 0.1, though the contained ExtJS version is 2.0b), and set it to be hidden.  The hidden option means that users won&#8217;t be able to see this module in the UI, but other modules can make calls to it.  Later, if I decide to add configurable options to this library that I do want users to be able to see, I can make it visible and add an icon and a UI.</p>
<p>Now, I can start dropping in my files.  I merely unzipped the ExtJS bundle, deleted the extraneous files, and dropped it into an <em>ext</em> directory within the module directory.  That&#8217;s just to make it easy to update ExtJS components separately from the helper functions that I write in Perl in the top-level directory.</p>
<p><strong>Helper Functions</strong></p>
<p>So, the simplest thing to automate away is the inclusion of the script tags that load the library.  So, I&#8217;ll create a <em>header_text</em> function in a file called <em>extjs-lib.pl</em> (Webmin has a convention of calling function libraries <em>modulename</em>-lib.pl), which looks like this:</p>

<div class="wp_syntax"><div class="code"><pre class="perl"> <span style="color: #808080; font-style: italic;"># extjs-lib.pl</span>
&nbsp;
<span style="color: #b1b100;">do</span> <span style="color: #ff0000;">'../web-lib.pl'</span>;
&amp;amp;init_config<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;
&nbsp;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$debug</span>=<span style="color: #ff0000;">''</span>; <span style="color: #808080; font-style: italic;"># Set to '-debug' to use non-stripped library</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># header_text()</span>
<span style="color: #808080; font-style: italic;"># Text to load JavaScript and CSS for use of extjs</span>
<span style="color: #000000; font-weight: bold;">sub</span> header_text <span style="color: #66cc66;">&#123;</span>
  <span style="color: #000066;">return</span> &lt;EOF;
&lt;script src=<span style="color: #ff0000;">&quot;/extjs/ext/adapter/ext/ext-base.js&quot;</span> type=<span style="color: #ff0000;">&quot;text/javascript&quot;</span>&gt;&lt;/script&gt;
&lt;script src=<span style="color: #ff0000;">&quot;/extjs/ext/ext-all.js&quot;</span> type=<span style="color: #ff0000;">&quot;text/javascript&quot;</span>&gt;&lt;/script&gt;
&lt;link href=<span style="color: #ff0000;">&quot;/extjs/ext/resources/css/ext-all.css&quot;</span> rel=<span style="color: #ff0000;">&quot;stylesheet&quot;</span> type=<span style="color: #ff0000;">&quot;text/css&quot;</span> /&gt;
&lt;link href=<span style="color: #ff0000;">&quot;/extjs/ext/resources/css/xtheme-$config%7B&quot;</span> rel=<span style="color: #ff0000;">&quot;stylesheet&quot;</span> type=<span style="color: #ff0000;">&quot;text/css&quot;</span> /&gt;
EOF
<span style="color: #66cc66;">&#125;</span>
&nbsp;
<span style="color: #cc66cc;">1</span>;</pre></div></div>

<p>Here we pull in the Webmin core library, pull in the configuration for this module (which I&#8217;ll cover in a couple of days when I&#8217;ve completed the configuration code for this module), and build the function to return the bits of text we need to properly load ExtJS and its stylesheets.</p>
<p><strong>Using It</strong></p>
<p>Believe it or not, we&#8217;ve now got a library that can be used by other Webmin modules or by themes.  Webmin has a <em>foreign_require</em> function that will pull libraries like this in under their own namespace.  So, when I need to use ExtJS, I can do this:</p>

<div class="wp_syntax"><div class="code"><pre class="perl">foreign_require<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;extjs&quot;</span>, <span style="color: #ff0000;">&quot;extjs-lib.pl&quot;</span><span style="color: #66cc66;">&#41;</span>;
<span style="color: #000066;">print</span> extjs::<span style="color: #006600;">header_text</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;</pre></div></div>

<p>All done!  In a few days I&#8217;ll be finished with the first full-featured version of this library, and will wrap it up for distribution, along with some proof-of-concept modules that show how to use a full-featured AJAX interface without breaking text-mode browsers and readers, among other things.</p>
]]></content:encoded>
			<wfw:commentRss>http://inthebox.webmin.com/sharing-javascript-code-in-webmin/feed</wfw:commentRss>
		</item>
		<item>
		<title>One config file to rule them all</title>
		<link>http://inthebox.webmin.com/one-config-file-to-rule-them-all</link>
		<comments>http://inthebox.webmin.com/one-config-file-to-rule-them-all#comments</comments>
		<pubDate>Thu, 11 Oct 2007 01:29:08 +0000</pubDate>
		<dc:creator>Joe Cooper</dc:creator>
		
		<category><![CDATA[Development]]></category>

		<category><![CDATA[Features]]></category>

		<guid isPermaLink="false">http://inthebox.webmin.com/one-config-file-to-rule-them-all</guid>
		<description><![CDATA[Configuration files are a boring necessity in software development.  Parsing existing configuration files is a necessary aspect of almost any systems automation task.  I regularly need to read and write configuration files from different languages, as I have simple maintenance, startup, and installation scripts written in BASH, larger Webmin-related tools in Perl, and [...]]]></description>
			<content:encoded><![CDATA[<p>Configuration files are a boring necessity in software development.  Parsing existing configuration files is a necessary aspect of almost any systems automation task.  I regularly need to read and write configuration files from different languages, as I have simple maintenance, startup, and installation scripts written in BASH, larger Webmin-related tools in Perl, and stuff related to our website written in PHP.  Of course, there are some great configuration file parsers for Perl in CPAN, but if you need a highly portable script and you don&#8217;t want your user to have to know anything about CPAN, it makes sense to build your own.</p>
<p>Luckily, in all three of these languages, plus Ruby and Python (other favorites of mine), simple configuration files can be easy, if you choose the right format.</p>
<p><strong>Start from the Least Common Denominator</strong></p>
<p>The least capable language in this story, at least with regard to data structures, is probably BASH, so we&#8217;ll start by creating a configuration file that&#8217;s easy to use with BASH.  The obvious choice is a file filled with simple variable assignments, like so:</p>
<p><em>apache.config</em></p>

<div class="wp_syntax"><div class="code"><pre class="bash"><span style="color: #808080; font-style: italic;"># A comment</span>
<span style="color: #007800;">show_order=</span><span style="color: #000000;">0</span>
<span style="color: #007800;">start_cmd=</span>/etc/rc.d/init.d/httpd start
<span style="color: #007800;">mime_types=</span>/etc/mime.types
<span style="color: #007800;">apachectl_path=</span>/usr/sbin/apachectl
<span style="color: #007800;">stop_cmd=</span>/etc/rc.d/init.d/httpd stop
<span style="color: #007800;">emptyvalue=</span>
<span style="color: #808080; font-style: italic;"># A blank line too..</span>
&nbsp;
<span style="color: #007800;">max_servers=</span><span style="color: #000000;">100</span>
<span style="color: #007800;">test_config=</span><span style="color: #000000;">1</span>
<span style="color: #007800;">apply_cmd=</span>/etc/rc.d/init.d/httpd restart
<span style="color: #007800;">httpd_path=</span>/usr/sbin/httpd
<span style="color: #007800;">httpd_dir=</span>/etc/httpd
<span style="color: #808080; font-style: italic;">#  A comment with <span style="color: #007800;">an=</span>sign</span></pre></div></div>

<p>This file is valid BASH syntax&#8211;you could run this directly with <em>/bin/sh apache.config</em> and it would return no errors (though it wouldn&#8217;t do anything, because the values are not exported, so they are only in scope for the split second it takes BASH to parse the file.  Because it&#8217;s BASH syntax, empty lines are ignored, and any line that starts with a # is a comment and also ignored.  Empty values are also legal, so we need to accommodate lines that have only a key and no value.  Also because this is a valid BASH script, we can make use of these variables in our scripts easily by sourcing this file.  In shell scripts this is done using the dot operator ( . ), like so:</p>

<div class="wp_syntax"><div class="code"><pre class="bash">. apache.config</pre></div></div>

<p>After this, each of the values in the apache.config file are accessible by their names.  There are some caveats that make this a less than ideal practice for anything more complicated than a small script.  The variables pollute the namespace when pulled in this way.  So, if you later wanted to use <em>$apachectl_path</em> as a variable for some other purpose, for example, you would overwrite the existing assignment, and cause possibly difficult to diagnose errors.  BASH doesn&#8217;t have support for complex data structures, so there isn&#8217;t much we can do about this, without introducing quite a lot of complexity, so we&#8217;ll take our chances and keep our scripts short and simple.</p>
<p><strong>Getting the values into a Perl data structure</strong></p>
<p>While our configuration file is not valid Perl syntax, Perl still has plenty of tools for working with this kind of file.  After all, Perl was born to pick up the ball where shell scripts fumbled (and eventually evolved into a hodge podge of every great, and some not so great, ideas in programming languages from the past couple of decades), so it&#8217;s natural that it would have the ability to do the same sorts of things as a shell script.</p>
<p>But, since our configuration file is not valid Perl syntax, we can&#8217;t simply call <em>do apache.config;</em> as we would to import another Perl script.  We&#8217;ll have to parse it into a data structure (which is better programming practice, anyway, as mentioned above).  One way to do this would be a while loop, like so:</p>

<div class="wp_syntax"><div class="code"><pre class="perl"><span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$file</span> = <span style="color: #ff0000;">&quot;apache.config&quot;</span>;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">%config</span>;
<span style="color: #000066;">open</span><span style="color: #66cc66;">&#40;</span>CONFIG, <span style="color: #ff0000;">&quot;&amp;lt; $file&quot;</span><span style="color: #66cc66;">&#41;</span> <span style="color: #b1b100;">or</span> <span style="color: #000066;">die</span> <span style="color: #ff0000;">&quot;can't open $file: $!&quot;</span>;
<span style="color: #b1b100;">while</span> <span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
    <span style="color: #000066;">chomp</span>;
    <span style="color: #000066;">s</span>/<span style="color: #808080; font-style: italic;">#.*//; # Remove comments</span>
    s/^\s+//; <span style="color: #808080; font-style: italic;"># Remove opening whitespace</span>
    s/\s+$//;  <span style="color: #808080; font-style: italic;"># Remove closing whitespace</span>
    <span style="color: #b1b100;">next</span> <span style="color: #b1b100;">unless</span> <span style="color: #000066;">length</span>;
    <span style="color: #b1b100;">my</span> <span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$key</span>, <span style="color: #0000ff;">$value</span><span style="color: #66cc66;">&#41;</span> = <span style="color: #000066;">split</span><span style="color: #66cc66;">&#40;</span>/\<span style="color: #000066;">s</span>*=\<span style="color: #000066;">s</span>*/, <span style="color: #0000ff;">$_</span>, <span style="color: #cc66cc;">2</span><span style="color: #66cc66;">&#41;</span>;
    <span style="color: #0000ff;">$config</span><span style="color: #66cc66;">&#123;</span><span style="color: #0000ff;">$key</span><span style="color: #66cc66;">&#125;</span> = <span style="color: #0000ff;">$value</span>;
<span style="color: #66cc66;">&#125;</span>
&nbsp;
<span style="color: #808080; font-style: italic;"># Print it out</span>
<span style="color: #000000; font-weight: bold;">use</span> Data::<span style="color: #006600;">Dumper</span>;
<span style="color: #000066;">print</span> Dumper<span style="color: #66cc66;">&#40;</span>\<span style="color: #0000ff;">%config</span><span style="color: #66cc66;">&#41;</span>;</pre></div></div>

<p>Now, we can access the values in our configuration file from the <em>%config</em> hash, such as <em>$config{&#8217;apachectl_path&#8217;}</em>.  Another option, if you&#8217;re feeling particularly idiomatic, is to use <em>map</em>:</p>

<div class="wp_syntax"><div class="code"><pre class="perl"><span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$file</span> = <span style="color: #ff0000;">&quot;apache.config&quot;</span>;
<span style="color: #000066;">open</span><span style="color: #66cc66;">&#40;</span>CONFIG, <span style="color: #ff0000;">&quot;<span style="color: #000099; font-weight: bold;">\&</span>lt; $file&quot;</span><span style="color: #66cc66;">&#41;</span> <span style="color: #b1b100;">or</span> <span style="color: #000066;">die</span> <span style="color: #ff0000;">&quot;can't open $file: $!&quot;</span>;
<span style="color: #b1b100;">my</span> <span style="color: #0000ff;">%config</span> = <span style="color: #000066;">map</span> <span style="color: #66cc66;">&#123;</span>
      <span style="color: #000066;">s</span>/<span style="color: #808080; font-style: italic;">#.*//; # Remove comments</span>
      s/^\s+//; <span style="color: #808080; font-style: italic;"># Remove opening whitespace</span>
      s/\s+$//;  <span style="color: #808080; font-style: italic;"># Remove closing whitespace</span>
      m/<span style="color: #66cc66;">&#40;</span>.*?<span style="color: #66cc66;">&#41;</span>=<span style="color: #66cc66;">&#40;</span>.*<span style="color: #66cc66;">&#41;</span>/; <span style="color: #66cc66;">&#125;</span>
      ;
&nbsp;
<span style="color: #808080; font-style: italic;"># Print it out</span>
<span style="color: #000000; font-weight: bold;">use</span> Data::<span style="color: #006600;">Dumper</span>;
<span style="color: #000066;">print</span> Dumper<span style="color: #66cc66;">&#40;</span>\<span style="color: #0000ff;">%config</span><span style="color: #66cc66;">&#41;</span>;</pre></div></div>

<p>So, what&#8217;s the benefit to this latter example?  Nothing major, it&#8217;s just another way to approach the problem.  It&#8217;s a couple of lines shorter, but more importantly it has fewer temporary variables, which can be a source of errors in large programs.  The multiple substitution regular expressions I&#8217;ve shown above in either example could be reduced to a single line, but I believe this is more readable, and according to the Perl documentation breaking the tests out into single tests is faster than having multiple possible tests in a single substitution.  Some folks also find long regular expressions difficult to scan.</p>
<p><strong>But, I only like Ruby!</strong></p>
<p>OK, so you want to do it in Ruby.  Ruby has a lot in common with Perl, so it&#8217;s actually pretty similar, though a bit more verbose. Ruby fans seem to discourage regular expressions, though it is a core part of the language and it has roughly the same regex capabilities as Perl, so I&#8217;ve only used one (I guess I could have gotten rid of it somehow&#8230;but I got tired of searching for the non-regex answer and punted):</p>

<div class="wp_syntax"><div class="code"><pre class="ruby">config = <span style="color:#006600; font-weight:bold;">&#123;</span><span style="color:#006600; font-weight:bold;">&#125;</span>
&nbsp;
<span style="color:#CC00FF; font-weight:bold;">File</span>.<span style="color:#9900CC;">foreach</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;apache.config&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span> |line|
  line.<span style="color:#9900CC;">strip</span>!
  <span style="color:#008000; font-style:italic;"># Skip comments and whitespace</span>
  <span style="color:#9966CC; font-weight:bold;">if</span> <span style="color:#006600; font-weight:bold;">&#40;</span>line<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#006666;">0</span><span style="color:#006600; font-weight:bold;">&#93;</span> != ?<span style="color:#008000; font-style:italic;"># and line =~ /\S/ )</span>
    i = line.<span style="color:#9900CC;">index</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'='</span><span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#9966CC; font-weight:bold;">if</span> <span style="color:#006600; font-weight:bold;">&#40;</span>i<span style="color:#006600; font-weight:bold;">&#41;</span>
      config<span style="color:#006600; font-weight:bold;">&#91;</span>line<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#006666;">0</span>..<span style="color:#9900CC;">i</span> - <span style="color:#006666;">1</span><span style="color:#006600; font-weight:bold;">&#93;</span>.<span style="color:#9900CC;">strip</span><span style="color:#006600; font-weight:bold;">&#93;</span> = line<span style="color:#006600; font-weight:bold;">&#91;</span>i + <span style="color:#006666;">1</span>..<span style="color:#006666;">-1</span><span style="color:#006600; font-weight:bold;">&#93;</span>.<span style="color:#9900CC;">strip</span>
    <span style="color:#9966CC; font-weight:bold;">else</span>
      config<span style="color:#006600; font-weight:bold;">&#91;</span>line<span style="color:#006600; font-weight:bold;">&#93;</span> = <span style="color:#996600;">''</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Print it out</span>
config.<span style="color:#9900CC;">each</span> <span style="color:#9966CC; font-weight:bold;">do</span> |key, value|
  <span style="color:#CC0066; font-weight:bold;">print</span> key + <span style="color:#996600;">&quot; = &quot;</span> + value
  <span style="color:#CC0066; font-weight:bold;">print</span> <span style="color:#996600;">&quot;<span style="color:#000099;">\n</span>&quot;</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>Same end result as the Perl versions above: A config hash containing all of the elements in our configuration file.</p>
<p><strong>What about those web applications written in PHP?</strong></p>
<p>Two of the websites I maintain (Virtualmin.com, and this site) are written in PHP.  One is a Joomla application with numerous extensions and custom modules and components, the other is a mildly customized Wordpress site.  In the case of Virtualmin.com, we&#8217;re developing a number of applications that have both Perl components for the back end work and PHP components for the web front end, so sharing configuration files can be useful.  Webmin, conveniently enough, already uses shell variable key=value style configuration files, so everything we do is already in this format.</p>
<p>So, let&#8217;s see about getting these configuration files into a PHP data structure.  PHP isn&#8217;t quite as rich as Perl in its data manipulation capabilities, but it did inherit quite a few of the same tools from Perl, so our solution in PHP looks pretty similar to the while loop version above, though it is a bit more verbose due to the keyword heavy nature of PHP (Perl is often accused of having too much syntax, and PHP has way too many keywords):</p>

<div class="wp_syntax"><div class="code"><pre class="php"><span style="color: #0000ff;">$file</span>=<span style="color: #ff0000;">&quot;apache.config&quot;</span>;
<span style="color: #0000ff;">$lines</span> = <span style="color: #000066;">file</span><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$file</span><span style="color: #66cc66;">&#41;</span>;
<span style="color: #0000ff;">$config</span> = <span style="color: #000066;">array</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;
&nbsp;
<span style="color: #b1b100;">foreach</span> <span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$lines</span> <span style="color: #b1b100;">as</span> <span style="color: #0000ff;">$line_num</span>=&amp;gt;<span style="color: #0000ff;">$line</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
  <span style="color: #808080; font-style: italic;"># Comment?</span>
  <span style="color: #b1b100;">if</span> <span style="color: #66cc66;">&#40;</span> ! <span style="color: #000066;">preg_match</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;/#.*/&quot;</span>, <span style="color: #0000ff;">$line</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
    <span style="color: #808080; font-style: italic;"># Contains non-whitespace?</span>
    <span style="color: #b1b100;">if</span> <span style="color: #66cc66;">&#40;</span> <span style="color: #000066;">preg_match</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;/<span style="color: #000099; font-weight: bold;">\S</span>/&quot;</span>, <span style="color: #0000ff;">$line</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span>
      <span style="color: #000066;">list</span><span style="color: #66cc66;">&#40;</span> <span style="color: #0000ff;">$key</span>, <span style="color: #0000ff;">$value</span> <span style="color: #66cc66;">&#41;</span> = <span style="color: #000066;">explode</span><span style="color: #66cc66;">&#40;</span> <span style="color: #ff0000;">&quot;=&quot;</span>, <span style="color: #000066;">trim</span><span style="color: #66cc66;">&#40;</span> <span style="color: #0000ff;">$line</span> <span style="color: #66cc66;">&#41;</span>, <span style="color: #cc66cc;">2</span><span style="color: #66cc66;">&#41;</span>;
      <span style="color: #0000ff;">$config</span><span style="color: #66cc66;">&#91;</span><span style="color: #0000ff;">$key</span><span style="color: #66cc66;">&#93;</span> = <span style="color: #0000ff;">$value</span>;
    <span style="color: #66cc66;">&#125;</span>
  <span style="color: #66cc66;">&#125;</span>
<span style="color: #66cc66;">&#125;</span>
&nbsp;
<span style="color: #808080; font-style: italic;">// Print it out</span>
<span style="color: #000066;">print_r</span><span style="color: #66cc66;">&#40;</span><span style="color: #0000ff;">$config</span><span style="color: #66cc66;">&#41;</span>;</pre></div></div>

<p><strong>Hey, what about snake handlers?</strong></p>
<p>Of course, it can also be done in Python.  As with the Ruby implementation, I&#8217;m not certain this is the best way to do it, but it works on my test file.</p>

<div class="wp_syntax"><div class="code"><pre class="python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
config = <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
&nbsp;
file_name = <span style="color: #483d8b;">&quot;apache.config&quot;</span>
config_file = <span style="color: #008000;">open</span><span style="color: black;">&#40;</span>file_name, <span style="color: #483d8b;">'r'</span><span style="color: black;">&#41;</span>
<span style="color: #ff7700;font-weight:bold;">for</span> line <span style="color: #ff7700;font-weight:bold;">in</span> config_file:
    <span style="color: #808080; font-style: italic;"># Get rid of \n</span>
    line = line.<span style="color: black;">rstrip</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #808080; font-style: italic;"># Empty?</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> line:
        <span style="color: #ff7700;font-weight:bold;">continue</span>
    <span style="color: #808080; font-style: italic;"># Comment?</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> line.<span style="color: black;">startswith</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;#&quot;</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">continue</span>
    <span style="color: black;">&#40;</span>name, value<span style="color: black;">&#41;</span> = line.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;=&quot;</span><span style="color: black;">&#41;</span>
    name = name.<span style="color: black;">strip</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    config<span style="color: black;">&#91;</span>name<span style="color: black;">&#93;</span> = value
&nbsp;
<span style="color: #ff7700;font-weight:bold;">print</span> config</pre></div></div>

<p>Or, as dysmas suggested on Reddit, a more idiomatic version would be:</p>

<div class="wp_syntax"><div class="code"><pre class="python">config = <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span>
&nbsp;
file_name = <span style="color: #483d8b;">&quot;apache.config&quot;</span>
config_file= <span style="color: #008000;">open</span><span style="color: black;">&#40;</span>file_name<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">for</span> line <span style="color: #ff7700;font-weight:bold;">in</span> config_file:
    line = line.<span style="color: black;">strip</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> line <span style="color: #ff7700;font-weight:bold;">and</span> line<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span> <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #483d8b;">&quot;#&quot;</span> <span style="color: #ff7700;font-weight:bold;">and</span> line<span style="color: black;">&#91;</span><span style="color: #ff4500;">-1</span><span style="color: black;">&#93;</span> <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #483d8b;">&quot;=&quot;</span>:
        var,val = line.<span style="color: black;">rsplit</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;=&quot;</span>,<span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>
        config<span style="color: black;">&#91;</span>var.<span style="color: black;">strip</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span> = val.<span style="color: black;">strip</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">print</span> config</pre></div></div>

<p>So, now we&#8217;ve got a config associative array filled with all of our values in all of our favorite languages (except BASH, which gets straight variables).  Assuming we use a common file locking mechanism, or always open them read only, we could even begin to use the same configuration files across our BASH, Perl, Ruby, Python, and PHP scripts independently but simultaneously.</p>
<p><strong>What&#8217;s the point?</strong></p>
<p>This isn&#8217;t just an academic exercise.  The simple examples above make up the early start of a cross-language set of tools for systems management.</p>
<p>With these simple parsers, we can build tools that use the best language for the job, while still leveraging some interesting knowledge contained in Webmin&#8217;s configuration files (which are in this key=value format).  Webmin supports dozens of Operating Systems and hundreds of services and configuration files, so the config files in a Webmin installation (usually found in /etc/webmin) contain a huge array of compatibility information that would take ages to gather.  If you need to know how to stop or start Apache on Debian 4.0, or on Solaris, or on Red Hat Enterprise Linux, you&#8217;d have to check an installation of those systems or search the web or ask someone who has one of those systems handy.  Or, you could check the Webmin configuration file, and get the same data for all of the Operating Systems Webmin supports.  It&#8217;s a pretty valuable pile of data.  Imagine writing a script for your own favorite OS, and then being able to hand it to anyone that happens to have Webmin installed, regardless of their OS and version.  Or, if they don&#8217;t have Webmin installed, you could provide a template configuration file that they could fix for their OS and version, addressing both situations as simply as possible.</p>
<p><strong>Not the only configuration file format</strong></p>
<p>Of course, this isn&#8217;t the only configuration file format out there, or even the best.  Python users really like INI files, and I can&#8217;t argue with them.  When I was writing Perl and Python predominantly, I used the Config::INI::Simple module from CPAN and ConfigParser for Python so I could share configuration between my various software easily (I was generally writing a Webmin front end in Perl to a Python back end application).  That worked great.  So, I&#8217;m not arguing you ought to be using key=value configuration files for everything.  But being able to read them makes a lot of portability data available to you for free.</p>
<p>Next time I&#8217;ll wrap a couple of these routines up into friendly libraries for easy use, and add some tests to be sure we&#8217;re doing what we think we&#8217;re doing.</p>
]]></content:encoded>
			<wfw:commentRss>http://inthebox.webmin.com/one-config-file-to-rule-them-all/feed</wfw:commentRss>
		</item>
		<item>
		<title>The new face of Webmin</title>
		<link>http://inthebox.webmin.com/the-new-face-of-webmin</link>
		<comments>http://inthebox.webmin.com/the-new-face-of-webmin#comments</comments>
		<pubDate>Tue, 09 Oct 2007 08:47:31 +0000</pubDate>
		<dc:creator>Joe Cooper</dc:creator>
		
		<category><![CDATA[Business]]></category>

		<category><![CDATA[Design]]></category>

		<category><![CDATA[Snippets]]></category>

		<guid isPermaLink="false">http://inthebox.webmin.com/the-new-face-of-webmin</guid>
		<description><![CDATA[After much deliberation (a bloodbath!), Jamie, Kevin, and I (and a little help from some friends) have chosen a new logo for Webmin, from the seemingly infinite great options offered up by our recent logo contest at SitePoint.  Without further ado, I present to you the new face of Webmin:

 The figure represents the [...]]]></description>
			<content:encoded><![CDATA[<p>After much deliberation (a bloodbath!), Jamie, Kevin, and I (and a little help from some friends) have chosen a new logo for Webmin, from the seemingly infinite great options offered up by our recent <a href="http://contests.sitepoint.com/contests/3497">logo contest at SitePoint</a>.  Without further ado, I present to you the new face of Webmin:</p>
<p style="text-align: center"><img src="/images/new-webmin-logo.png" height="420" width="324" /></p>
<p> The figure represents the letters W and M, but there is also the additional symbolism of three Ms to represent Webmin, Usermin, and Virtualmin.  We agonized quite a bit over leaving behind the spider web themed branding of the old logo, but in the end, decided that a web was simply meaningless in a modern context where <em>everything</em> is web-based.  When Webmin began, <em>almost nothing</em> for system administration ran on the web, and it was the defining characteristic of Webmin.  Jamie is still justifiably proud of being so far ahead of the curve on adopting the web-based paradigm, but felt it was time to move on.  The future of Webmin is virtualization and grid computing, mobile devices, public APIs, clustering, and more.  The web-based UI is merely one aspect of a large swath of interesting facets.</p>
<p>We&#8217;d like to thank all of the designers who took part in the contest.  They were patient, enthusiastic, and <em>really good</em>.  We came into the final round of judging with a dozen or more entries that at least two of us loved, and only after two days of debate and seeking advice from friends and family, did we finally come to a concensus on one that we could all love.  We&#8217;re extremely excited about the new logo, and plan to roll it out to the website, and the default Webmin theme in the next few days.  We&#8217;ll also be printing some T-shirts, as soon as I find someone good here in the Bay Area to make them for us.</p>
<p>Thanks also to Kevin Hale, of <a href="http://www.particletree.com">Particle Tree</a> and <a href="http://www.wufoo.com">Wufoo</a> fame for being our celebrity judge and providing adult supervision during the contest.  His magical designer-y ways kept us thoroughly on the right path.  Jamie&#8217;s sister <a href="http://laracameron.com/">Lara Cameron</a><br />
also loaned us her eyes and expertise.</p>
<p><em>See also</em></p>
<p><a href="http://inthebox.webmin.com/webmin-logo-contest">Webmin Logo Contest </a></p>
<p><a href="http://inthebox.webmin.com/getting-a-great-logo-reducing-the-field">Getting a great logo: Reducing the field</a></p>
]]></content:encoded>
			<wfw:commentRss>http://inthebox.webmin.com/the-new-face-of-webmin/feed</wfw:commentRss>
		</item>
		<item>
		<title>It&#8217;s just not a contest until you see a goatse</title>
		<link>http://inthebox.webmin.com/its-just-not-a-contest-until-you-see-a-goatse</link>
		<comments>http://inthebox.webmin.com/its-just-not-a-contest-until-you-see-a-goatse#comments</comments>
		<pubDate>Sat, 06 Oct 2007 23:19:47 +0000</pubDate>
		<dc:creator>Joe Cooper</dc:creator>
		
		<category><![CDATA[Business]]></category>

		<category><![CDATA[Design]]></category>

		<category><![CDATA[Snippets]]></category>

		<guid isPermaLink="false">http://inthebox.webmin.com/its-just-not-a-contest-until-you-see-a-goatse</guid>
		<description><![CDATA[As I&#8217;ve mentioned here and here, we&#8217;re holding a logo contest for Webmin&#8217;s tenth anniversary.  We&#8217;ve gotten a ton of fantastic entries, and we&#8217;re coming down to the final hours of the contest.  We are feeling really good about quite a few of the entries, but today the entries finally achieved what all [...]]]></description>
			<content:encoded><![CDATA[<p>As I&#8217;ve mentioned <a href="http://inthebox.webmin.com/webmin-logo-contest">here</a> and <a href="http://inthebox.webmin.com/getting-a-great-logo-reducing-the-field">here</a>, we&#8217;re holding a logo contest for Webmin&#8217;s tenth anniversary.  We&#8217;ve gotten a ton of fantastic entries, and we&#8217;re coming down to the final hours of the contest.  We are feeling really good about quite a few of the entries, but today the entries finally achieved what all great contests should aspire to: an unintentional goatse troll.</p>
<p>The finest goatse logo troll of all time, of course, appeared during the <a href="http://www.glumbert.com/media/bbcgoatse">BBC Olympic &#8220;Lisa Simpson doing something naughty&#8221; logo coverage</a> (pay attention at about :29 on the clock).  But, now we&#8217;ve got one of our very own:</p>
<p style="text-align: center" align="center"><img src="/images/214692.medium.png" height="300" width="300" /></p>
<p>I&#8217;m so proud.</p>
<p><em>See also: <a href="http://www.theregister.co.uk/2007/06/04/bbc_olympics_cx/">Article at the Register</a></em></p>
]]></content:encoded>
			<wfw:commentRss>http://inthebox.webmin.com/its-just-not-a-contest-until-you-see-a-goatse/feed</wfw:commentRss>
		</item>
		<item>
		<title>Getting a great logo: reducing the field</title>
		<link>http://inthebox.webmin.com/getting-a-great-logo-reducing-the-field</link>
		<comments>http://inthebox.webmin.com/getting-a-great-logo-reducing-the-field#comments</comments>
		<pubDate>Sat, 06 Oct 2007 00:30:54 +0000</pubDate>
		<dc:creator>Joe Cooper</dc:creator>
		
		<category><![CDATA[Business]]></category>

		<category><![CDATA[Design]]></category>

		<category><![CDATA[Features]]></category>

		<guid isPermaLink="false">http://inthebox.webmin.com/getting-a-great-logo-reducing-the-field</guid>
		<description><![CDATA[
We&#8217;re holding a logo contest over on SitePoint.  We mentioned it in an article a few days ago and since then it has become the most popular contest running right now on the site!  Awesome.  That&#8217;s the good news.  There&#8217;s also great news: The designers entering the contest are really good! [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: center"><img src="http://inthebox.webmin.com/images/nextlogo.png" height="285" width="304" /></p>
<p>We&#8217;re holding a <a href="http://contests.sitepoint.com/contests/3497">logo contest over on SitePoint</a>.  We mentioned it in an <a href="http://inthebox.webmin.com/webmin-logo-contest">article a few days ago</a> and since then it has become the most popular contest running right now on the site!  Awesome.  That&#8217;s the good news.  There&#8217;s also great news: The designers entering the contest are really good!  There&#8217;s at least half a dozen designs that we&#8217;d be proud to call our logo, and at least a dozen of the designers are folks I would love to work with in the future. Hundreds of entries would do us no good if they all sucked, but these guys are doing really solid work.</p>
<p>We&#8217;re about 30 hours from the end of the contest, so I wanted to post a summary of the work so far, and offer some advice to the designers, as well as offer up our thought process on why we like the logos that we like, and a few for logos we want to like, but don&#8217;t, and why.  This is, by no means, an exhaustive list.  For that you&#8217;ll need to check out the contest itself and the feedback on each of the entries.</p>
<p><strong>Our Judging Guidelines</strong></p>
<p>These are the guiding principles in our decision making process.  We don&#8217;t all agree on how they should be reflected in the end product, but we all agree that these are right for the Webmin logo.  It helps to know, in advance, the general feel of the branding you want, as it makes it really easy to rule out some possibly great executions of ideas that don&#8217;t fit.  I think this is one of the leading causes of a failed branding effort; if you don&#8217;t know what you want, you&#8217;ll almost certainly not get it.  So here&#8217;s the guidelines we&#8217;re using in our judging and advice to the designers:</p>
<ul>
<li>We&#8217;re an Open Source project, so super corporate looking logos probably  aren&#8217;t right.</li>
<li>We&#8217;ll be printing T-shirts, so too much complexity is a negative.  Costs  more to print, and looks stupid when screen-printed.  It also leads to a  weaker brand image&#8230;takes a long time to remember a really complex  logo, but a simple one can stick on first or second viewing.</li>
<li>Colors aren&#8217;t set in stone.  We&#8217;ll have the SVG vector version, and can  change the colors, as needed.  Though poor color choices might indicate  a lack of skill on the part of the designer, and we might be wasting our  time trying to guide them towards perfection if the logo has problems  other than color.  I&#8217;ve noticed some of the designers take advice much  better than others.  Some of these folks are pros, while others are  well-meaning amateurs, and one of the things that separates the pros from the amateurs is an uncanny ability to read my mind.  We aren&#8217;t going to miss out on a great logo just because it is by an amateur, but we&#8217;re also going to choose a perfect execution of a good idea over a mediocre execution of a great idea.</li>
<li>Be gentle, and have fun.  We&#8217;re encouraging everyone to get involved, so  we&#8217;ve got a few entries that are, frankly, not great.  You can be harder on an entry that  you really like a lot, because it&#8217;s easy to soften the criticism with  praise.  But, if one of us picks something that another hates, be gentle  in vetoing it.</li>
<li>Jamie has veto power (we all do, but Jamie really does).  It&#8217;s his baby, and he gets to say no to any logo, no matter how much one of the other judges loves something.</li>
</ul>
<p><strong>The Top Ten (give or take a few)</strong></p>
<p>This is a bunch of logos that Jamie or I loved.  Kevin is reserving judgment until the end, so we&#8217;ll have to wait for the professional opinions, but here&#8217;s where we stand, right now.  This is definitely not an exhaustive list of the good to great logos in the contest, but it&#8217;s the ones that we picked out as being our favorites.  Some of these won&#8217;t actually be going into the final round, due to a veto by Jamie or I, but these are all great by either my estimation or Jamie&#8217;s, so they&#8217;re worth commenting on.</p>
<p><strong><em>Modern stylized spider web by vjeko</em></strong></p>
<p style="text-align: center"><img src="http://contests.sitepoint.com/contests/3497/entries/206141.medium.png" height="297" width="300" /></p>
<p><a href="http://contests.sitepoint.com/contests/3497/entrants/206414#entry206141" class="moz-txt-link-freetext">http://contests.sitepoint.com/contests/3497/entrants/206414#entry206141</a></p>
<p>I like everything about this one.  The spider web is clearly a spider  web, it feels kinda like the Pentagon of spider webs: a place where  Serious Internet Business takes place.  The font is fun and the colors  are soothing and modern.  It scales small and large with no loss of  impact and handles limited colors like a champ. Jamie also likes this logo.  He&#8217;s unsure of hanging on to the spider or spider web branding of the old Webmin logo, so many of my favorites are in limbo (most of my favorites are spider related).  But the strength of this logo won Jamie over, and he&#8217;d be happy with this concept.</p>
<p>vjeko deserves special mention for poking fun at me with this variant  that adds a fitting tagline:</p>
<p style="text-align: center"><img src="http://contests.sitepoint.com/contests/3497/entries/207627.medium.png" height="120" width="300" /></p>
<p><a href="http://contests.sitepoint.com/contests/3497/entrants/206414#entry207627" class="moz-txt-link-freetext">http://contests.sitepoint.com/contests/3497/entrants/206414#entry207627</a></p>
<p><strong><em>Fat, friendly spider by Haetro</em></strong></p>
<p style="text-align: center"><img src="http://contests.sitepoint.com/contests/3497/entries/205608.medium.jpg" height="177" width="300" /></p>
<p><a href="http://contests.sitepoint.com/contests/3497/entrants/181135#entry205608" class="moz-txt-link-freetext">http://contests.sitepoint.com/contests/3497/entrants/181135#entry205608</a></p>
<p>I love this spider!  Every time I see it, I like it more.  It&#8217;s got real  personality, and with only one color.  It looks great in any color, and  even with fonts I don&#8217;t care for, like the one in this particular  instance.  Some of the other variants of this logo have better fonts,  but miss out on the purity of this one.  I like the single color, and I  like the square spider icon better than the later instances that round  the spider or add more colors to the Webmin text (though other instances  do have better fonts).  Haetro has a great sense of style and a  minimalist approach that I find very appealing.</p>
<p>Unfortunately for me, and for this design, Jamie vetoed it.  The white space is pretty deeply offensive to him, and when scaled up he finds the spider frightening (I can see that&#8230;the eyes get really scary when he&#8217;s big).  That said, Haetro is among the best designers in this contest, and I hate that none of his entries will make it to the final round.  So, an interesting lesson to learn from this is that perhaps some of the most compelling images can also be the most off-putting.  I asked around about this one, and it&#8217;s a very polarizing logo, you either love it or hate it.</p>
<p><strong><em>Solid Webmin surrounding racks full of servers by RetroMetro Designs/Steve</em></strong></p>
<p style="text-align: center"><img src="http://contests.sitepoint.com/contests/3497/entries/208658.medium.png" height="229" width="300" /></p>
<p><a href="http://contests.sitepoint.com/contests/3497/entrants/163719#entry208658" class="moz-txt-link-freetext">http://contests.sitepoint.com/contests/3497/entrants/163719#entry208658</a></p>
<p>This one is out of left field, and that&#8217;s a big part of why I like it.  It&#8217;s unlike any other entry, so far, and gets bonus points for that  originality.  The feel I get from the green blocks in the center is  clearly &#8220;look at these modern server racks filled with systems doing  Serious Internet Business&#8221;.  And the big fat WEBMIN sitting on either  side makes it real clear who&#8217;s in charge.  It&#8217;s simple, clean, clearly  relevant, reduces nicely, and looks good.  Steve&#8217;s entry prior to this  one is really nice, too, and in fact, Jamie likes it  better.  Steve has done some revisions of that idea, which Jamie and I both like better, so it may find its way into the final round.</p>
<p>Interestingly, while Jamie and I both like Steve&#8217;s sense of style, we diverged on which of his designs we like best.  But, at least one of Steve&#8217;s entries will be in the final round.</p>
<p><strong><em>Give me a &#8220;W&#8221;, Give me an &#8220;M&#8221;, What does that spell?  Spider! by  highendprofile</em></strong></p>
<p style="text-align: center"><img src="http://contests.sitepoint.com/contests/3497/entries/205913.medium.gif" height="219" width="300" /></p>
<p><a href="http://contests.sitepoint.com/contests/3497/entrants/195360#entry205913" class="moz-txt-link-freetext">http://contests.sitepoint.com/contests/3497/entrants/195360#entry205913</a></p>
<p>Awesome execution on the idea of a spider built from the letters W and  M.  This is a gorgeous illustration.  I wasn&#8217;t so sure about  highendprofile&#8217;s skills based on his first entry, which was a nice idea  but not very well executed, but this spider immediately blew me away.  Beautiful execution and the spider is among the best illustrations in  the contest.  I don&#8217;t love the font on this one&#8211;it&#8217;s a bit tall and  thin, but the colors are nice, and the spider is what draws the eye, so  even with a not quite right font, I really like this logo.</p>
<p>This is another of the spider-based logos that has gotten the axe by Jamie.  In this case, the cuteness that I love is a turnoff for Jamie.  It won&#8217;t, unfortunately, make it into the final round, but highendprofile is a great designer, and I wish we had another idea or two from him in the contest.</p>
<p><strong><em>Ooh, shiny spider makes me happy, by demonhale</em></strong></p>
<p style="text-align: center"><img src="http://contests.sitepoint.com/contests/3497/entries/205579.medium.gif" height="150" width="300" /></p>
<p><a href="http://contests.sitepoint.com/contests/3497/entrants/108155#entry205579" class="moz-txt-link-freetext">http://contests.sitepoint.com/contests/3497/entrants/108155#entry205579</a></p>
<p>What a champ.  Give demonhale an idea and he runs it all the way in.  This is a great spider illustration.  Cute and shiny, very modern, very  friendly.  The font looks spidery, and the whole thing just screams &#8220;New  Technology!&#8221;  Great color choice, but color isn&#8217;t necessary for this one to  look good.  I like that the spider is hanging by a thread&#8230;perhaps  going some place new.  And, who doesn&#8217;t love shiny things?</p>
<p>Jamie, surprisingly, did not veto this spider.  It&#8217;s shiny and serious enough to pass the &#8220;is it too cute?&#8221; muster, and it&#8217;s also a really simple design.  The colors are subdued and the execution is clean.   So, shiny spider is going to the final round.</p>
<p><strong><em>Webmin is like a box or a building block, by joswan</em></strong></p>
<p style="text-align: center"><img src="http://contests.sitepoint.com/contests/3497/entries/207368.medium.png" height="250" width="300" /></p>
<p><a href="http://contests.sitepoint.com/contests/3497/entrants/178126#entry207368" class="moz-txt-link-freetext">http://contests.sitepoint.com/contests/3497/entrants/178126#entry207368</a></p>
<p>This is another nice idea, that breaks from the old spider web and  spider tradition.  A box built from the letters W and M, with a nice  solid font and cool colors.  It&#8217;s quite pleasant to look at, and has  some relevance for what Webmin does.  Boxes don&#8217;t have a lot of  personality&#8230;but it looks good nonetheless.  It degrades nicely, and  makes for a good favicon and icon version.</p>
<p>Jamie doesn&#8217;t love the colors here, and I have to agree.  Orange and blue have a feel that is distinctly non-high tech.  But joswan is an excellent designer, and does really nice work, so we can probably chase him into getting the colors right.</p>
<p><strong><