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

<channel>
	<title>serialized.net &#187; Tech</title>
	<atom:link href="http://serialized.net/category/tech/feed/" rel="self" type="application/rss+xml" />
	<link>http://serialized.net</link>
	<description>An ongoing expression of fascination burnout</description>
	<lastBuildDate>Tue, 15 Jun 2010 05:31:57 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Capturing short-lived programs on Linux</title>
		<link>http://serialized.net/2010/06/capturing-short-lived-programs-on-linux/</link>
		<comments>http://serialized.net/2010/06/capturing-short-lived-programs-on-linux/#comments</comments>
		<pubDate>Tue, 15 Jun 2010 05:31:57 +0000</pubDate>
		<dc:creator>Joshua Barratt</dc:creator>
				<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://serialized.net/?p=389</guid>
		<description><![CDATA[One of the things I love most about DTrace is your ability to do things like this. Because you can ask the kernel to let you start tracking when something cool happens (like forking a new program), you can instrument it. // Measure parent fork time syscall::*fork*:entry { /* save start of fork */ self-&#62;fork [...]]]></description>
			<content:encoded><![CDATA[<p>One of the things I love most about DTrace is your <a href="http://www.brendangregg.com/DTrace/shortlived.d">ability to do things like this.</a> Because you can ask the kernel to let you start tracking when something cool happens (like forking a new program), you can instrument it.</p>
<pre class="brush: plain; light: true;">
// Measure parent fork time
syscall::*fork*:entry { /* save start of fork */
	self-&gt;fork = vtimestamp;
}
</pre>
<p>Sadly, those tools aren&#8217;t available on a standard issue Linux machine. (You can do it with <a href="http://sourceware.org/systemtap/wiki/systemtapstarters">SystemTap</a>, but it&#8217;s not on every server I end up looking at.) Today I was trying to track down some processes that were making very odd DNS lookups. I isolated the user ID making these calls via iptables logging:</p>
<pre class="brush: plain; light: true;">
iptables -I OUTPUT 1 -m string --string &quot;BADZONE&quot; -d 127.0.0.1 -p udp --destination-port 53 --algo bm -j LOG --log-uid --log-prefix &quot;BADZONE: &quot;
</pre>
<p>But this user&#8217;s PHP scripts were getting launched and dying again quickly. How to find out what was going on inside them? This hacky little trick actually worked really well. You can download it from <a href="http://axis.serialized.net/gitweb/?p=utilities.git;a=blob_plain;f=auto_stracer;hb=HEAD">my git utilities directory.</a></p>
<p>Basically, we check the process list as fast as we can for any owned by that user; If you find one, stick an strace on it. The script will &#8216;hang around&#8217; until all the strace processes have finished. You end up with a directory full of trace files which you can then post-grep through to get what you need.</p>
<p>The short version is I was able to catch one of that user&#8217;s programs doing the odd behaviour within about 30 seconds of running this tool, which was great!</p>
<pre class="brush: perl; light: true;">
#!/usr/bin/perl

use warnings;
use strict;
use Getopt::Long;

my $opt = {};
GetOptions($opt, &quot;uid=i&quot;, &quot;number=i&quot;, &quot;help&quot;, &quot;man&quot;);

if($opt-&gt;{help} || $opt-&gt;{man} || !defined($opt-&gt;{uid})) {
    print &quot;Usage: auto_stracer --number &lt;processes to capture&gt; --uid &lt;numeric uid&gt;\n&quot;;
    exit;
}
$opt-&gt;{number} ||= 5;

my $traced = 0;
my %seen = ();
my @pids = ();
my %ourpid = ();
$ourpid{$$}++;

$SIG{INT} = sub { kill 9, @pids; print &quot;Interrupt: Killed all running strace processes and quitting.\n&quot;; exit;};

while ($traced &lt; $opt-&gt;{number}) {
    for my $line (split(/\n/, `ps -Ao 'uid,pid'`)) {
        $line =~ s/^\s+//g; # eat leading spaces
        my($uid, $pid) = split(/\s+/, $line);
        next if($ourpid{$pid}); # don't run on something we are tracking
        if($pid &amp;&amp; $uid eq $opt-&gt;{uid} &amp;&amp; !$seen{$pid}) {
            $seen{$pid}++;
            my $st_pid = do_strace($pid);
            $ourpid{$st_pid}++;
            push(@pids, $st_pid);
            $traced++;
            last if ($traced &gt;= $opt-&gt;{number});
        }

    }
}

print &quot;Traced $opt-&gt;{number}, waiting for all to finish\n&quot;;
my $kid = 0;
do {
      $kid = waitpid(-1, 0);
} while($kid&gt;0);
print &quot;All processes completed, exiting.\n&quot;;
exit;

sub do_strace {
    my($pid) = @_;
    my $ourpid = fork();
    return $ourpid if($ourpid != 0);
    my $cmd = &quot;strace -p $pid -f -s 65535 -o trace.$pid.$$ -v&quot;;
    print &quot;\tcmd: $cmd\n&quot;;
    system($cmd);
    exit;
}
</pre>
<p>So, very brute force compared to the elegance of systemtap or DTrace, but when you need it, it&#8217;s still handy.</p>
]]></content:encoded>
			<wfw:commentRss>http://serialized.net/2010/06/capturing-short-lived-programs-on-linux/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Optimal web-sized identifiers</title>
		<link>http://serialized.net/2010/05/optimal-web-sized-identifiers/</link>
		<comments>http://serialized.net/2010/05/optimal-web-sized-identifiers/#comments</comments>
		<pubDate>Sat, 15 May 2010 06:59:34 +0000</pubDate>
		<dc:creator>Joshua Barratt</dc:creator>
				<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://serialized.net/?p=375</guid>
		<description><![CDATA[As part of a project I&#8217;m working on for fun (shhhh) I&#8217;ve been trying to solve an interesting problem &#8212; what is the most compact way I can uniquely refer to something in a URI? This one&#8217;s going to get complicated, so here&#8217;s the tl;dr version. To get the shortest possible, web-safe identifiers: encode identifiers [...]]]></description>
			<content:encoded><![CDATA[<p>As part of a project I&#8217;m working on for fun (shhhh) I&#8217;ve been trying to solve an interesting problem &#8212; what is the most compact way I can uniquely refer to something in a URI?</p>
<p>This one&#8217;s going to get complicated, so here&#8217;s the tl;dr version. To get the shortest possible, web-safe identifiers:</p>
<ul>
<li>encode identifiers with base62 or a customized version of base64 that uses valid characters.</li>
<li>Use a big enough hash space to get an acceptable level of collisions.</li>
</ul>
<p>Ok. Here&#8217;s the long-winded version.</p>
<p>It&#8217;s a little like the &#8220;URL Shortener&#8221; use case, where you want to take a big fat link and describe it in a tiny link. In this case, I&#8217;m converting individual sentences into a tiny, linkable representation.</p>
<p>So, I need a very short identifier that obeys certain (conflicting) properties:</p>
<ul>
<li>As much as possible, the identifier should be the same regardless of the order that I create the sentences in</li>
<li>It should be as short as possible, because I may need to refer to a lot of these in a single URI</li>
</ul>
<p>In the URL shortener mode, they tend to solve this by just keeping a global &#8220;ID&#8221; counter and incrementing it every time someone creates a new link. I need to use something a lot more like a hash. (MD5 or SHA1 are typical choices here.)</p>
<p>So what are the constraints?</p>
<ul>
<li>How long can a URI be?</li>
<li>What characters are we allowed to use?</li>
<li>How much unique data is going to be represented?</li>
<li>How tolerant can we be of collisions? (When 2 &#8220;objects&#8221; might map to the same shortened identifier?)</li>
</ul>
<h3>URI constraints</h3>
<p>The first 2 are simple and global. There&#8217;s no real standard on URI length, but <a href="http://www.boutell.com/newfaq/misc/urllength.html ">smart people have done the legwork for us</a> so we&#8217;ll steal their conclusions, and say &#8220;keep URI&#8217;s shorter than 2,000 characters.&#8221;</p>
<p>As far as the characters that can be used, we can consult <a href="http://tools.ietf.org/html/rfc3986">RFC 3986</a> in the &#8220;Unreserved Characters&#8221; section and find we&#8217;re allowed 0-9, a-z, A-Z, and &#8220;- _ . ~&#8221; (dash, underscore, dot, tilde.) I&#8217;m going to avoid using those because I&#8217;d like to keep them for separating my identifiers. So, if we just go with the &#8220;normal characters&#8221;, there&#8217;s 62 of them.</p>
<h3>Collision Constraints</h3>
<p>If you assume a hashing function generates numbers pretty randomly spread throughout the number space available to it (which I will for this) there&#8217;s actually some pretty good math we can use to figure out how likely a collision will be. If you&#8217;re interested in where this comes from, check out the <a href="http://en.wikipedia.org/wiki/Birthday_paradox">Birthday Paradox</a>, which is nicely written up at Wikipedia. Using the formulas there you can calculate the probability of having 2 keys that collide, as a function of how many things you&#8217;re trying to insert, and the size of the overall hash space.</p>
<h3>Putting it all together</h3>
<p>First, figure out how many items you want to be able to store. (for me, 20,000 is a high estimate).<br />
Second, figure out how comfortable you are with having some keys collide. 50/50 odds? (0.5) 1/1000 chance? (0.001)<br />
I&#8217;m actually pretty ok having a very small number of collisions &#8212; I&#8217;m going to use 0.99, a 99% chance that SOMETHING will collide. (That&#8217;s still going to be a small number, less than a 100% chance that anything at all will collide, let alone more than one.)</p>
<p>I sketched up a little script for this article (which you can <a href="http://axis.serialized.net/gitweb/?p=utilities.git;a=blob;f=hashspace_model;hb=HEAD">grab from my git repo</a>. It&#8217;s pretty self-documenting, so I&#8217;ll let it speak for itself.</p>
<p>The goal is, given our constraints, and the fact that we need to use only the characters 0 .. 9, a .. z, A .. Z, (a) how many of those characters do we need, and (b) given that it probably won&#8217;t be an exact fit, what will our collision probability end up being?</p>
<pre class="brush: perl;">
my ($items, $probability) = (0,0);
GetOptions(&quot;items=i&quot; =&gt; \$items, &quot;probability=f&quot; =&gt; \$probability);

printf(&quot;Trying to store %d items with a %0.5f chance of collision\n&quot;, $items, $probability);

# solve the birthday paradox in terms of 'how big the range should be'
# http://en.wikipedia.org/wiki/Birthday_paradox#Calculating_the_probability =&gt; wolfram alpha, 'solve for d'
my $max = -1*(($items - 1)*$items)/(2 * log(1-$probability));

print &quot;To do that, we'd need to be working in the range 1:$max\n&quot;;

# compare that back to the original formula from Wikipedia and make sure that worked
my $calculated_prob = get_probability($max, $items);

printf(&quot;probability %0.10f should match your requested probability of %0.10f, or something went wrong.\n&quot;, $calculated_prob, $probability);

# so it does, cool
# how many bits would it take to represent $max?

my $frac_bits = log($max)/log(2);
my $real_bits = int($frac_bits)+1;

printf(&quot;This can be represented by %0.2f bits (%d)\n&quot;, $frac_bits, $real_bits);

# ok, so how about encoding those into URI's? We can use 0-9, a-z and A-Z, which gives 62 &quot;digits&quot;
# aka base62. $max in base62 can be calculated like we did &quot;how many base2 digits were needed&quot;

my $base62_digits = int(log($max)/log(62))+1;

print &quot;Using 0..9, a..z and A..Z we can represent each object with $base62_digits digits\n&quot;;

# ok, if we're using 5 digits, what's our actual &quot;space&quot; and what's the probability of a collision?

my $newmax = 62**$base62_digits - 1;

# which is actually how many bits?
my $newbits = int(log($newmax)/log(2));

printf(&quot;Given that we'll have to use %d digits, (%d bits), the real probability of a collision is %0.10f\n&quot;, $base62_digits, $newbits, get_probability($newmax, $items));

sub get_probability {
    my($max, $items) = @_;
    return 1 - (($max - 1)/$max)**(($items*($items-1))/2);
}
</pre>
<p>The most mysterious parts of this are probably the formulas. The one in the get_probability subroutine is transcribed right <a href="http://en.wikipedia.org/wiki/Birthday_paradox#Calculating_the_probability">from the Wikipedia page</a>, but the other one is the same formula, solved for a different value. In general, if you need to do this, <a href="http://wolframalpha.com">WolframAlpha</a> is a math nerd&#8217;s dream come true. I just asked it to &#8220;solve (the equation) for d&#8221; and got the new formula I needed.<br />
<a href="http://serialized.net/wp-content/uploads/2010/05/solve_equation.jpg"><img src="http://serialized.net/wp-content/uploads/2010/05/solve_equation.jpg" alt="solve p = 1 - e^((-1*n*(n-1))_2d) for d" title="Solve Equation" width="579" height="379" class="alignnone size-full wp-image-381" /></a></p>
<p>The solution actually comes from &#8220;show your steps&#8221; &#8212; I can find an intermediate form that&#8217;s easier to represent in a non-math-centric programming language. (I&#8217;m sure you can do imaginary numbers in perl, but it was kind of outside the scope of my plans for this evening.)</p>
<p>Here&#8217;s the formula I ended up using:<br />
<a href="http://serialized.net/wp-content/uploads/2010/05/solution.jpg"><img src="http://serialized.net/wp-content/uploads/2010/05/solution.jpg" alt="Solved for D" title="solution" width="107" height="41" class="alignnone size-full wp-image-379" /></a></p>
<p>Here&#8217;s a few sample runs of the script:</p>
<p>First, using my personal constraints for this project:</p>
<pre class="brush: plain;">
./hashspace_model -i 20000 -p 0.99
Trying to store 20000 items with a 0.99000 chance of collision
To do that, we'd need to be working in the range 1:43427276.7179157
probability 0.9900000006 should match your requested probability of 0.9900000000, or something went wrong.
This can be represented by 25.37 bits (26)
Using 0..9, a..z and A..Z we can represent each object with 5 digits
Given that we'll have to use 5 digits, (29 bits), the real probability of a collision is 0.1961141788
</pre>
<p>Cool! So I said I&#8217;m ok with a 99% chance of a collision, and the algorithm figured out that in order to do that, I&#8217;d need to be using 5 digits of base62. And if I&#8217;m using 5 digits of base62, I get 3 more bits than I strictly &#8220;need&#8221;, which means I end up with only about a 1/5 chance of EVER getting a collision.</p>
<p>Let&#8217;s say I wanted to be more strict, and go &#8220;one in a million&#8221;.</p>
<pre class="brush: plain;">
./hashspace_model -i 20000 -p 0.000001
Trying to store 20000 items with a 0.000001 chance of collision
To do that, we'd need to be working in the range 1:199989899999232
probability 0.0000009992 should match your requested probability of 0.0000010000, or something went wrong.
This can be represented by 47.51 bits (48)
Using 0..9, a..z and A..Z we can represent each object with 8 digits
Given that we'll have to use 8 digits, (47 bits), the real probability of a collision is 0.0000009103
</pre>
<p>In this case base62 comes pretty close to exactly the dimensions that we want, so we more or less get 1/1,000,000 on the nose with 8 digits.</p>
<p>If you grab the script to play with, you may need to tweak the printf&#8217;s and make sure they have enough resolution for the data you&#8217;re trying to examine.</p>
<p>Ok, so that tells us how bit our bitspace needs to be, but not how to get a hash, or how to do base62.</p>
<h3>Hashing Function</h3>
<p>I chose md5 because&#8230;. it seems to work fine. I&#8217;m sure there&#8217;s a better option, but this is working for now. However, md5 has a lot more bits available than I need. How to just steal a few of them?</p>
<p>First, you need to know how many bits you want. Thankfully, I know I want 29 bits (thanks, helper script!). I can extract 29 bits of information from it by making a &#8220;mask&#8221; of 29 1&#8242;s, which can be done easily like so:</p>
<pre class="brush: perl;">
my $mask = 2**29 - 1;
</pre>
<p>So, now I just need a raw integer slice of an md5, and do a &#8220;logical and&#8221; of that:</p>
<pre class="brush: perl;">
use Digest::MD5 qw(md5);
# unpack makes this back into an integer for us
# L == interpret the data as a 32 bit unsigned long.
# See 'perldoc -f pack' for a ton of other options
my $value = unpack(&quot;L&quot;, md5(&quot;the string we want&quot;));
$value = $value &amp; $mask;
</pre>
<p>Groovy. Now we know what number we want to represent, we need to actually represent that in this weird &#8220;base62&#8243; format.</p>
<p>The algorithm for converting to base(anything) is actually pretty easy.<br />
We&#8217;re used to thinking in base10, so I&#8217;ll show an example of running the algorithm to from and to base10, just so the flow is clear.</p>
<table>
<tr>
<td>125</td>
<td>Starting Value</td>
</tr>
<tr>
<td>125 % 10 = 5</td>
<td>Find the remainder when dividing by the &#8220;destination base.&#8221; Keep &#8220;5&#8243; as the &#8220;new number&#8221;</td>
</tr>
<tr>
<td>125 / 10 = 12</td>
<td>Divide by the &#8220;destination base&#8221;. This chops the number off the end that we just snagged as a remainder</td>
</tr>
<tr>
<td>12 % 10 = 2</td>
<td>Do it again with the result of the division. Stick the result to the front of number we&#8217;re keeping track of (now 25)</td>
</tr>
<tr>
<td>12 / 10 = 1</td>
<td>And do the division again</td>
</tr>
<tr>
<td>1 % 10 = 1</td>
<td>Last remainder, glue to the front again, and we have the answer &#8220;125&#8243;. (Back where we started.)</td>
</tr>
<tr>
<td>1 / 10 = 0</td>
<td>As soon as we get 0 from the division, our work here is done.</td>
</tr>
</table>
<p>Simple enough to understand in base10, but the exact same technique works when going from base10 to any other base (2 would work to convert to binary, we&#8217;re using 62, and if you could find enough characters, you could go to something crazy like base300 using the same idea.)</p>
<p>Here&#8217;s the source for it:</p>
<pre class="brush: perl;">
# even though they are letters we are using them here in the role of &quot;digits&quot;
my @digits = (0 .. 9, 'a' .. 'z', 'A' .. 'Z');
my %digits = map { $digits[$_] =&gt; $_ } 0 .. $#digits;

sub to_base {
    my($base, $value) = @_;
    die &quot;base $base out of range 1-62&quot; unless ($base &gt; 0 &amp;&amp; $base &lt;= 62);
    return $digits[0] if $value == 0;
    my $rep = &quot;&quot;;

    while($value &gt; 0) {
        # prepend the &quot;digit&quot; we get when dividing by the base
        $rep = $digits[$value % $base ] . $rep;
        # then &quot;shift right&quot; the working value
        $value = int( $value / $base );
    }
    return $rep;
}

sub from_base {
    my($base, $rep) = @_;
    die &quot;base $base out of range 1-62&quot; unless ($base &gt; 0 &amp;&amp; $base &lt;= 62);
    my $value = 0;
    # this pattern grabs a character at a time, left to right
    for ( $rep =~ /./g ) {
        $value *= $base;
        $value += $digits{$_};
    }
    return $value;
}
</pre>
<p>So there you have it. Provably optimal URI-compatible identifiers with 3 easy steps:</p>
<ol>
<li>Figure out what the constraints for your problem space are</li>
<li>Grab enough bits from md5</li>
<li>Convert to (and from) base62</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://serialized.net/2010/05/optimal-web-sized-identifiers/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Getting around tmpfs &#8216;noexec&#8217; problems</title>
		<link>http://serialized.net/2010/03/getting-around-tmpfs-noexec-problems/</link>
		<comments>http://serialized.net/2010/03/getting-around-tmpfs-noexec-problems/#comments</comments>
		<pubDate>Tue, 16 Mar 2010 17:55:45 +0000</pubDate>
		<dc:creator>Joshua Barratt</dc:creator>
				<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://serialized.net/?p=366</guid>
		<description><![CDATA[In general, running your /tmp (or /var/tmp) without the execute bit set is a good idea. And sometimes, you don&#8217;t have a choice &#8212; for example, when running in a hosting environment running Virtuozzo. You&#8217;re liable to see a mount that looks like this: $ mount &#124; grep noexec /dev/simfs on /tmp type simfs (rw,noexec) [...]]]></description>
			<content:encoded><![CDATA[<p>In general, running your /tmp (or /var/tmp) without the execute bit set is a good idea. And sometimes, you don&#8217;t have a choice &#8212; for example, when running in a hosting environment running Virtuozzo.</p>
<p>You&#8217;re liable to see a mount that looks like this:</p>
<pre class="brush: plain; gutter: false;">
$ mount | grep noexec
/dev/simfs on /tmp type simfs (rw,noexec)
/dev/simfs on /var/tmp type simfs (rw,noexec)
devpts on /dev/pts type devpts (rw,nosuid,noexec)
</pre>
<p>However, sometimes that&#8217;s a problem, for example if you run the fantastic <a href="http://www.asic-linux.com.mx/~izto/checkinstall/">checkinstall</a> tool to package software. You might see an error message like this:</p>
<pre class="brush: plain; gutter: false;">
/usr/bin/installwatch: /var/tmp/tmp.SuogJyYftZ/installscript.sh: /bin/sh: bad interpreter: Permission denied
</pre>
<p>The noexec permission has gotten us. How to work around it?<br />
Here&#8217;s a quick, easy to roll back method:</p>
<pre class="brush: plain; gutter: false;">
# make a new temporary directory for this use
mkdir ~/tmptmp
# use mount --bind to overlay this on our 'real' /var/tmp for now
$ sudo mount --bind ~/tmptmp /var/tmp
# do your work
$ sudo checkinstall make install
# restore the natural order to the universe
$ sudo umount /var/tmp
</pre>
<p>You could also (potentially) remount /tmp without that option temporarily, but that isn&#8217;t always possible. (See &#8216;virtuozzo&#8217; above.)</p>
]]></content:encoded>
			<wfw:commentRss>http://serialized.net/2010/03/getting-around-tmpfs-noexec-problems/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Adding iSCSI exports to OpenSolaris for remote Time Machine</title>
		<link>http://serialized.net/2010/03/adding-iscsi-exports-to-opensolaris-for-remote-time-machine/</link>
		<comments>http://serialized.net/2010/03/adding-iscsi-exports-to-opensolaris-for-remote-time-machine/#comments</comments>
		<pubDate>Tue, 16 Mar 2010 01:08:56 +0000</pubDate>
		<dc:creator>Joshua Barratt</dc:creator>
				<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://serialized.net/?p=362</guid>
		<description><![CDATA[There are 3 ways to Time Machine over the network (that I know of) Use a Time Capsule Run Snow Leopard Server, which allows you to set remote drives as Time Machine volumes Mount the iSCSI share via an &#8220;initiator&#8221;, and use it like it was a normal hard drive. I&#8217;m not sure I&#8217;d want [...]]]></description>
			<content:encoded><![CDATA[<p>There are 3 ways to Time Machine over the network (that I know of)</p>
<ol>
<li>Use a Time Capsule</li>
<li>Run Snow Leopard Server, which allows you to set remote drives as Time Machine volumes</li>
<li>Mount the iSCSI share via an &#8220;initiator&#8221;, and use it like it was a normal hard drive.</li>
</ol>
<p>I&#8217;m not sure I&#8217;d want to use the iSCSI option with a laptop, so I use a hybrid of the second and third options.</p>
<p>I&#8217;m running Snow Leopard Server on a Mac Mini with limited disk space. I&#8217;ve got a large server running OpenSolaris about which I <a href="http://serialized.net/2009/02/the-littlest-thumper-opensolaris-nas-on-an-msi-wind-pc/">blogged previously.</a></p>
<p>Here are the brief steps you need to take to get it working.</p>
<p>On the Snow Leopard Server, install the <a href="http://www.studionetworksolutions.com/products/product_detail.php?t=more&#038;pi=11">ISCSI initiator from GlobalSan.</a></p>
<p>On your OpenSolaris server, make sure you&#8217;ve installed the support packages needed.</p>
<pre class="brush: plain;">
pkg install SUNWiscsi SUNWiscsitgt
</pre>
<p>Now, from one of your available disk pools, create the virtual hard drives for your servers to mount, (making sure you make them about 1.5x larger than the disks you&#8217;re trying to back up, so you can get historical information from them.</p>
<pre class="brush: plain;">
zfs create -o shareiscsi=on -s -V 160GB mypool/laptop_tm
zfs create -o shareiscsi=on -s -V 140GB mypool/server_tm
</pre>
<p>To find out what the &#8220;target ID&#8217;s&#8221; are, run this:</p>
<pre class="brush: plain;">
iscsitadm list target
</pre>
<p>shows the target names to paste into the initiator.</p>
<p>You can follow the instruction at this <a href="http://blogs.sun.com/constantin/entry/zfs_and_mac_os_x">blog on sun.com</a> which has screenshots of the rest of the process.</p>
<p>At this point the drives are just like regular disks, from the perspective of your Mac (desktop or server) &#8212; and can be formatted and used for Time Machine. Very convenient and the data that you&#8217;re backing up will be in the safe, safe hands of ZFS in the event of a disk failure.</p>
]]></content:encoded>
			<wfw:commentRss>http://serialized.net/2010/03/adding-iscsi-exports-to-opensolaris-for-remote-time-machine/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Using custom functions in Puppet templates</title>
		<link>http://serialized.net/2010/01/using-custom-functions-in-puppet-templates/</link>
		<comments>http://serialized.net/2010/01/using-custom-functions-in-puppet-templates/#comments</comments>
		<pubDate>Sat, 02 Jan 2010 00:09:48 +0000</pubDate>
		<dc:creator>Joshua Barratt</dc:creator>
				<category><![CDATA[Puppet]]></category>

		<guid isPermaLink="false">http://serialized.net/?p=353</guid>
		<description><![CDATA[This eventually required a support ticket to figure out, so I&#8217;m documenting it here in case it&#8217;s useful to others. The problem: You have a puppet function you find useful. At this point custom functions are really easy to create and distribute via modules so there&#8217;s really no reason not to be using them. However, [...]]]></description>
			<content:encoded><![CDATA[<p>This eventually required a support ticket to figure out, so I&#8217;m documenting it here in case it&#8217;s useful to others.</p>
<p>The problem: You have a puppet function you find useful. At this point custom functions are <a href="http://reductivelabs.com/trac/puppet/wiki/PluginsInModules">really easy to create and distribute via modules</a> so there&#8217;s really no reason not to be using them. However, you want to use the result of a function in your templates.</p>
<p>A real-world example of this is sharing authentication information. (For example, crypted password hashes. Plain text, as always, would be evil no matter where it was living.)<br />
You really have 3 choices:</p>
<ul>
<li>Coding it right into your puppet code and checking that into your version control system</li>
<li>Not distributing that information with puppet</li>
<li>Using puppet to distribute the information, but store it outside of your puppet modules somehow.</li>
</ul>
<p>We chose the third way, and store this kind of information in YAML files outside the puppet modules tree.<br />
Anyway. So we want to access this info inside a template.</p>
<p>Assume you have a function called &#8216;get_extdata&#8217;.<br />
You normally call from your &#8216;.pp&#8217; manifests with something like</p>
<pre class="brush: ruby;">
$data = get_extdata('mymodule', 'path:to:data')
</pre>
<p>You can make a call like that from your template by doing this wonderful fragment of magic-wand-waving to get this function accessible:</p>
<p>In <b>mytemplate.erb</b>:</p>
<pre class="brush: plain;">
&lt;% Puppet::Parser::Functions.autoloader.loadall %&gt;
Now you can use the 'scope' variable to get at your function.
This is the equivalent of the call get_extdata('mymodule',  'path:to:data'):
&lt;%= scope.function_get_extdata('mymodule',  'path:to:data') %&gt;
</pre>
<p>This works like a charm, and means you don&#8217;t have to create a bunch of variables that you don&#8217;t really need before you load the template.</p>
<p>So if you were creating an .htpasswd style file with puppet, you could do</p>
<p>In your <b>init.pp</b></p>
<pre class="brush: ruby;">
$users = ['steve', 'paul', 'stu']
</pre>
<p><b>htpasswd.erb</b>:</p>
<pre class="brush: plain;">
&lt;% Puppet::Parser::Functions.autoloader.loadall %&gt;
&lt;% users.each do |user| -%&gt;
&lt;%= user %&gt;:&lt;%= scope.function_get_extdata('authmodule',  user) %&gt;
&lt;% end -%&gt;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://serialized.net/2010/01/using-custom-functions-in-puppet-templates/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Updated &#8220;Inbox Zero with Mail.app&#8221; technique</title>
		<link>http://serialized.net/2009/12/updated-inbox-zero-with-mail-app-technique/</link>
		<comments>http://serialized.net/2009/12/updated-inbox-zero-with-mail-app-technique/#comments</comments>
		<pubDate>Wed, 02 Dec 2009 17:37:13 +0000</pubDate>
		<dc:creator>Joshua Barratt</dc:creator>
				<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://serialized.net/?p=344</guid>
		<description><![CDATA[Back in March, I wrote about my technique for implementing Inbox Zero with Mail.app. Since then, the world has changed. I&#8217;ve upgraded to Snow Leopard, and Quicksilver has essentially stopped working out for me. (I&#8217;ve been trying to keep it up to date, and sometimes it kind of works, but my confidence levels are really [...]]]></description>
			<content:encoded><![CDATA[<p>Back in March, I wrote about my <a href="http://serialized.net/2009/03/my-approach-to-inbox-zero-with-mailapp/">technique for implementing Inbox Zero with Mail.app</a>.</p>
<p>Since then, the world has changed. I&#8217;ve upgraded to Snow Leopard, and Quicksilver has essentially stopped working out for me. (I&#8217;ve been trying to keep it up to date, and sometimes it kind of works, but my confidence levels are really low.) On the other hand, I&#8217;m loving me some <a href="http://code.google.com/p/qsb-mac/">Google Quick Search Box</a> (a new project from the <a href="http://www.cultofmac.com/quicksilver-is-sort-of-dead-long-live-google-quick-search-box/6986">original creator of Quicksilver</a>). The only feature that I still wanted Quicksilver for was triggers. And until today, I was still running it for trigger support to implement my Inbox Zero trick.</p>
<p>And then triggers broke when I upgraded it. And I&#8217;m out.</p>
<p>So here&#8217;s how to get Inbox Zero magic in Mail.app using only tools native to Snow Leopard.</p>
<p>First, launch Automator and create a new Service workflow.<br />
<img src="http://serialized.net/wp-content/uploads/2009/12/Automator_select_service.jpg" alt="Automator Create a new Service Workflow" title="Automator Create a new Service Workflow" width="556" height="517" class="aligncenter size-full wp-image-345" /></p>
<p>Select &#8220;Utilities&#8221; and then drag in &#8220;Run Applescript&#8221; to the window on the right.</p>
<p><img src="http://serialized.net/wp-content/uploads/2009/12/automator-drag-run-applescript.jpg" alt="Automator: Run Applescript" title="Automator: Run Applescript" width="532" height="350" class="aligncenter size-full wp-image-346" /></p>
<p>Fill in your applescript, replacing only the center &#8220;comment&#8221; block &#8212; leave the other autogenerated lines there.</p>
<p>The core code again, (tweak to fit your accounts and archive mailbox names)</p>
<pre class="brush: plain; light: true;">
tell application &quot;Mail&quot;
	set theSelectedMessages to selection
	set myAccount to &quot;zimbra&quot;
	set myMailbox to &quot;Archive&quot;
	repeat with theMessage in theSelectedMessages
		move theMessage to mailbox myMailbox of account myAccount
	end repeat
end tell
</pre>
<p>It should look something like this:<br />
<img src="http://serialized.net/wp-content/uploads/2009/12/automator_archive.jpg" alt="Automator: Archive Applescript" title="Automator: Archive Applescript" width="586" height="490" class="aligncenter size-full wp-image-349" /></p>
<p>And that&#8217;s it! Save it, and give it a name you&#8217;ll remember. (I chose &#8220;Archive Selected Mail.&#8221;)</p>
<p>Now it&#8217;s actually in Mail&#8217;s Menu (under &#8216;Services&#8217;) and we can use the built in OSX hotkey support to launch it.</p>
<p>Go to the System Preferences Menu, click &#8220;Keyboard Shortcuts&#8221;, and add one for the menu item you created. (It needs to be the same exact name you saved your service as.)</p>
<p><img src="http://serialized.net/wp-content/uploads/2009/12/Keyboard-Shortcuts-Archive-Mail.jpg" alt="Keyboard Shortcuts: Archive Mail" title="Keyboard Shortcuts: Archive Mail" width="708" height="637" class="aligncenter size-full wp-image-350" /></p>
<p>Badda bing. Feature complete, and I can (sadly) retire Quicksilver forever. This technique should be extensible to all kinds of other cases when you want to add new features to applications, for access either through the menus or with hot keys.</p>
]]></content:encoded>
			<wfw:commentRss>http://serialized.net/2009/12/updated-inbox-zero-with-mail-app-technique/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Great WordPress Plugin: WP Widget Cache</title>
		<link>http://serialized.net/2009/10/great-wordpress-plugin-wp-widget-cache/</link>
		<comments>http://serialized.net/2009/10/great-wordpress-plugin-wp-widget-cache/#comments</comments>
		<pubDate>Sun, 25 Oct 2009 05:26:51 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://serialized.net/?p=333</guid>
		<description><![CDATA[Using WP Widget Cache, I improved my site load time by about 600% by just starting to cache the widgets. Here are some screenshots from my strace-based &#8216;magic profiler&#8217; (which I still promise to talk more about later.) It lets you attach to a process which is about to render your website, and then trace [...]]]></description>
			<content:encoded><![CDATA[<p>Using <a href="http://wordpress.org/extend/plugins/wp-widget-cache/">WP Widget Cache</a>, I improved my site load time by about 600% by just starting to cache the widgets.</p>
<p>Here are some screenshots from my strace-based &#8216;magic profiler&#8217; (which I still promise to talk more about later.)<br />
It lets you attach to a process which is about to render your website, and then trace all the interactions it has with the underlying system while it renders it.</p>
<p>That includes network traffic, file server traffic, DNS resolver lookups &#8212; everything your app does to communicate to the outside world. The timing is always going to look slower than normal (at least a bit) as the tracing does impose some overhead, but it&#8217;s relatively pretty accurate.</p>
<p>Here&#8217;s what I saw right after telling WP Widget Cache to clear the caches.</p>
<p><img class="size-full wp-image-334 alignnone" title="With a freshly cleaned Widget cache" src="http://serialized.net/wp-content/uploads/2009/10/serialized_net_cleared_cache.jpg" alt="With a freshly cleaned Widget cache" width="348" height="269" /></p>
<p>And here is what I saw on the second hit:</p>
<p><img class="size-full wp-image-336 alignnone" title="With WP Widget Cache working like it should" src="http://serialized.net/wp-content/uploads/2009/10/serialized_net_widgets_cached.jpg" alt="With WP Widget Cache working like it should" width="303" height="188" /></p>
<p>I have widgets to load Flickr, (the entry with &#8216;flickr&#8217; in it) delicious bookmarks, (the yahoo one) and Twitter. (The naked IP. Apparently they don&#8217;t like PTR records.) Those three external calls dominated my total time to generate the page. Now, it&#8217;s down to a very quick MySQL lookup and a chatty conversation with a fileserver.</p>
<p>One feature I especially appreciate is the ability to tune cache times per widget. If I want new Flickr photos to be available hourly, sure. If my tweets need to be there within 5 minutes, not a problem. And because the widgets are there every time anyone uses any part of the site (even the rarer ones) it plays really well with the other WP Cache Plugins.</p>
]]></content:encoded>
			<wfw:commentRss>http://serialized.net/2009/10/great-wordpress-plugin-wp-widget-cache/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Standard Deviation with Arduino</title>
		<link>http://serialized.net/2009/10/standard-deviation-with-arduino/</link>
		<comments>http://serialized.net/2009/10/standard-deviation-with-arduino/#comments</comments>
		<pubDate>Sat, 24 Oct 2009 18:30:55 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[arduino]]></category>

		<guid isPermaLink="false">http://serialized.net/?p=320</guid>
		<description><![CDATA[My brother and I were playing around with Arduinos as part of my epic road trip this summer. He was trying to get a stable temperature reading from a sensor. The issue was that the sensor was fine, except when it was rapidly transitioning from one temperature to another. For example, if the ambient temperature [...]]]></description>
			<content:encoded><![CDATA[<p>My brother and I were playing around with Arduinos as part of my epic road trip this summer. He was trying to get a stable temperature reading from a sensor.</p>
<p>The issue was that the sensor was fine, except when it was rapidly transitioning from one temperature to another. For example, if the ambient temperature was 74, and suddenly a can of soda fresh from the fridge was placed on it, you&#8217;d get several &#8220;transitional readings&#8221; (70, 68, 52, 48, &#8230;) until the temperature finally stabilized.</p>
<p><img src="http://serialized.net/wp-content/uploads/2009/10/119421176_e1c82c8298-300x199.jpg" alt="Delicious Arduino" title="Delicious Arduino" width="300" height="199" class="alignright size-medium wp-image-326" /></p>
<p>We talked about various ways of detecting this case, but the most straightforward one seemed to be <a href="http://en.wikipedia.org/wiki/Standard_Deviation">Standard Deviation</a>, which Wikipedia explains much more clearly than I could.</p>
<p>I googled around for sample code or a library and didn&#8217;t find any. So even if my Google-fu is weak and there are great resources out there, now there&#8217;s another one.</p>
<p>The basic idea is that we take 10 samples quickly (20 ms apart), figure out the Standard Deviation, and if that&#8217;s close enough to zero, we can call this a &#8220;stable temperature.&#8221;</p>
<p>The <a href="http://hub.serialized.net/gitweb/?p=arduino.git;a=blob_plain;f=Standard_Deviation/Standard_Deviation.pde;hb=HEAD">full code</a> is available from <a href="http://hub.serialized.net/gitweb/">my git repository</a>, but here&#8217;s the core of it:</p>
<pre class="brush: cpp;">
  // Gather sample data

  float sampleSum = 0;
  for(int i = 0; i &lt; SAMPLES; i++) {
    s_val[i] = analogRead(TS);
    sampleSum += s_val[i];
    delay(20); // set this to whatever you want
  }
  float meanSample = sampleSum/float(SAMPLES);

  // HOW TO FIND STANDARD DEVIATION
  // STEP 1, FIND THE MEAN. (We Just did.)

  // STEP 2, sum the squares of the differences from the mean

  float sqDevSum = 0.0;

  for(int i = 0; i &lt; SAMPLES; i++) {
    // pow(x, 2) is x squared.
    sqDevSum += pow((meanSample - float(s_val[i])), 2);
  }

  // STEP 3, FIND THE MEAN OF THAT
  // STEP 4, TAKE THE SQUARE ROOT OF THAT

  float stDev = sqrt(sqDevSum/float(SAMPLES));

  // TADA, STANDARD DEVIATION.
  // this is in units of sensor ticks (0-1023)
</pre>
<p>So, hopefully the comments are self-explanatory (when combined with Wikipedia, if you&#8217;ve never seen Standard Deviation before.)</p>
<p>At the end of this block you have 2 useful variables defined: &#8216;meanSample&#8217;, which is the mean (average) value of all the samples you polled, and &#8216;stDev&#8217; &#8212; the standard deviation amongst all the samples.</p>
<p>This allows you to do things like</p>
<pre class="brush: cpp;">
if(stDev &lt; TOLERANCE) {
    // reading is stable enough
    fireMissleAt(meanSample);
}
</pre>
<p>An important note about the units &#8212; the Arduino analogRead values are 12 bit, meaning they range from 0-1023.<br />
In general, those numbers will &#8220;mean something&#8221; to you. Perhaps you can convert them to a temperature as we were doing, or a direction froma  compass, or a position on a pot. I chose to do the math with the numbers in as raw a form as possible. This means that if you have a sensorToTemp() function, you can call that on meanSample, but you&#8217;d also want to call that on stDev as well. Make sure to<br />
convert both values into meaningful values for your application. If you just want to know if the measurement is stable, then perhaps just knowing that the (12 bit) version of stDev is over your comfort level is enough.</p>
<p>Along with Standard Deviation came the need to print out some floating point numbers, so I also included some sample code to do that.<br />
It&#8217;s hard coded to send things to the serial port, but could be easily tweaked to fabricate a return string or print somewhere else.</p>
<pre class="brush: cpp;">
// This is a utility function for printing out floating point values
// Fixed at %0.2f form. (XX.YY, 2 digits after whatever decimal part there is.)
void printFloat(float var) {

  int int_part = int(var);
  int float_part = 100*var - (100*int_part);

  Serial.print(int_part, DEC);
  Serial.print(&quot;.&quot;);
  if(float_part &lt; 10) {
    Serial.print(&quot;0&quot;);
  }
  Serial.print(float_part, DEC);
}
</pre>
<p>Hopefully this helps someone else out. It was fun to write and I&#8217;m sure I&#8217;ll have a use for it someday!</p>
<p>Photo Credit (no that&#8217;s not me!)
<div xmlns:cc="http://creativecommons.org/ns#" about="http://www.flickr.com/photos/jeanbaptisteparis/119421176/"><a rel="cc:attributionURL" href="http://www.flickr.com/photos/jeanbaptisteparis/">http://www.flickr.com/photos/jeanbaptisteparis/</a> / <a rel="license" href="http://creativecommons.org/licenses/by-sa/2.0/">CC BY-SA 2.0</a></div>
]]></content:encoded>
			<wfw:commentRss>http://serialized.net/2009/10/standard-deviation-with-arduino/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>local::lib: Better perl when you don&#8217;t have root.</title>
		<link>http://serialized.net/2009/10/locallib-better-perl-when-you-dont-have-root/</link>
		<comments>http://serialized.net/2009/10/locallib-better-perl-when-you-dont-have-root/#comments</comments>
		<pubDate>Mon, 19 Oct 2009 16:54:25 +0000</pubDate>
		<dc:creator>Joshua Barratt</dc:creator>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[perl]]></category>

		<guid isPermaLink="false">http://serialized.net/?p=313</guid>
		<description><![CDATA[These days as we move increasingly to &#8216;the clouds&#8217;, it&#8217;s becoming common to get a server where you&#8217;ve got local root. However, those (from a provider and a user perspective) aren&#8217;t always the best way to go. Those are more expensive to provide (dedicated IP&#8217;s, more dedicated resources on the server) and thus more expensive [...]]]></description>
			<content:encoded><![CDATA[<p>These days as we move increasingly to &#8216;the clouds&#8217;, it&#8217;s becoming common to get a server where you&#8217;ve got local root. However, those (from a provider and a user perspective) aren&#8217;t always the best way to go. Those are more expensive to provide (dedicated IP&#8217;s, more dedicated resources on the server) and thus more expensive to buy. Also, in some use cases they might not scale so well if you&#8217;re buying a lower end one &#8212; if you buy 256MB of RAM, it&#8217;s pretty easy to push that into the red. On the administration side, the bar is raised for what you need to be able to do to solve simple problems. Adding an email account, or setting up the web server &#8212; all of these are solvable, and control panels can help, but what if you just want to serve some content? Why deal with it?</p>
<p>So, we still have shared hosting, in it&#8217;s various forms. I like the <a href="http://mediatemple.net">(mt) Grid Server</a>, but I will admit to being biased (a lot.)</p>
<p>So with shared hosting, you don&#8217;t have root. And managing software you need (to get useful web applications up and running) can be a pain &#8212; a lot of the documentation is biased towards the idea that you have a full server, be it virtual or physical.</p>
<p>If you don&#8217;t have root on your server, and you&#8217;re using perl (for any of the amazing Modern Perl tools and stacks out there,) I recommend checking out the awesome <a href="http://search.cpan.org/perldoc?local::lib">local::lib</a>.</p>
<p>Once you follow their &#8216;bootstrapping config&#8217; and set up your bashrc, it makes things feel like you&#8217;re on your own server. Just &#8216;cpan -i WordPress::Post&#8217; or &#8216;./Build install&#8217;, and it just works!</p>
<p>So a full install using local::lib would look like:<br />
Download and unpack local::lib:<br />
(Warning, update the link to the latest version, this is the current release:)</p>
<pre class="brush: plain;">
mkdir tmp
 cd tmp
wget http://search.cpan.org/CPAN/authors/id/A/AP/APEIRON/local-lib-1.004008.tar.gz
tar -zxvf local-lib-1.004008.tar.gz
</pre>
<p>cd into the local::lib directory, and then run:</p>
<pre class="brush: plain;">
perl Makefile.PL --bootstrap
make test &amp;&amp; make install
echo 'eval $(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)' &gt;&gt;~/.bash_profile
. ~/.bash_profile
</pre>
<p>And you&#8217;re done! Every time you ssh in, your environment will be set up to use your whole local tree. So even if you want perl&#8217;s New Fancy Hotness, all it takes is:</p>
<pre class="brush: plain;">
cpan -i Moose
</pre>
<p>One caveat if you&#8217;re using local::lib and trying to run this tool from cron, is that you&#8217;ll probably want to use a wrapper script to correctly set up the environment. (Sometimes you&#8217;ll need to set up the environment for your web apps, too &#8212; that can be done via .htaccess.)</p>
<p>Here&#8217;s an example wrapper script, set up to work on my MediaTemple (gs) account and work with my local::lib setup:</p>
<pre class="brush: plain;">
#!/bin/bash

#set up environment
HOME=/home/12345/users/.home
eval $(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)

# run the script
$HOME/perl5/bin/mycron.pl
</pre>
<p>To make things work with your webapp, you need some &#8216;SetEnv&#8217; commands in the .htaccess. You can do this by just loading the local::lib module and checking the output:</p>
<pre class="brush: plain;">
%&gt; perl -I$HOME/perl5/lib/perl5 -Mlocal::lib
export MODULEBUILDRC=&quot;/home/12345/users/.home/perl5/.modulebuildrc&quot;
export PERL_MM_OPT=&quot;INSTALL_BASE=/home/12345/users/.home/perl5&quot;
export PERL5LIB=&quot;/home/68601/users/.home/perl5/lib/perl5:/home/12345/users/.home/perl5/lib/perl5/i386-linux-thread-multi:$PERL5LIB&quot;
export PATH=&quot;/home/12345/users/.home/perl5/bin:$PATH&quot;
</pre>
<p>Each one of those EXPORT statements needs to become a SetEnv statement.</p>
<pre class="brush: plain;">
# so this:
#export MODULEBUILDRC=&quot;/home/12345/users/.home/perl5/.modulebuildrc&quot;
# becomes:
SetEnv MODULEBUILDRC &quot;/home/12345/users/.home/perl5/.modulebuildrc&quot;
</pre>
<p>Repeat that for each one of the variable &#8216;export&#8217; lines, and you should be good to go! Affordable, scalable, low management overhead shared hosting and an easy way to get modern perl. </p>
]]></content:encoded>
			<wfw:commentRss>http://serialized.net/2009/10/locallib-better-perl-when-you-dont-have-root/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>FlickrPress now available: Turns Flickr RSS to WordPress Posts</title>
		<link>http://serialized.net/2009/10/flickrpress-now-available-turns-flickr-rss-to-wordpress-posts/</link>
		<comments>http://serialized.net/2009/10/flickrpress-now-available-turns-flickr-rss-to-wordpress-posts/#comments</comments>
		<pubDate>Fri, 16 Oct 2009 18:05:53 +0000</pubDate>
		<dc:creator>Joshua Barratt</dc:creator>
				<category><![CDATA[Tech]]></category>

		<guid isPermaLink="false">http://serialized.net/?p=304</guid>
		<description><![CDATA[My wife and I like to post a lot of photos of our son to his blog. After using WordPress from both the browser and the iPhone client, I just wasn&#8217;t that happy using it for our pictures. On the other hand, we both love (and already use, and have Pro accounts on) Flickr. Having [...]]]></description>
			<content:encoded><![CDATA[<p>My wife and I like to post a lot of photos of our son to <a href="http://carterbarratt.com">his blog</a>.</p>
<div style="text-align:center;"><img src="http://serialized.net/wp-content/uploads/2009/10/carterbarratt_dontlikethiseither.jpg" alt="Carter's Site" border="0" width="422" height="368" /></div>
<p>After using WordPress from both the browser and the iPhone client, I just wasn&#8217;t that happy using it for our pictures. On the other hand, we both love (and already use, and have Pro accounts on) Flickr.</p>
<p>Having the photos &#8220;live&#8221; on Flickr means some handy things.</p>
<ol>
<li>They let you get an RSS feed of a tag. (We use &#8216;carterbarratt.&#8217;) This means we don&#8217;t even have to use an API key, which is convenient.</li>
<li>They always have an image available that&#8217;s sized to 500 on the longest side. This turns out to be a perfect image size (either height or width) for a lot of WordPress themes.</li>
<li>They handle video and make the thumbnails look exactly like a &#8220;non-video image&#8221; in the RSS feed.</li>
<li>Lots of things can upload to Flickr &#8212; we use mostly the desktop uploader (after exporting from Lightroom) and the new Flickr native client on our iPhones.</li>
<li>The exact same stuff (title, description) that I&#8217;d want on a photo blog is available when I upload to Flickr.</li>
</ol>
<p>So, FlickrPress was born. It&#8217;s a fairly simple perl script, intended to be run as a cron job, which uses <a href="http://search.cpan.org/perldoc?WordPress::Post">WordPress::Post</a> to create new blog posts for every flickr photo it finds with a certain tag, from certain users.</p>
<div style="text-align:center;"><img src="http://serialized.net/wp-content/uploads/2009/10/FlickrPress-flow.png" alt="FlickrPress Workflow" border="0" width="579" height="317" /></div>
<p>Check out the <a href="http://serialized.net/FlickrPress">FlickrPress page</a> for downloads and installation instructions.</p>
]]></content:encoded>
			<wfw:commentRss>http://serialized.net/2009/10/flickrpress-now-available-turns-flickr-rss-to-wordpress-posts/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
