<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss 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/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>IONCANNON</title>
	
	<link>http://www.ioncannon.net</link>
	<description>Thoughts on Software Development and Engineering</description>
	<lastBuildDate>Wed, 10 Mar 2010 23:26:24 +0000</lastBuildDate>
	
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
		
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/ioncannon" /><feedburner:info uri="ioncannon" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Parsing the SXSW Twitter Stream for Fun</title>
		<link>http://feedproxy.google.com/~r/ioncannon/~3/z172eFW7tQg/</link>
		<comments>http://www.ioncannon.net/uncategorized/963/parsing-the-sxsw-twitter-stream-for-fun/#comments</comments>
		<pubDate>Wed, 10 Mar 2010 23:26:24 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=963</guid>
		<description><![CDATA[Over the weekend I decided to toss together a simple twitter stream monitoring app that would capture SXSW tweets. I wanted to build on some of what I learned while hacking together the stuff for code2009 and it was also an excuse to play with node.js and a few other things. I figured I would [...]]]></description>
			<content:encoded><![CDATA[<p>Over the weekend I decided to toss together a simple twitter stream monitoring app that would <a href="http://www.ioncannon.net/projects/watchsxsw2010/">capture SXSW tweets</a>. I wanted to build on some of what I learned while hacking together the stuff for <a href="http://www.ioncannon.net/projects/code2009/">code2009</a> and it was also an excuse to play with <a href="http://nodejs.org/">node.js</a> and a few other things. I figured I would put together a quick post with all the different parts and as I have time I&#039;ll pull together some of the more important sections into larger posts. Be warned that the site may stop functioning since it was only a few hours work and mostly put together with bailing wire and gum. </p>
<p>Server side:</p>
<ul>
<li><a href="http://nodejs.org/">node.js</a> &#8211; Used to process the long polling requests</li>
<li><a href="http://nginx.org/">ngnix</a> &#8211; Front for multiple nodejs instances and serving some static files</li>
<li><a href="http://documentcloud.github.com/underscore/">underscore.js</a></li>
<li><a href="http://www.rabbitmq.com/">rabbitmq</a> &#8211; Used to monitor the realtime feed</li>
<li><a href="http://code.google.com/p/redis/">Redis</a> &#8211; Used as a datastore for everything</li>
<li><a href="http://www.ruby-lang.org/">ruby</a> &#8211; Used to glue a bunch of things together, tons of gems used</li>
<li><a href="http://github.com/blog/542-introducing-resque">Resque</a> &#8211; Used to handle the screen captures</li>
</ul>
<p>Browser side:</p>
<ul>
<li><a href="http://jquery.com/">jQuery</a></li>
<li><a href="http://code.google.com/p/flot/">jQuery flot</a></li>
<li><a href="http://omnipotent.net/jquery.sparkline/">jQuery sparklines</a></li>
</ul>
<p>Hosting:</p>
<ul>
<li><a href="http://aws.amazon.com/ec2/">AWS EC2</a> &#8211; Used for processing browser snapshots</li>
<li><a href="http://aws.amazon.com/cloudfront/">AWS Cloudfront</a> &#8211; Used for storing CSS, etc</li>
<li><a href="http://www.rackspacecloud.com/">Rackspace Cloud Servers</a> &#8211; Used to run the node servers</li>
<li><a href="http://www.linode.com/">Linode</a> &#8211; Used to run the main web server</li>
</ul>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ioncannon?a=z172eFW7tQg:GOnJ9DyGM1w:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=z172eFW7tQg:GOnJ9DyGM1w:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=z172eFW7tQg:GOnJ9DyGM1w:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=z172eFW7tQg:GOnJ9DyGM1w:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=z172eFW7tQg:GOnJ9DyGM1w:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=z172eFW7tQg:GOnJ9DyGM1w:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ioncannon/~4/z172eFW7tQg" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/uncategorized/963/parsing-the-sxsw-twitter-stream-for-fun/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.ioncannon.net/uncategorized/963/parsing-the-sxsw-twitter-stream-for-fun/</feedburner:origLink></item>
		<item>
		<title>Using Daemon-Kit and RobustThread to Build Ruby Daemons</title>
		<link>http://feedproxy.google.com/~r/ioncannon/~3/Sfib2lYU11c/</link>
		<comments>http://www.ioncannon.net/programming/941/using-daemon-kit-and-robustthread-to-build-ruby-daemons/#comments</comments>
		<pubDate>Tue, 02 Mar 2010 10:30:22 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[daemon]]></category>
		<category><![CDATA[daemon-kit]]></category>
		<category><![CDATA[rabbitmq]]></category>
		<category><![CDATA[robustthread]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=941</guid>
		<description><![CDATA[On a number of occasions I have found myself needing to assemble a daemon process for some type or processing done using Ruby. Each time I roll things a little different and I finally started to wonder if someone had already put together tools for doing the daemon parts. After some quick digging I ran [...]]]></description>
			<content:encoded><![CDATA[<p>On a number of occasions I have found myself needing to assemble a daemon process for some type or processing done using Ruby. Each time I roll things a little different and I finally started to wonder if someone had already put together tools for doing the daemon parts. After some quick digging I ran into <a href="http://github.com/kennethkalmer/daemon-kit">Daemon-Kit</a> and after adding it together with a couple other tools it seems like what I&#039;ve needed. I&#039;ve put together a few recipes here to help guide others who might be looking for something similar.</p>
<p><span id="more-941"></span></p>
<ol>
<li><a href="#simple-daemon">A simple daemon</a></li>
<li><a href="#evented-daemon">An event driven daemon with RabbitMQ</a></li>
<li><a href="#other">RobustThread, timeouts, monit/god, capistrano</a></li>
<li><a href="#ruby-daemon-options">Other Ruby daemon options</a></li>
</ol>
<p><b><a name="simple-daemon">A simple daemon</a></b></p>
<p>Creating a simple daemon is easy. This is a simple walk through so look at the project&#039;s documentation for much more information. First you will need to get the gem installed:</p>
<pre class="brush: bash;">
gem install daemon-kit
</pre>
<p>After the gem is installed you create a daemon with the following command:</p>
<pre class="brush: bash;">
daemon-kit asimple
</pre>
<p>Now you have some generated code in the asimple directory. In that directory you will find a README that tells you want each directory is used for. The main file to look at for this example is:</p>
<pre class="brush: bash;">
libexec/asimple-daemon.rb
</pre>
<p>A fresh file will have a lot of comments in it pointing you in the right direction. Here is a modified version where I&#039;m displaying the contents of /tmp over and over:</p>
<pre class="brush: ruby;">
DaemonKit::Application.running! do |config|
end

loop do
  DaemonKit.logger.info Dir.entries('/tmp/').join(' ')
  sleep 5
end
</pre>
<p>To run the daemon you would use a command like the following:</p>
<pre class="brush: bash;">
RUBYOPT=rubygems ./bin/asimple
</pre>
<p>The documentation found in the project is great so look there for more information. There is a lot of flexibility built in like multiple environment support and command line options. The <a href="http://www.elctech.com/articles/ruby-daemons-and-angels">daemons and angels</a> blog post is educational and worth a read for more on the basics of daemon-kit.</p>
<p>After experimenting for a while I think I may want to go back and redo the <a href="http://www.ioncannon.net/projects/http-live-video-stream-segmenter-and-distributor/">live segmenter</a> so that it uses daemon-kit instead of my hand rolled process. That is probably a good example of where the simple functionality comes in.</p>
<p><b><a name="evented-daemon">An event driven daemon with RabbitMQ</a></b></p>
<p>What tilted me towards looking into Ruby daemon options was actually another event driven process I was thinking about. As it turns out daemon-kit supports creating evented daemons as well. I picked RabbitMQ for the following example but anything that supports <a href="http://www.amqp.org/confluence/display/AMQP/Advanced+Message+Queuing+Protocol">AMQP</a> should work. I&#039;m going to assume you have RabbitMQ installed already.</p>
<p>First off you need to install the <a href="http://github.com/tmm1/amqp">AMQP gem</a>:</p>
<pre class="brush: bash;">
gem install eventmachine
gem install amqp
</pre>
<p>At this point you might want to verify that you can connect to your MQ server using some <a href="http://github.com/ninjaconcept/amqp_examples">AMQP examples</a>. </p>
<p>To generate an event based daemon use the following command:</p>
<pre class="brush: bash;">
daemon-kit -i amqp eventbased
</pre>
<p>For some reason the generated code didn&#039;t work right out of the box for me but a slight modification fixed the issue. I added the following to the top of the config/environment.rb file:</p>
<pre class="brush: ruby;">
require 'amqp'
require 'mq'
</pre>
<p>Again the file generated in libexec is the starting point and gives you good hints as to where you want to start. I&#039;ve modified my example above to take a directory to gather the contents of and respond with those contents as another message:</p>
<pre class="brush: ruby;">
DaemonKit::Application.running! do |config|
end

DaemonKit::AMQP.run do
  amq = ::MQ.new

  amq.queue('dir_request').subscribe(:ack =&gt; true) do |h, msg|
      h.ack # just ack the message

      DaemonKit.logger.debug &quot;Received message: #{msg.inspect}&quot;

      amq.queue('dir_response').publish(Dir.entries(msg).join(' '))
  end
end
</pre>
<p>One thing to note is that I&#039;ve set the message queue to require an acknowledgement for each message. I did this to simulate a process that requires that each request be executed once. Now all that is needed is a simple application to produce request messages and display the response:</p>
<pre class="brush: ruby;">
require 'rubygems'
require 'amqp'
require 'mq'

AMQP.start(:vhost=&gt;&quot;/&quot;, :host=&gt;&quot;localhost&quot;, :user=&gt;&quot;guest&quot;, :pass=&gt;&quot;guest&quot;) do

  def produce_periodic_requests
    mq = MQ.new
    EM.add_periodic_timer(1) do
      puts &quot;Creating request...&quot;
      mq.queue('dir_request').publish(&quot;/tmp/&quot;)
    end
  end

  def watch_for_response
    mq = MQ.new
    mq.queue('dir_response').subscribe(:ack =&gt; true) do |h, msg|
      puts &quot;Got response: #{msg}&quot;
      h.ack
    end
  end

  produce_periodic_requests
  watch_for_response
end
</pre>
<p>I&#039;m assuming that the MQ server that is being used has the user guest set up with the password guest, this is the default in the default that is generated by daemon-kit in the config/amqp.yml as well. At this point you should be able to execute both the daemon process and the producer and see the output on each end as the requests are completed. Note that the messages that come in are processed one at a time and this may not be optimal, you would probably want to spawn a new thread for each message or use a thread pool so that multiple messages could process at the same time.</p>
<p><b><a name="other">RobustThread, timeouts, monit/god, and capistrano</a></b></p>
<p>The above gets you a long way but there are a number of open ends that need to be tied up. The first of those is what happens if the requested action fails? In the above evented example if the directory request fails the entire daemon will end and that isn&#039;t going to be good. The first step to fixing this is to use <a href="http://github.com/JaredKuolt/robustthread">RobustThread</a>. Wrapping the action with RobustThread will make sure any exceptions thrown don&#039;t cause everything to stop.</p>
<p>Next you may have tasks that end up never completing for some reason. In this case you will want to use a timeout to kill the processing and move along. This actually works well with RobustThread since it can catch TimeoutErrors that get raised after a certain amount of time. Here is a modified version of the evented daemon above that takes two parameters in the incoming message to give both RobustThread and timeouts a test:</p>
<pre class="brush: ruby;">
require 'robustthread'

DaemonKit::Application.running! do |config|
  RobustThread.logger = DaemonKit.logger
end

DaemonKit::AMQP.run do
  AMQP.conn.connection_status do |status|
    DaemonKit.logger.debug(&quot;AMQP connection status changed: #{status}&quot;)
    if status == :disconnected
      AMQP.conn.reconnect(true)
    end
  end

  amq = ::MQ.new

  RobustThread.exception_handler do |exception|
    RobustThread.logger.debug &quot;Exception: #{exception.inspect}&quot;
    amq.queue('dir_response').publish(&quot;Exception: #{exception.inspect}&quot;)
  end

  amq.queue('dir_request').subscribe(:ack =&gt; true) do |h, msg|
    rt = RobustThread.new(:args =&gt; [h, msg, amq], :label =&gt; &quot;message processing&quot;) do |h, msg, amq|
      h.ack # in this case we always want to just ack the message

      DaemonKit.logger.debug &quot;Received message: #{msg.inspect}&quot;
      dir_name, sleep_time = msg.split(&quot;,&quot;)

       # give the request 5 seconds to complete before ending it
      Timeout.timeout(5) do
        sleep sleep_time.to_i
        amq.queue('dir_response').publish(Dir.entries(dir_name).join(' '))
      end
    end
  end
end
</pre>
<p>A modified version of the producer is also needed:</p>
<pre class="brush: ruby;">
require 'rubygems'
require 'amqp'
require 'mq'

AMQP.start(:vhost=&gt;&quot;/&quot;, :host=&gt;&quot;localhost&quot;, :user=&gt;&quot;guest&quot;, :pass=&gt;&quot;guest&quot;) do

  def produce_periodic_requests
    mq = MQ.new
    EM.add_periodic_timer(1) do
      puts &quot;Creating request...&quot;
      mq.queue('dir_request').publish(&quot;/tmp/,1&quot;) # directory to list,seconds to sleep before listing
    end
  end

  def watch_for_response
    mq = MQ.new
    mq.queue('dir_response').subscribe(:ack =&gt; true) do |h, msg|
      puts &quot;Got response: #{msg}&quot;
      h.ack
    end
  end

  produce_periodic_requests
  watch_for_response
end
</pre>
<p>To see RobustThread in action just modify the request so it asks for a directory listing of a directory that doesn&#039;t exist. To see the timeout handling in action change the number after the directory to a value greater than 5. One thing to keep in mind with this example is that every request will spawn a new thread and if you have a large number of requests at one time you may end up starving your threads so that none of them can finish. A more complete implementation of this example would have some type of limit on the number of threads that would spawn at one time or use some type of thread pool (the Servolux project listed later has such a thread pool).</p>
<p>Now you have a fairly sturdy daemon process but there is still some possibility that what gets processed causes Ruby itself to die. In this case daemon-kit also has support for <a href="http://mmonit.com/monit/">Monit</a> and <a href="http://god.rubyforge.org/">God</a> that can both restart the daemon if needed. This is probably also a good place to mention that instead of using threads it may be better to use processes and something like the process worker pool from <a href="http://github.com/TwP/servolux">Servolux</a> to provide the ability to withstand process crashes.</p>
<p>On top of all that there is also support for <a href="http://www.capify.org/">Capistrano</a> deployments.</p>
<p><b><a name="ruby-daemon-options">Other Ruby daemon options</a></b></p>
<p>After building a the above examples and some more digging I ran into a few other options that are worth mentioning. I haven&#039;t looked into any of these in detail so I may be overlooking features and I also believe that these all fall outside of the simple case where all you want is a long running process. The first few are <a href="http://backgroundrb.rubyforge.org/">BackgrounDRB</a>, <a href="http://kr.github.com/beanstalkd/">Beanstalkd</a> and <a href="http://github.com/TwP/servolux">Servolux</a>. Another option I will mention is <a href="http://github.com/defunkt/resque">Resque</a> that was <a href="http://github.com/blog/542-introducing-resque">released by github</a> and lives on top of <a href="http://code.google.com/p/redis/">Redis</a>. From an initial look it seems like Resque may be a better option for situations where evented tasks being executed may crash the entire process and also has a lot of other features right out of the box. Out of these I&#039;m probably going to investigate Resque more to see how it survives with workers that may crash.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ioncannon?a=Sfib2lYU11c:I160hbry5Ck:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=Sfib2lYU11c:I160hbry5Ck:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=Sfib2lYU11c:I160hbry5Ck:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=Sfib2lYU11c:I160hbry5Ck:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=Sfib2lYU11c:I160hbry5Ck:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=Sfib2lYU11c:I160hbry5Ck:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ioncannon/~4/Sfib2lYU11c" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/941/using-daemon-kit-and-robustthread-to-build-ruby-daemons/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.ioncannon.net/programming/941/using-daemon-kit-and-robustthread-to-build-ruby-daemons/</feedburner:origLink></item>
		<item>
		<title>Building HipHop PHP for Fedora 12 on 64 bit and 32 bit Systems</title>
		<link>http://feedproxy.google.com/~r/ioncannon/~3/-UhuvuJz6wc/</link>
		<comments>http://www.ioncannon.net/programming/918/building-hiphop-php-for-fedora-12-on-64-bit-and-32-bit-systems/#comments</comments>
		<pubDate>Tue, 23 Feb 2010 11:14:04 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[ec2]]></category>
		<category><![CDATA[Fedora]]></category>
		<category><![CDATA[hphp]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=918</guid>
		<description><![CDATA[Now that Facebook has finally released the source for HipHop PHP it is time to give it a spin. Of course it is still a little rough around the edges so I figured I would toss together a quick howto on getting it to build.
The first thing to note is that they are only supporting [...]]]></description>
			<content:encoded><![CDATA[<p>Now that Facebook has finally released the source for <a href="http://github.com/facebook/hiphop-php/">HipHop PHP</a> it is time to give it a spin. Of course it is still a little rough around the edges so I figured I would toss together a quick howto on getting it to build.</p>
<p>The first thing to note is that they are only supporting 64 bit systems officially. Having said that it isn&#039;t too hard to modify the code to make it work on a 32 bit system although it may turn out that such early modifications are missing some fundamental bits on why they were only support 64 bit systems. I&#039;m going to assume at first that you are using a 64 bit system and then end with what you need if you are still using a 32 bit system.</p>
<p><span id="more-918"></span></p>
<p>I don&#039;t actually have a 64 bit system myself so I used an EC2 instance for the following instructions. To do the same start with Amazon&#039;s Basic 64-bit Fedora Core 8 (AMI Id: ami-86db39ef) instance (note that this is EBS backed so you will end up with an EBS volume after you start it) and then upgrade to Fedora 12 using my <a href="http://www.ioncannon.net/system-administration/894/fedora-12-bootable-root-ebs-on-ec2/">previous instructions on building a EBS bootable Fedora 12 instance</a>. You will need to remove a few packages to get the 64 bit version of Fedora 8 to upgrade that I didn&#039;t have to do for the 32 bit version, here are all the commands you need to get to a running 64 bit Fedora 12 instance (the entire upgrade takes about 20 minutes):</p>
<pre class="brush: bash;">
# Fedora 8 to Fedora 10
yum -y remove dmraid-1.0.0.rc14-4.fc8.i386 dmraid-1.0.0.rc14-4.fc8.i386 curl-7.18.2-7.fc8.i386
yum clean all
rpm -Uhv http://archive.kernel.org/fedora-archive/releases/10/Fedora/i386/os/Packages/fedora-release-10-1.noarch.rpm http://archive.kernel.org/fedora-archive/releases/10/Fedora/i386/os/Packages/fedora-release-notes-10.0.0-1.noarch.rpm
yum -y update

# Fedora 10 to Fedora 11
yum -y remove gpm-1.20.5-2.fc10.i386
yum clean all
rpm -Uvh http://mirrors.usc.edu/pub/linux/distributions/fedora/linux/releases/11/Fedora/i386/os/Packages/fedora-release-11-1.noarch.rpm http://mirrors.usc.edu/pub/linux/distributions/fedora/linux/releases/11/Fedora/i386/os/Packages/fedora-release-notes-11.0.0-2.fc11.noarch.rpm
yum -y update

# Fedora 11 to Fedora 12
yum -y remove cryptsetup-luks-1.0.6-7.fc11.i586
yum clean all
rpm -Uvh http://mirrors.kernel.org/fedora/releases/12/Fedora/i386/os/Packages/fedora-release-notes-12.0.0-4.fc12.noarch.rpm http://mirrors.kernel.org/fedora/releases/12/Fedora/i386/os/Packages/fedora-release-12-1.noarch.rpm
yum -y update

# Make sure the basics are installed
yum -y install gcc-c++ git
</pre>
<p>To start with there are some prerequisites you need. This can be taken care of in one command with yum:</p>
<pre class="brush: bash;">
yum -y install git cmake boost pcre-devel libicu-devel libmcrypt-devel oniguruma-devel mysql-devel gd-devel boost-devel libxml2-devel libcap-devel binutils-devel flex bison expat-devel
</pre>
<p>Next create a directory to hold everything in, change into that directory and create another directory to hold the customized libraries needed to compile HipHop PHP:</p>
<pre class="brush: bash;">
mkdir hiphop
cd hiphop
mkdir local
</pre>
<p>Next it is time to pull down the HipHop PHP source along with the source for some libraries it depends on (these all go into the hiphop directory created above):</p>
<pre class="brush: bash;">
git clone git://github.com/facebook/hiphop-php.git

wget &quot;http://downloads.sourceforge.net/project/re2c/re2c/0.13.5/re2c-0.13.5.tar.gz?use_mirror=cdnetworks-us-2&quot;
wget &quot;http://www.threadingbuildingblocks.org/uploads/77/142/2.2/tbb22_20090809oss_src.tgz&quot;
wget http://curl.haxx.se/download/curl-7.20.0.tar.bz2
wget http://www.monkey.org/~provos/libevent-1.4.13-stable.tar.gz

tar xvjf curl-7.20.0.tar.bz2
tar xvzf libevent-1.4.13-stable.tar.gz
tar xvzf re2c-0.13.5.tar.gz
tar xvzf tbb22_20090809oss_src.tgz
</pre>
<p>Next the customized patches get applied to some of the library sources and each is built to install in the custom directory:</p>
<pre class="brush: bash;">
export CMAKE_PREFIX_PATH=`pwd`/local

cd tbb22_20090809oss
gmake
cp -Rp include/tbb/ /usr/include/
cp `pwd`/build/*_release/*.so /usr/lib/
ldconfig
cd ..

cd re2c-0.13.5
./configure --prefix=`pwd`/../local
make install
cd ..

cd libevent-1.4.13-stable
cp ../hiphop-php/src/third_party/libevent.fb-changes.diff .
patch &lt; libevent.fb-changes.diff
./configure --prefix=`pwd`/../local
make install
cd ..

cd curl-7.20.0
cp ../hiphop-php/src/third_party/libcurl.fb-changes.diff .
patch -p0 &lt; libcurl.fb-changes.diff
./configure --prefix=`pwd`/../local
make install
cd ..
</pre>
<p>There is one problem at this point that requires a little surgery on the HipHop PHP source itself. There is more about this in <a href="http://github.com/facebook/hiphop-php/issues#issue/6">issue #6</a> and once it gets fixed this won&#039;t need to be done. </p>
<pre class="brush: bash;">
cd hiphop-php
echo &quot;#ifndef LHASH&quot; &gt;&gt; src/cpp/ext/ext_openssl.h
echo &quot;#define LHASH LHASH_OF(CONF_VALUE)&quot; &gt;&gt; src/cpp/ext/ext_openssl.h
echo &quot;#endif&quot; &gt;&gt; src/cpp/ext/ext_openssl.h
</pre>
<p>And at last it is time to compile HipHop PHP itself:</p>
<pre class="brush: bash;">
git submodule init
git submodule update
export HPHP_HOME=`pwd`
export HPHP_LIB=`pwd`/bin
cmake .
make
</pre>
<p>It takes about 20 minutes to compile everything. Once the compile is done you are ready to roll. Check out the <a href="http://wiki.github.com/facebook/hiphop-php/running-hiphop">running HipHop wiki page</a> to learn how to run the resulting binary. One important thing to note is that you need to make sure you have the correct environment variables set when you go to compile things. I created a little file I can source with the following in it:</p>
<pre class="brush: bash;">
export HPHP_BASE=&lt;path to the first directory&gt;
export CMAKE_PREFIX_PATH=$HPHP_BASE/local
export HPHP_HOME=$HPHP_BASE/hiphop-php
export HPHP_LIB=$HPHP_HOME/bin
</pre>
<p>For those who just want it to go I&#039;ve put all of the above into one script that can be found <a href="/examples/hiphopgo.sh">here</a>. If you are going from Fedora 8 to Fedora 12 on an EC2 node you can get a script for that <a href="/examples/64bitfedora12ec2.sh">here</a>.</p>
<p>Now if you want to do this on a 32 bit Fedora 12 install you will need to modify the source first. The easiest way I know of doing this is to look at <a href="http://github.com/carsonmcdonald/hiphop-php/commit/792a37cb10514178341877c1425e2f3884898645">this commit log</a> or clone my version that can be found here:</p>
<pre class="brush: bash;">
git clone git://github.com/carsonmcdonald/hiphop-php.git
</pre>
<p>Please note that my version my not be up to date and the modifications to get the source to build on the 32 bit system may not be 100% correct. My goal was to get it to build and run on a 32 bit system but I don&#039;t have the time to very much more than that.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ioncannon?a=-UhuvuJz6wc:z-EDxs7Yans:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=-UhuvuJz6wc:z-EDxs7Yans:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=-UhuvuJz6wc:z-EDxs7Yans:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=-UhuvuJz6wc:z-EDxs7Yans:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=-UhuvuJz6wc:z-EDxs7Yans:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=-UhuvuJz6wc:z-EDxs7Yans:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ioncannon/~4/-UhuvuJz6wc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/918/building-hiphop-php-for-fedora-12-on-64-bit-and-32-bit-systems/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://www.ioncannon.net/programming/918/building-hiphop-php-for-fedora-12-on-64-bit-and-32-bit-systems/</feedburner:origLink></item>
		<item>
		<title>Fedora 12 Bootable Root EBS on EC2</title>
		<link>http://feedproxy.google.com/~r/ioncannon/~3/4jwyf9PhaUw/</link>
		<comments>http://www.ioncannon.net/system-administration/894/fedora-12-bootable-root-ebs-on-ec2/#comments</comments>
		<pubDate>Tue, 16 Feb 2010 12:30:13 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[system administration]]></category>
		<category><![CDATA[EBS]]></category>
		<category><![CDATA[ec2]]></category>
		<category><![CDATA[Fedora]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=894</guid>
		<description><![CDATA[I recently needed to create a clean EC2 AMI using a fairly new linux distro. It has been a while since I&#039;ve needed to create a new AMI so I also wanted to move away from the older pre-packaged AMI and boot using EBS. After taking a look at what was currently available publicly I [...]]]></description>
			<content:encoded><![CDATA[<p>I recently needed to create a clean EC2 AMI using a fairly new linux distro. It has been a while since I&#039;ve needed to create a new AMI so I also wanted to move away from the older pre-packaged AMI and boot using EBS. After taking a look at what was currently available publicly I decided I would just create my own EBS bootable AMI using Fedora 12. It wasn&#039;t all that complicated but there are a decent number of steps so I figured I would document them for anyone else who might want to give it a try.</p>
<p>I&#039;m going to assume you already understand how to do things like create instances, create EBS volumes and ssh into your running instance using key based authentication. I use the AWS management console for a lot of what follows with the exception of needing to register the AMI and for that you will need the <a href="http://developer.amazonwebservices.com/connect/entry.jspa?externalID=351">Amazon EC2 API Tools</a> and <a href="http://developer.amazonwebservices.com/connect/entry.jspa?externalID=368">Amazon EC2 AMI Tools</a> </p>
<p><span id="more-894"></span></p>
<p>There are two ways to get to a bootable EBS backed Fedora 12 instance and they start off the same. The first thing to do is fire up the AMI named &#034;Basic Fedora Core 8 (AMI Id: ami-84db39ed)&#034; that is provided by Amazon.</p>
<p>Once the Fedora Core 8 EC2 instance is ready ssh into it. Fedora 12 requries a newer version of RPM to install so you now need to upgrade the instance to Fedora 10. This is pretty easy and can be done by following my instructions on <a href="http://www.ioncannon.net/system-administration/142/upgrading-from-fedora-9-to-fedora-10-with-yum/">upgrading from Fedora 9 to Fedora 10</a> (don&#039;t worry about skipping 9 it will work). Here are the commands needed to do the upgrade:</p>
<pre class="brush: bash;">
yum clean all
rpm -Uhv http://archive.kernel.org/fedora-archive/releases/10/Fedora/i386/os/Packages/fedora-release-10-1.noarch.rpm http://archive.kernel.org/fedora-archive/releases/10/Fedora/i386/os/Packages/fedora-release-notes-10.0.0-1.noarch.rpm
yum -y update
</pre>
<p>After a few minutes the instance will be upgraded and ready for the next step. This is where the two paths diverge depending on how you want the final product constructed. The options are to install Fedora 12 on a freshly minted volume or continue upgrading the instance you just created.</p>
<p><b>Upgrade path</b></p>
<p>I will start with the upgrade path since that is probably the easier of the two although may leave you with a messier instances after it is done. The next step for the upgrade path is to do what I outline in <a href="http://www.ioncannon.net/system-administration/358/upgrading-to-fedora-11-from-fedora-10/">upgrading from Fedora 10 to Fedora 11</a> and <a href="http://www.ioncannon.net/system-administration/719/upgrade-to-fedora-12-from-fedora-11/">upgrading from Fedora 11 to Fedora 12</a>. Here are the commands all in one place to make it easy:</p>
<pre class="brush: bash;">
yum clean all
rpm -Uvh http://mirrors.usc.edu/pub/linux/distributions/fedora/linux/releases/11/Fedora/i386/os/Packages/fedora-release-11-1.noarch.rpm http://mirrors.usc.edu/pub/linux/distributions/fedora/linux/releases/11/Fedora/i386/os/Packages/fedora-release-notes-11.0.0-2.fc11.noarch.rpm
yum -y update
yum clean all
rpm -Uvh http://mirrors.kernel.org/fedora/releases/12/Fedora/i386/os/Packages/fedora-release-notes-12.0.0-4.fc12.noarch.rpm http://mirrors.kernel.org/fedora/releases/12/Fedora/i386/os/Packages/fedora-release-12-1.noarch.rpm
yum -y update
</pre>
<p>Once you have everything upgraded to Fedora 12 you will have a 15G root partition that has less than 2G used. This may not suite your needs very well if you really don&#039;t need that extra 13G but thankfully if you want to shrink the root EBS partition you can. </p>
<p>I found some instructions in this article on <a href="http://www.elastician.com/2009/12/creating-ebs-backed-ami-from-s3-backed.html">EBS backed AMI</a>s that describes using the following command to copy the entire file system over. Assuming you have created a smaller volume and attached it to the instance as sdh you should be able to do something like the following to copy everything to the new volume:</p>
<pre class="brush: bash;">
mkfs.ext3 /dev/sdh
mount /dev/sdh /mnt
tar cpS / | cpipe -vt -b 1024 | gzip -c | tar zxpS -C /mnt
rm -rf /mnt/mnt/*
rm -rf /mnt/proc/*
umount /mnt
</pre>
<p>One thing to note in the above is that the entire sdh drive is formatted for the file system (you will actually get a prompt asking if that is ok). As far as I can tell this is the way it has to be or the instance will not boot correctly. I assume this is because the root device is hidden behind a partition already as /dev/sda1 and so shouldn&#039;t have a second partition table.</p>
<p>Skip to the common part now to learn how to make the final bootable AMI.</p>
<p><b>From scratch path</b></p>
<p>This path is similar to and mostly an update/extension to my post on creating a <a href="http://www.ioncannon.net/system-administration/128/how-to-create-a-fedora-7-instance-for-ec2/">Fedora 7 AMI setup</a>. I&#039;m going to leave out most of the details and just provide you with a script that will take an empty volume (assumed to be attached as /dev/sdh) and turn it into a bootable EBS backed Fedora 12 volume. Download the script <a href="http://www.ioncannon.net/examples/createfedora12bootebs.sh">createfedora12bootebs.sh</a> instead of trying to cut and paste the following, it gets formatted in such a way as to lose a newline that is important. Please note that you will need at least 1G of space on the given volume.</p>
<pre class="brush: bash;">
#!/bin/sh

echo &quot;y&quot; | mkfs.ext3 /dev/sdh
mount /dev/sdh /mnt

mkdir /mnt/dev
mkdir /mnt/proc
mkdir /mnt/etc

for i in console null zero ; do /sbin/MAKEDEV -d /mnt/dev -x $i ; done

cat &lt;&lt;EOL &gt; /mnt/etc/fstab
/dev/sda1               /                       ext3    defaults 1 1
none                    /dev/pts                devpts  gid=5,mode=620 0 0
none                    /dev/shm                tmpfs   defaults 0 0
none                    /proc                   proc    defaults 0 0
none                    /sys                    sysfs   defaults 0 0
/dev/sdc1               /mnt                    ext3    defaults 0 0
/dev/sdc2               swap                    swap    defaults 0 0
EOL

mount -t proc none /mnt/proc

cat &lt;&lt;EOL &gt; /tmp/yumec2.conf
[main]
cachedir=/var/cache/yum
debuglevel=2
logfile=/var/log/yum.log
exclude=*-debuginfo
gpgcheck=0
obsoletes=1
reposdir=/dev/null

[base]
name=Fedora 12 – i386 – Base
mirrorlist=http://mirrors.fedoraproject.org/mirrorlist?repo=fedora-12&amp;arch=i386
enabled=1

[updates-released]
name=Fedora 12 – i386 – Released Updates
mirrorlist=http://mirrors.fedoraproject.org/mirrorlist?repo=updates-released-f12&amp;arch=i386
enabled=1
EOL

yum -c /tmp/yumec2.conf --installroot=/mnt -y groupinstall Base
yum -c /tmp/yumec2.conf --installroot=/mnt -y install openssh-server

yum -c /tmp/yumec2.conf --installroot=/mnt -y clean packages

echo &quot;UseDNS no&quot; &gt;&gt; /mnt/etc/ssh/sshd_config
echo &quot;PermitRootLogin without-password&quot; &gt;&gt; /mnt/etc/ssh/sshd_config

cp /etc/rc.local /mnt/etc/
cp /etc/sysconfig/network /mnt/etc/sysconfig/network
cp /etc/sysconfig/network-scripts/ifcfg-eth0 /mnt/etc/sysconfig/network-scripts/ifcfg-eth0
cp /usr/local/sbin/* /mnt/usr/local/sbin/
cp -Rp /lib/modules/2.6.21.7-2.fc8xen/ /mnt/lib/modules/

echo &quot;/sbin/MAKEDEV /dev/urandom&quot; &gt;&gt; /mnt/etc/rc.sysinit
echo &quot;/sbin/MAKEDEV /dev/random&quot; &gt;&gt; /mnt/etc/rc.sysinit
echo &quot;/sbin/MAKEDEV /dev/sdc&quot; &gt;&gt; /mnt/etc/rc.sysinit
echo &quot;/sbin/MAKEDEV /dev/sdc1&quot; &gt;&gt; /mnt/etc/rc.sysinit
echo &quot;/sbin/MAKEDEV /dev/sdc2&quot; &gt;&gt; /mnt/etc/rc.sysinit

cat &lt;&lt;EOF &gt;&gt; /mnt/etc/rc.sysinit
# The following will partition the local drive and set up swap
cat &lt;&lt;EOL | fdisk /dev/sdc
n
p
1
1
+140G
n
p
2

w
EOL
mkswap /dev/sdc2
EOF

mv /mnt/lib/tls /mnt/lib/tls.disabled
echo &quot;hwcap 0 nosegneg&quot; &gt;&gt; /mnt/etc/ld.so.conf.d/kernelcap-2.6.21.7-2.fc8.conf

chroot /mnt chkconfig --level 2345 NetworkManager off
chroot /mnt chkconfig --level 2345 network on

sync
umount /mnt/proc
umount /mnt
</pre>
<p><b>Common wrap up</b></p>
<p>At this point you will need to create a snapshot of the volume that was created for one of the paths above. Once the snapshot is available you will need to then register the snapshot as an AMI that is bootable from EBS. To do that you would issue something like the following command substituting the correct data in where it relates to your volume and snapshot.</p>
<pre class="brush: bash;">
ec2-register -n &quot;AMIName&quot; -d &quot;AMI Description&quot; --block-device-mapping /dev/sdc=ephemeral0 --snapshot your-snapname --architecture i386 --kernel aki-a71cf9ce --ramdisk ari-a51cf9cc
</pre>
<p>One thing to note in this command is the &#8211;block-device-mapping option. That option is what gives you access to the local drive on your node once it is booted. This gives you extra storage for things you don&#039;t need to keep after the life of the running node. In the from scratch option I&#039;m turning part of the local drive into swap as well as creating a partition that could be used as a large temporary storage. If you want to know more details on the ephermeral storage look at <a href="http://developer.amazonwebservices.com/connect/thread.jspa?messageID=155916">this post</a></p>
<p>After all that you should have a bootable EBS backed Fedora 12 install to work with.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ioncannon?a=4jwyf9PhaUw:WzJWSeexfX8:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=4jwyf9PhaUw:WzJWSeexfX8:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=4jwyf9PhaUw:WzJWSeexfX8:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=4jwyf9PhaUw:WzJWSeexfX8:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=4jwyf9PhaUw:WzJWSeexfX8:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=4jwyf9PhaUw:WzJWSeexfX8:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ioncannon/~4/4jwyf9PhaUw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/system-administration/894/fedora-12-bootable-root-ebs-on-ec2/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		<feedburner:origLink>http://www.ioncannon.net/system-administration/894/fedora-12-bootable-root-ebs-on-ec2/</feedburner:origLink></item>
		<item>
		<title>PHP MySQLi and Multiple Prepared Statements</title>
		<link>http://feedproxy.google.com/~r/ioncannon/~3/-6IKcD9R58M/</link>
		<comments>http://www.ioncannon.net/programming/889/php-mysqli-and-multiple-prepared-statements/#comments</comments>
		<pubDate>Tue, 09 Feb 2010 11:58:44 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[mysqli]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=889</guid>
		<description><![CDATA[While sprucing up the PHP code I use to provide my own Stack Overflow API for GeeQe I ran into an error caused by trying to use multiple prepared statements with MySQLi. It turned up when I tried to execute one prepared statement while looping over the result set from another prepared statement that were [...]]]></description>
			<content:encoded><![CDATA[<p>While sprucing up the PHP code I use to provide my own Stack Overflow API for <a href="http://www.ioncannon.net/projects/geeqe/">GeeQe</a> I ran into an error caused by trying to use multiple prepared statements with <a href="http://www.php.net/manual/en/book.mysqli.php">MySQLi</a>. It turned up when I tried to execute one prepared statement while looping over the result set from another prepared statement that were both created on the same connection. What came out was the following error:</p>
<p>&#034;Commands out of sync; you can&#039;t run this command now&#034;</p>
<p>Details about this error can be found in the <a href="http://dev.mysql.com/doc/refman/5.0/en/commands-out-of-sync.html">mysql docs</a>. Reading those details makes it clear that the result sets of a prepared statement execution need to be fetched completely before executing another prepared statement on the same connection.</p>
<p><span id="more-889"></span></p>
<p>Fixing the issue can be accomplished by using the <a href="http://php.net/manual/en/mysqli-stmt.store-result.php">store result</a> call. Here is an example of what I initially was trying to do:</p>
<pre class="brush: php;">
&lt;?php

  $db_connection = new mysqli('127.0.0.1', 'user', '', 'test');

  $post_stmt = $db_connection-&gt;prepare(&quot;select id, title from post where id = 1000&quot;);
  $comment_stmt = $db_connection-&gt;prepare(&quot;select user_id from comment where post_id = ?&quot;);

  if ($post_stmt-&gt;execute())
  {
    $post_stmt-&gt;bind_result($post_id, $title);

    if ($post_stmt-&gt;fetch())
    {
      $comments = array();

      $comment_stmt-&gt;bind_param('i', $post_id);
      if ($comment_stmt-&gt;execute())
      {
        $comment_stmt-&gt;bind_result($user_id);
        while ($comment_stmt-&gt;fetch())
        {
          array_push($comments, array('user_id' =&gt; $user_id));
        }
      }
      else
      {
        printf(&quot;Comment statement error: %s\n&quot;, $comment_stmt-&gt;error);
      }
    }
  }
  else
  {
    printf(&quot;Post statement error: %s\n&quot;, $post_stmt-&gt;error);
  }

  $post_stmt-&gt;close();
  $comment_stmt-&gt;close();

  $db_connection-&gt;close();

  printf(&quot;ID: %d -&gt; %s\n&quot;, $post_id, $post_title);
  print_r($comments);
?&gt;
</pre>
<p>The above will result in the following error:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">Comment statement error: Commands out of sync; you can&#039;t run this command now<br />
PHP Notice: &nbsp;Undefined variable: post_title in error.php on line 41<br />
ID: 9033 -&amp;gt; <br />
Array<br />
(<br />
)</div>
</div>
<p>Here is what needs to be done to make it work correctly:</p>
<pre class="brush: php;">
&lt;?php

  $db_connection = new mysqli('127.0.0.1', 'user', '', 'test');

  $post_stmt = $db_connection-&gt;prepare(&quot;select id, title from post where id = 1000&quot;);
  $comment_stmt = $db_connection-&gt;prepare(&quot;select user_id from comment where post_id = ?&quot;);

  if ($post_stmt-&gt;execute())
  {
    $post_stmt-&gt;store_result();
    $post_stmt-&gt;bind_result($post_id, $title);

    if ($post_stmt-&gt;fetch())
    {
      $comments = array();

      $comment_stmt-&gt;bind_param('i', $post_id);
      if ($comment_stmt-&gt;execute())
      {
        $comment_stmt-&gt;bind_result($user_id);
        while ($comment_stmt-&gt;fetch())
        {
          array_push($comments, array('user_id' =&gt; $user_id));
        }
      }
      else
      {
        printf(&quot;Comment statement error: %s\n&quot;, $comment_stmt-&gt;error);
      }
    }

    $post_stmt-&gt;free_result();
  }
  else
  {
    printf(&quot;Post statement error: %s\n&quot;, $post_stmt-&gt;error);
  }

  $post_stmt-&gt;close();
  $comment_stmt-&gt;close();

  $db_connection-&gt;close();

  printf(&quot;ID: %d -&gt; %s\n&quot;, $post_id, $post_title);
  print_r($comments);
?&gt;
</pre>
<p>A couple things to note about the above example:</p>
<ul>
<li>The bind and fetch on the statement still works correctly.</li>
<li>Make sure the results are freed when the processing is done.</li>
</ul>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ioncannon?a=-6IKcD9R58M:4ic1ZWX0OJk:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=-6IKcD9R58M:4ic1ZWX0OJk:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=-6IKcD9R58M:4ic1ZWX0OJk:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=-6IKcD9R58M:4ic1ZWX0OJk:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=-6IKcD9R58M:4ic1ZWX0OJk:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=-6IKcD9R58M:4ic1ZWX0OJk:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ioncannon/~4/-6IKcD9R58M" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/889/php-mysqli-and-multiple-prepared-statements/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://www.ioncannon.net/programming/889/php-mysqli-and-multiple-prepared-statements/</feedburner:origLink></item>
		<item>
		<title>Developing Adobe Air Apps with Linux</title>
		<link>http://feedproxy.google.com/~r/ioncannon/~3/KZyKO7euX2M/</link>
		<comments>http://www.ioncannon.net/programming/875/developing-adobe-air-apps-with-linux/#comments</comments>
		<pubDate>Tue, 12 Jan 2010 12:21:24 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[air]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[linux]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=875</guid>
		<description><![CDATA[I finally found a little project I wanted to do using Adobe Air and after some searching I found out you can use Linux to develop Air applications. At first I thought I would have to use Flex Builder which is still in alpha for Linux but it turns out there is a better option [...]]]></description>
			<content:encoded><![CDATA[<p>I finally found a little project I wanted to do using <a href="http://www.adobe.com/products/air/">Adobe Air</a> and after some searching I found out you can use Linux to develop Air applications. At first I thought I would have to use Flex Builder which is still in <a href="http://labs.adobe.com/technologies/flex/flexbuilder_linux/">alpha for Linux</a> but it turns out there is a better option from <a href="http://www.aptana.org/">Aptana</a>. </p>
<p>The <a href="http://www.aptana.org/air/">Aptana Air</a> plugin supports developing Adobe Air applications using HTML and Javascript. It even support the 2.0 release of Air that is currently in beta. Aptana uses the Eclipse framework as an editor so if you are familure with Eclipse it will be even easier to use.</p>
<p>I started by downloading and installing the latest version of the <a href="http://get.adobe.com/air/">Air runtime</a>. Next I grabbed the <a href="http://www.adobe.com/products/air/tools/sdk/">Air SDK</a>, the SDK doesn&#039;t come with the plugin so it is something you have to get directly from the Air developers site. After getting the SDK unpacked I installed the latest Aptana core release. Once the core is installed there is a big plugin button on the startup screen that currently has Air listed.</p>
<p>The install went smoothly except for a few issues. The first one I ran into was very noticeable since it kept any dialog buttons from working when they were clicked although they did work when I clicked them and then hit enter or navigated to them with the keyboard. Luckily someone has already figured out that there is an issue with <a href="http://mou.me.uk/2009/10/31/fixing-eclipse-in-ubuntu-9-10-karmic-koala/">Eclipse and GTK+</a> that is the cause (even though the post is for Ubuntu the same problem and solution worked for me on Fedora). The fix is to set the GDK_NATIVE_WINDOWS variable before running the Aptana binary:</p>
<div class="codesnip-container" >GDK_NATIVE_WINDOWS=true; AptanaStudio</div>
<p>The next thing I noticed was the application.xml descriptor that Aptana created didn&#039;t generate correctly. It needs to start with the correct xmlns or the following error will be thrown on run: &#034;invalid application descriptor: descriptor version does not match runtime version&#034;. To fix this check the version of the Air SDK by running the following command:</p>
<div class="codesnip-container" >./adt -version<br />
adt version &quot;1.5.3.9120&quot;</div>
<p>For the version of the Air SDK I downloaded the correct xmlns was http://ns.adobe.com/air/application/1.5 so I needed the following application tag:</p>
<div class="codesnip-container" >&lt;application xmlns=&quot;http://ns.adobe.com/air/application/1.5&quot;&gt;</div>
<p>Once I had that working I was able to compile and execute a demo application. I was also able to create an Air application package from within Aptana using File > Export > Adobe AIR > Adobe AIR Package. Before creating the Air package I had to create a signing certificate. Creating the certificate can be done within Aptana too but because I had not yet fixed the above button issue I created a cert on the command line with the Air SDK and then imported it. To create the Air signing certificate from the command line I used the adt command from the SDK:</p>
<div class="codesnip-container" >adt -certificate -cn SelfSigned 1024-RSA sampleCert.pfx samplePassword</div>
<p>Remember the password that gets used to generate the certificate because it will have to be used before a package is signed.</p>
<p>Finally Adobe has a lot of information on developing Air applications on their <a href="http://www.adobe.com/devnet/air/">Air devnet site</a>. The <a href="http://www.adobe.com/devnet/air/ajax/">Air ajax section</a> is especially important.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ioncannon?a=KZyKO7euX2M:txtp0SPzqEI:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=KZyKO7euX2M:txtp0SPzqEI:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=KZyKO7euX2M:txtp0SPzqEI:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=KZyKO7euX2M:txtp0SPzqEI:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=KZyKO7euX2M:txtp0SPzqEI:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=KZyKO7euX2M:txtp0SPzqEI:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ioncannon/~4/KZyKO7euX2M" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/875/developing-adobe-air-apps-with-linux/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.ioncannon.net/programming/875/developing-adobe-air-apps-with-linux/</feedburner:origLink></item>
		<item>
		<title>Heroku Tips for the Cheap</title>
		<link>http://feedproxy.google.com/~r/ioncannon/~3/73i7NeZ-808/</link>
		<comments>http://www.ioncannon.net/programming/842/heroku-tips-for-the-cheap/#comments</comments>
		<pubDate>Sun, 20 Dec 2009 10:50:14 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[heroku]]></category>
		<category><![CDATA[mongodb]]></category>
		<category><![CDATA[rack]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[tips]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=842</guid>
		<description><![CDATA[I&#039;ve been playing around with the Ruby/Rails cloud provider Heroku a little bit lately just to try it out. It is somewhat like Google App Engine or Microsoft Azure in the way it works since you bundle your application and push it out to the Heroku cloud for deployment. It is very easy to get [...]]]></description>
			<content:encoded><![CDATA[<p>I&#039;ve been playing around with the Ruby/Rails cloud provider <a href="http://www.heroku.com">Heroku</a> a little bit lately just to try it out. It is somewhat like Google App Engine or Microsoft Azure in the way it works since you bundle your application and push it out to the Heroku cloud for deployment. It is very easy to get things going but I ran into a few interesting items that I figured I would share. </p>
<p><span id="more-842"></span></p>
<p>Some of the following grew out of the requirement that you verify for a lot of the Heroku addons and verification requires a credit card. For some of these tips there are easier paths if you don&#039;t mind paying a little extra or just verifying your account.</p>
<ul>
<li><a href="#heroku-config">Configuration</a></li>
<li><a href="#heroku-cname">Set up a custom domain name</a></li>
<li><a href="#heroku-cron">Poor man&#039;s cron</a></li>
<li><a href="#heroku-gmail">Sending mail with GMail</a></li>
<li><a href="#heroku-logs">Keeping your logs</a></li>
<li><a href="#heroku-rack">Rack works too</a></li>
<li><a href="#heroku-compiled-gems">Compiled gems</a></li>
<li><a href="#heroku-dynos">Understanding dynos</a></li>
</ul>
<p><a name="heroku-config"><b>Configuration</b></a></p>
<p>For configuration <a href="http://docs.heroku.com/config-vars#local-setup">Heroku has a way</a> but for some reason it didn&#039;t seem like the best or easiest way to do things. I started off setting up a configuration file as described in this <a href="http://railscasts.com/episodes/85-yaml-configuration-file">railscast on using yaml configuration files</a>. I took a while to dig a little more and found something similar but even <a href="http://almosteffortless.com/2009/06/25/config-vars-and-heroku/">better way of doing configuration with Heroku</a>.</p>
<p><a name="heroku-cname"><b>Set up a custom domain name</b></a></p>
<p>To make a more professional looking app you probably want to have a non-Heroku base domain name. They make it very easy to set that up using a <a href="http://docs.heroku.com/custom-domains#cname-setup">CNAME DNS entry</a>.</p>
<p><a name="heroku-cron"><b>Poor man&#039;s cron</b></a></p>
<p>One of the things I ran into pretty quickly was the need to run a task once every minute. Heroku offers both <a href="http://docs.heroku.com/cron">cron</a> and <a href="http://docs.heroku.com/delayed-job">delayed job</a>. The cron jobs offered are a little limited since the finest grained execution for cron is once an hour. I believe delayed jobs could be made to run every minute but the next issue with both cron and delayed job on Heroku is that they are addons that require verification. </p>
<p>The solution for me was to create a poor man&#039;s cron. I added a controller that would execute the task and then ran wget to hit the controller from an external server. This isn&#039;t a great solution but for testing the service it worked fine. The main note on doing this is to keep in mind that you will tie up a dyno for the length of the request, see <a href="#heroku-dynos">Understanding dynos</a> for more.</p>
<p><a name="heroku-gmail"><b>Sending mail with GMail</b></a></p>
<p>If you want to send email you have a number of different options on Heroku. First off it is important to note that Heroku doesn&#039;t support sending email from their systems directly but instead they support outgoing <a href="http://docs.heroku.com/smtp">SMTP</a>. Sending mail with the <a href="http://docs.heroku.com/sendgrid">Sendgrid addon</a> is probably the most flexible option but the <a href="http://docs.heroku.com/gmail-smtp">GMail SMTP</a> option is the least costly. The GMail option gives you 500 emails a day and that was plenty for my use. </p>
<p>Even though there is an addon for the GMail option you don&#039;t actually need to use it. Instead install the <a href="http://github.com/collectiveidea/action_mailer_optional_tls">action mailer optional TLS plugin</a>. Follow the readme to get it installed and configured. Then you put something like the following into your production.rb file:</p>
<pre class="brush: plain;">
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.raise_delivery_errors = true
  ActionMailer::Base.smtp_settings = {
    :tls            =&gt; true,
    :address        =&gt; 'smtp.gmail.com',
    :port           =&gt; 587,
    :domain         =&gt; 'example.come',
    :authentication =&gt; :plain,
    :user_name      =&gt; 'support@example.com',
    :password       =&gt; 'password'
  }
</pre>
<p><br/></p>
<p><a name="heroku-logs"><b>Keeping your logs</b></a></p>
<p>One downside to Heroku is that they only retain a small portion of your logs. They indicate in their docs that they only retain <a href="http://docs.heroku.com/logs-exceptions">100 lines of logs</a>. So if you want to track your log output you will need to store them outside of the service. They give two suggestions in the docs for logging but there are other ways to do something similar on your own.</p>
<p>The solution I found that seems like the easiest is to use <a href="http://blog.philburrows.com/articles/2009/09/28/rails-logging-with-mongodb/">MongoDB to log</a> since it is <a href="http://blog.mongodb.org/post/172254834/mongodb-is-fantastic-for-logging">&#034;fantastic for logging&#034;</a>. You will need to have a <a href="http://www.mongodb.org/">MongoDB</a> server available first. If you don&#039;t want to <a href="http://www.engineyard.com/blog/2009/mongodb-a-light-in-the-darkness-key-value-stores-part-5/">install MongoDB on your own external server</a> you can try the new <a href="http://mongohq.com/">MongoDB hosted solution</a>.</p>
<p>Once you have the MongoDB server ready you will need to add the MongoDB gem to your .gems file:</p>
<pre class="brush: plain;">
mongodb-mongo --source gems.github.com
</pre>
<p>Then you will want to install the <a href="http://github.com/peburrows/mongo_db_logger">mongo db logger</a> plugin in your rails project. Just follow the instructions they give on the project page to get it installed. </p>
<p>Your app/controllers/application_controller.rb will look something like this:</p>
<pre class="brush: ruby;">
class ApplicationController &lt; ActionController::Base
  include MongoDBLogging

  helper :all # include all helpers, all the time
  protect_from_forgery # See ActionController::RequestForgeryProtection for details
end
</pre>
<p>After you get the plugin installed you will have to hard code the MongoDB information into the plugin. This could probably be fixed and pulled out of a configuration file but it won&#039;t work the way it is set up out of the box. Edit the file vendor/plugins/mongo_db_logger/lib/mongo_logger.rb and change the db_configuration to match your setup. It should be something like this:</p>
<pre class="brush: plain;">
  db_configuration = {
    'host'    =&gt; 'my.mongohost.com',
    'port'    =&gt; 56700,
    'database'    =&gt; 'testapp',
    'capsize' =&gt; default_capsize}
</pre>
<p>Now you should be able to deploy to Heroku and see your logs show up in your MongoDB database.</p>
<p>A couple very important things to note are that this logging won&#039;t catch exceptions that happen above the application itself and if your MongoDB server goes down your app may hang trying to connect to it.</p>
<p><a name="heroku-rack"><b>Rack works too</b></a></p>
<p>You aren&#039;t limited to just Ruby on Rails with Heroku, <a href="http://docs.heroku.com/rack">rack works</a> as well. That opens the door for other frameworks like <a href="http://www.sinatrarb.com/">Sinatra</a>, <a href="http://merbivore.com/">Merb</a> and <a href="http://camping.rubyforge.org/files/README.html">Camping</a> on Heroku. It is also possible that more in depth logging could be done using <a href="http://clogger.rubyforge.org/">clogger</a> or even <a href="http://www.rackamole.com/">rack a mole</a>.</p>
<p>Here is an example Camping application:</p>
<p>The .gems file:</p>
<pre class="brush: plain;">
camping
</pre>
<p>The config.ru file:</p>
<pre class="brush: ruby;">
require 'hello'
run Rack::Adapter::Camping.new(Hello)
</pre>
<p>The hello.rb file:</p>
<pre class="brush: ruby;">
require 'camping'

Camping.goes :Hello

module Hello::Controllers
  class Index &lt; R '/'
     def get
        render :hello
     end
  end
end

module Hello::Views
  def hello
     p  &quot;Hello World!&quot;
  end
end
</pre>
<p><br/></p>
<p><a name="heroku-compiled-gems"><b>Compiled gems</b></a></p>
<p>Heroku also lets you use gems that need to be compiled before they are installed. You can see this if you try to use something like <a href="http://github.com/ice799/memprof">memprof</a> however you will also notice that in the memprof case it won&#039;t actually work because there are missing libraries. So using gems that require libraries may be hit or miss. </p>
<p>If you are lucky you may find that the gem is already part of Heroku like RMagick is. Here is a little example of using Heroku and the RMagick gem to produce an image using Camping:</p>
<p>The .gems file:</p>
<pre class="brush: plain;">
camping
rmagick
</pre>
<p>The config.ru file:</p>
<pre class="brush: ruby;">
require 'hello'
run Rack::Adapter::Camping.new(Hello)
</pre>
<p>The hello.rb file:</p>
<pre class="brush: ruby;">
require 'camping'
require 'RMagick'

Camping.goes :Hello

module Hello::Controllers
  class Index &lt; R '/'
     def get
       @headers[&quot;Content-Type&quot;] = &quot;image/gif&quot;
       img = Magick::Image.new(200, 200)

       gc = Magick::Draw.new
       gc.gravity = Magick::CenterGravity
       gc.pointsize = 32
       gc.font_family = &quot;Helvetica&quot;
       gc.font_weight = Magick::BoldWeight
       gc.stroke = 'none'
       gc.annotate(img, 0, 0, 0, 0, &quot;Hello world!&quot;)

       img.format = &quot;GIF&quot;
       img.to_blob
     end
  end
end
</pre>
<p><br/></p>
<p><a name="heroku-dynos"><b>Understanding dynos</b></a></p>
<p>It is important to understand how <a href="http://docs.heroku.com/dynos">dynos</a> work. The bottom line is that you need one for every concurrent request. This is easy to demonstrate with the following modification of the Camping example above:</p>
<pre class="brush: plain;">
require 'camping'

Camping.goes :Hello

module Hello::Controllers
  class Index &amp;lt; R '/'
     def get
        render :hello
     end
  end
end

module Hello::Views
  def hello
     sleep 10
     p  &quot;Hello World!&quot;
  end
end
</pre>
<p>If you deploy this application to the free version of Heroku and then open two requests to it at the same time the second request will hang until the first one completes. This should illustrate why it is important to keep your processing quick and have enough dynos to match the concurrent request needs of your application.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ioncannon?a=73i7NeZ-808:rVeGu-Y6gns:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=73i7NeZ-808:rVeGu-Y6gns:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=73i7NeZ-808:rVeGu-Y6gns:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=73i7NeZ-808:rVeGu-Y6gns:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=73i7NeZ-808:rVeGu-Y6gns:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=73i7NeZ-808:rVeGu-Y6gns:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ioncannon/~4/73i7NeZ-808" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/842/heroku-tips-for-the-cheap/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://www.ioncannon.net/programming/842/heroku-tips-for-the-cheap/</feedburner:origLink></item>
		<item>
		<title>Building Chromium and Chrome OS with EC2</title>
		<link>http://feedproxy.google.com/~r/ioncannon/~3/lpcjxicsJnQ/</link>
		<comments>http://www.ioncannon.net/system-administration/771/building-chromium-and-chrome-os-with-ec2/#comments</comments>
		<pubDate>Mon, 23 Nov 2009 21:17:59 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[system administration]]></category>
		<category><![CDATA[chrome os]]></category>
		<category><![CDATA[chromium]]></category>
		<category><![CDATA[ec2]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=771</guid>
		<description><![CDATA[When the initial cut of the Chromium OS source was released last week I decided to use the opportunity to see if it would run on my EEE PC 900 netbook (check out EEE PC 900 running Chrome OS on Youtube to see the final result). The first roadblock I hit with the build instructions [...]]]></description>
			<content:encoded><![CDATA[<p>When the initial cut of the <a href="http://www.chromium.org/chromium-os">Chromium OS</a> source was released last week I decided to use the opportunity to see if it would run on my EEE PC 900 netbook (check out <a href="http://www.youtube.com/watch?v=gfQ6Qef5DWk">EEE PC 900 running Chrome OS</a> on Youtube to see the final result). The first roadblock I hit with the <a href="http://sites.google.com/a/chromium.org/dev/chromium-os/building-chromium-os/build-instructions">build instructions</a> was the Ubuntu requirement (I did give a little effort to getting it working on Fedora first). I don&#039;t have an Ubuntu box so I started out trying to use VirtualBox but that was going to take forever so I decided to move things to EC2 and what follows is the result. This isn&#039;t meant to be a replacement for the build docs since they are surely going to change, it is more of a cookbook to build <a href="http://www.chromium.org/Home">Chromium</a> (the browser) and Chromium OS using EC2 (EBS is used as well if you want to cache the source over time).</p>
<p>When I first started down the path of using EC2 I thought I would grab the source each time I wanted to build. I quickly ran into a snag however because it took forever to sync the source and download the Ubuntu repo. Once I had the initial sync of the source I decided I would copy it all to an EBS volume and keep that volume up to date. Using EBS to store the source feels better too since I assume Google expects people to be syncing changes only as opposed to pulling the entire source tree down every time they want to build.</p>
<p>I started out by finding this <a href="http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1952&#038;categoryID=101">Ubuntu AMI</a> for a base to work from. For the most efficient compile times I ended up using the High CPU (c1.medium) instance. I started with the default small instance but it was just too slow. With the high cpu instance you are looking at about 45 minutes to build the OS after you have the source synced for the first time and if you add building Chromium in there you are looking at around 55 additional minutes. All told you can have a complete build in less than 2 hours even if there are some source updates needed. For EBS you need a 3G volume for the Chrome OS source plus Ubuntu package repo and a 4G volume for the Chromium source. </p>
<p><span id="more-771"></span></p>
<p>I&#039;ve bundled everything up into one script called <a href="http://www.ioncannon.net/examples/builder.sh">builder.sh</a> and if you don&#039;t care to know the details you can download it and give it a try. There are some things you need to know about it however even if you don&#039;t want to follow all the details. First there are some of the assumptions made by the script:</p>
<ol>
<li>The EC2 AMI ami-ccf615a5 image is used on the node it is run on.</li>
<li>If you are storing the source then you have attached the EBS volumes and you have initialized a filesystem on both with mkfs.ext3 </li>
<li>If you are storing the source then you have attached the EBS volumes and indicated what devices they are using OSVOLDEV and BROWSERVOLDEV in the following config section</li>
<li>You are running the script as the root user</li>
</ol>
<p>Next is a small configuration area at the top of the script that lets it know what devices the two volumes will be mounted on. If you don&#039;t intend on having stored source then you don&#039;t need to worry about setting them to anything as long as what they are set to doesn&#039;t match a real device.</p>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;"><span class="re2">OSVOLDEV</span>=<span class="sy0">/</span>dev<span class="sy0">/</span>sdf1<br />
<span class="re2">BROWSERVOLDEV</span>=<span class="sy0">/</span>dev<span class="sy0">/</span>sdg1</div>
</div>
<p><br/></p>
<p>There are a few command line options that will let you tune the script if you want to skip parts of the build process:</p>
<ul>
<li>&#45;-skip-sync &#8211; This will skip trying to sync any of the repos. If you aren&#039;t using EBS to store the source the script will fail if you use this option since there will be nothing to build.</li>
<li>&#45;-skip-chrome-build &#8211; This will skip trying to build chrome. You have to build chrome at least once for the chrome os build to work.</li>
<li>&#45;-skip-chrome-os-build &#8211; This will skip trying to build chrome os.</li>
</ul>
<p>Here are a few other important things to make note of:</p>
<ul>
<li>The build script adds a user named &#034;gogo&#034; that can be used to log in if you don&#039;t want to use your Google login.</li>
<li>The build script sets the password for root access to &#034;gogo&#034;.</li>
<li>Running the build script should result in an image named /mnt/builder/<BUILDID>.usb.img.bz when everything is done. This is a compressed bootable image that can be written to a USB key (use something like dd if=usb.img of=/dev/usbkeydevice bs=10M).</li>
<li> I could imagine the same instructions and script being re-purposed for building on a non-EC2 system too.</li>
<li>The script can be run multiple times on the same node and will skip the parts that should only be done once.</li>
<li>There are probably ways to speed up the build to make it even faster. With some more effort the build for the browser could be kicked off while the source for the OS was still be synced. I also wondered if using a 64 bit EC2 node and creating a large ramdisk would help but I didn&#039;t want to fiddle with 64 bit builds of Chrome, I may revisit if I find time.</li>
<li>I was tempted to build an AMI that included all the prerequisites but it only takes 5 minutes to pull all of those together so I decided not to. It could streamline things to do that but I&#039;m not sure that it is worth the effort.</li>
</ul>
<p>If you are looking for more information about the script, places where it might make sense to modify the script and the build process in general then read on. </p>
<p>To get started there are a number of prerequisites that need to be installed. These are the required parts for both Chromium and Chromium OS. One note here is that there is some issue with lighttpd on Ubuntu that causes it to die while the apt-get is downloading all the packages so I had to resort to installing Apache as well. I make sure lighttpd isn&#039;t running since the prerequisites install it.</p>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;"><span class="kw1">if</span> <span class="br0">&#91;</span> <span class="re5">-f</span> <span class="sy0">/</span>var<span class="sy0">/</span>run<span class="sy0">/</span>chromepre <span class="br0">&#93;</span><br />
<span class="kw1">then</span><br />
&nbsp; <span class="kw3">echo</span> <span class="st0">&quot;Prereqs already installed, skipping&#8230;&quot;</span><br />
&nbsp; <span class="kw3">return</span><br />
<span class="kw1">fi</span></p>
<p><span class="kw2">touch</span> <span class="sy0">/</span>var<span class="sy0">/</span>run<span class="sy0">/</span>chromepre</p>
<p><span class="kw2">apt-get</span> <span class="re5">-y</span> <span class="re5">&#8211;force-yes</span> update<br />
<span class="kw2">apt-get</span> <span class="re5">-y</span> <span class="re5">&#8211;force-yes</span> upgrade<br />
<span class="kw2">wget</span> http:<span class="sy0">//</span>src.chromium.org<span class="sy0">/</span>svn<span class="sy0">/</span>trunk<span class="sy0">/</span>src<span class="sy0">/</span>build<span class="sy0">/</span>install-build-deps.sh <span class="re5">-O</span> <span class="sy0">/</span>tmp<span class="sy0">/</span>install-build-deps.sh<br />
<span class="kw2">chmod</span> +x <span class="sy0">/</span>tmp<span class="sy0">/</span>install-build-deps.sh<br />
<span class="kw3">echo</span> <span class="st0">&quot;yy&quot;</span> <span class="sy0">|</span> <span class="sy0">/</span>tmp<span class="sy0">/</span>install-build-deps.sh <span class="co0"># (requires y twice)</span><br />
<span class="sy0">/</span>etc<span class="sy0">/</span>init.d<span class="sy0">/</span>lighttpd stop<br />
<span class="kw2">apt-get</span> <span class="re5">-y</span> <span class="re5">&#8211;force-yes</span> <span class="kw2">install</span> git-core apache2 apt-mirror <span class="kw2">zip</span></p>
<p><span class="kw2">cat</span> <span class="sy0">&gt;</span> <span class="sy0">/</span>etc<span class="sy0">/</span>apt<span class="sy0">/</span>mirror.list <span class="sy0">&lt;&lt;</span>__EOF__<br />
<span class="kw1">set</span> base_path &nbsp; &nbsp;<span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>chromeos<span class="sy0">/</span>repo<br />
<span class="kw1">set</span> mirror_path &nbsp;<span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>chromeos<span class="sy0">/</span>repo<span class="sy0">/</span>mirror<br />
<span class="kw1">set</span> skel_path &nbsp; &nbsp;<span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>chromeos<span class="sy0">/</span>repo<span class="sy0">/</span>skel<br />
<span class="kw1">set</span> nthreads &nbsp; &nbsp; 20<br />
<span class="kw1">set</span> _tilde 0<br />
deb http:<span class="sy0">//</span>build.chromium.org<span class="sy0">/</span>buildbot<span class="sy0">/</span>packages<span class="sy0">/</span> chromeos main restricted universe multiverse<br />
deb http:<span class="sy0">//</span>build.chromium.org<span class="sy0">/</span>buildbot<span class="sy0">/</span>packages<span class="sy0">/</span> chromeos_dev main restricted universe multiverse<br />
clean http:<span class="sy0">//</span>build.chromium.org<span class="sy0">/</span>buildbot<span class="sy0">/</span>packages<span class="sy0">/</span><br />
__EOF__</p>
<p><span class="kw1">if</span> <span class="br0">&#91;</span> <span class="sy0">!</span> <span class="re5">-b</span> <span class="sy0">/</span>dev<span class="sy0">/</span>loop6 <span class="br0">&#93;</span><br />
<span class="kw1">then</span><br />
&nbsp; <span class="kw2">mknod</span> <span class="re5">-m660</span> <span class="sy0">/</span>dev<span class="sy0">/</span>loop6 b 7 6<br />
<span class="kw1">fi</span></p>
<p><span class="kw1">if</span> <span class="br0">&#91;</span> <span class="sy0">!</span> <span class="re5">-b</span> <span class="sy0">/</span>dev<span class="sy0">/</span>loop7 <span class="br0">&#93;</span><br />
<span class="kw1">then</span><br />
&nbsp; <span class="kw2">mknod</span> <span class="re5">-m660</span> <span class="sy0">/</span>dev<span class="sy0">/</span>loop7 b 7 7<br />
<span class="kw1">fi</span></div>
</div>
<p><br/></p>
<p>The next step is to create a user that will actually run the build. It turns out to be important that this user is not root so that is why there are some hoops being jumped here. The two source volumes get mounted into the user&#039;s home directory here as well. The last part of this section builds a filesystem to be used as temporary storage for output images. The way they build is set up it will push the final images into the source tree and because that is stored on EBS it could end up eating up a lot of room, there is more to this later in the Chrome OS build section.</p>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;"><span class="kw1">if</span> <span class="br0">&#91;</span> <span class="re5">-d</span> <span class="sy0">/</span>mnt<span class="sy0">/</span>builder <span class="br0">&#93;</span><br />
<span class="kw1">then</span><br />
&nbsp; <span class="kw3">echo</span> <span class="st0">&quot;Build user already created, skipping&#8230;&quot;</span><br />
&nbsp; <span class="kw3">return</span><br />
<span class="kw1">fi</span></p>
<p>useradd <span class="re5">-G</span> disk <span class="re5">-u</span> 1001 <span class="re5">-s</span> <span class="sy0">/</span>bin<span class="sy0">/</span><span class="kw2">bash</span> <span class="re5">-d</span> <span class="sy0">/</span>mnt<span class="sy0">/</span>builder <span class="re5">-m</span> builder<br />
<span class="kw3">echo</span> <span class="st0">&quot;builder &nbsp; &nbsp;ALL=NOPASSWD: ALL&quot;</span> <span class="sy0">&gt;&gt;</span> <span class="sy0">/</span>etc<span class="sy0">/</span>sudoers</p>
<p><span class="kw2">mkdir</span> <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>chromeos<br />
<span class="kw2">chown</span> builder.builder <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>chromeos<span class="sy0">/</span><br />
<span class="kw2">mkdir</span> <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>chrome<br />
<span class="kw2">chown</span> builder.builder <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>chrome<span class="sy0">/</span></p>
<p><span class="kw1">if</span> <span class="br0">&#91;</span> <span class="re5">-b</span> <span class="re1">$OSVOLDEV</span> <span class="br0">&#93;</span><br />
<span class="kw1">then</span><br />
&nbsp; <span class="kw2">mount</span> <span class="re1">$OSVOLDEV</span> <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>chromeos<span class="sy0">/</span><br />
<span class="kw1">fi</span><br />
<span class="kw1">if</span> <span class="br0">&#91;</span> <span class="re5">-b</span> <span class="re1">$BROWSERVOLDEV</span> <span class="br0">&#93;</span><br />
<span class="kw1">then</span><br />
&nbsp; <span class="kw2">mount</span> <span class="re1">$BROWSERVOLDEV</span> <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>chrome<span class="sy0">/</span><br />
<span class="kw1">fi</span></p>
<p><span class="kw3">echo</span> <span class="st0">&quot;export PATH=<span class="es1">\$</span>PATH:/mnt/builder/chromeos/depot_tools/&quot;</span> <span class="sy0">&gt;&gt;</span> <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>.bashrc</p>
<p><span class="kw2">ln</span> <span class="re5">-s</span> <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>chromeos<span class="sy0">/</span>repo<span class="sy0">/</span>mirror<span class="sy0">/</span>build.chromium.org<span class="sy0">/</span>buildbot<span class="sy0">/</span>packages<span class="sy0">/</span> <span class="sy0">/</span>var<span class="sy0">/</span>www<span class="sy0">/</span>packages</p>
<p><span class="kw1">if</span> <span class="br0">&#91;</span> <span class="sy0">!</span> <span class="re5">-f</span> <span class="sy0">/</span>mnt<span class="sy0">/</span>images.img <span class="br0">&#93;</span><br />
<span class="kw1">then</span><br />
&nbsp; <span class="kw2">dd</span> <span class="kw1">if</span>=<span class="sy0">/</span>dev<span class="sy0">/</span>zero <span class="re2">of</span>=<span class="sy0">/</span>mnt<span class="sy0">/</span>images.img <span class="re2">bs</span>=1 <span class="re2">count</span>=0 <span class="re2">seek</span>=10G<br />
&nbsp; losetup <span class="sy0">/</span>dev<span class="sy0">/</span>loop6 <span class="sy0">/</span>mnt<span class="sy0">/</span>images.img<br />
&nbsp; mkfs.ext3 <span class="sy0">/</span>dev<span class="sy0">/</span>loop6<br />
<span class="kw1">fi</span></p>
<p><span class="kw1">if</span> <span class="br0">&#91;</span> <span class="sy0">!</span> <span class="re5">-d</span> <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>chromeos<span class="sy0">/</span>depot_tools<span class="sy0">/</span> <span class="br0">&#93;</span><br />
<span class="kw1">then</span><br />
&nbsp; <span class="kw2">su</span> &#8211; builder <span class="re5">-c</span> <span class="st0">&quot;cd /mnt/builder/chromeos/; svn co http://src.chromium.org/svn/trunk/tools/depot_tools&quot;</span><br />
<span class="kw1">fi</span></div>
</div>
<p><br/></p>
<p>The next sections each sync either source or the apt-get repository. The first is syncing the apt-get repository using apt-mirror. In case you missed it the mirror was defined in the prerequisites section above.</p>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;"><span class="kw2">su</span> &#8211; builder <span class="re5">-c</span> apt-mirror</div>
</div>
<p><br/></p>
<p>The next is the Chromium OS source repository.</p>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;"><span class="kw3">cd</span> <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>chromeos<span class="sy0">/</span>chromiumos<span class="sy0">/</span><br />
<span class="kw1">if</span> <span class="br0">&#91;</span> <span class="sy0">!</span> <span class="re5">-f</span> .gclient <span class="br0">&#93;</span><br />
<span class="kw1">then</span><br />
&nbsp; <span class="kw2">su</span> &#8211; builder <span class="re5">-c</span> <span class="st0">&quot;cd /mnt/builder/chromeos/chromiumos/; /mnt/builder/chromeos/depot_tools/gclient config http://src.chromium.org/git/chromiumos.git&quot;</span><br />
<span class="kw1">fi</span></p>
<p><span class="kw2">su</span> &#8211; builder <span class="re5">-c</span> <span class="st0">&quot;cd /mnt/builder/chromeos/chromiumos/; /mnt/builder/chromeos/depot_tools/gclient sync&quot;</span></div>
</div>
<p><br/></p>
<p>There are a few extra parts to syncing the Chromium browser. One of those extras is to make sure the third party test suites don&#039;t get synced since they are large.</p>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;"><span class="kw2">cat</span> <span class="sy0">&gt;</span> <span class="sy0">/</span>tmp<span class="sy0">/</span>scs.sh <span class="sy0">&lt;&lt;</span>__EOF__<br />
<span class="kw3">export</span> <span class="re2">GYP_DEFINES</span>=<span class="st0">&quot;chromeos=1 target_arch=ia32&quot;</span><br />
<span class="kw3">export</span> <span class="re2">GYP_GENERATORS</span>=<span class="kw2">make</span><br />
<span class="kw3">cd</span> <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>chrome<span class="sy0">/</span></p>
<p><span class="kw1">if</span> <span class="br0">&#91;</span> <span class="sy0">!</span> <span class="re5">-f</span> .gclient <span class="br0">&#93;</span><br />
<span class="kw1">then</span><br />
&nbsp; <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>chromeos<span class="sy0">/</span>depot_tools<span class="sy0">/</span>gclient config http:<span class="sy0">//</span>src.chromium.org<span class="sy0">/</span>svn<span class="sy0">/</span>trunk<span class="sy0">/</span>src<br />
&nbsp; <span class="kw2">awk</span> <span class="re5">-f</span> &#8211; .gclient <span class="sy0">&lt;&lt;</span> __END__<br />
<span class="br0">&#123;</span> print <span class="br0">&#125;</span><br />
<span class="sy0">/</span><span class="st0">&quot; &nbsp; &nbsp;&quot;</span>custom_deps<span class="st0">&quot; : {&quot;</span><span class="sy0">/</span> <span class="br0">&#123;</span> <br />
&nbsp; &nbsp; print <span class="st0">&quot; &nbsp; &nbsp;<span class="es1">\&quot;</span>custom_deps<span class="es1">\&quot;</span> : {<span class="es1">\n</span><span class="es1">\&quot;</span>src/third_party/WebKit/LayoutTests<span class="es1">\&quot;</span>: None,&quot;</span> <br />
<span class="br0">&#125;</span><br />
__END__<br />
<span class="kw1">fi</span></p>
<p><span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>chromeos<span class="sy0">/</span>depot_tools<span class="sy0">/</span>gclient <span class="kw2">sync</span> <span class="re5">&#8211;deps</span>=<span class="st0">&quot;chromeos,unix&quot;</span></p>
<p><span class="kw3">exit</span><br />
__EOF__</p>
<p><span class="kw2">chmod</span> +x <span class="sy0">/</span>tmp<span class="sy0">/</span>scs.sh</p>
<p><span class="kw2">su</span> &#8211; builder <span class="re5">-c</span> <span class="sy0">/</span>tmp<span class="sy0">/</span>scs.sh</div>
</div>
<p><br/></p>
<p>Building the Chromium browser. This isn&#039;t needed every time. When it is done it sticks the browser in place for the Chromium OS build.</p>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;"><span class="kw1">if</span> <span class="br0">&#91;</span> <span class="sy0">!</span> <span class="re5">-d</span> <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>chrome.tmp <span class="br0">&#93;</span><br />
<span class="kw1">then</span><br />
&nbsp; <span class="kw2">su</span> &#8211; builder <span class="re5">-c</span> <span class="st0">&quot;cp -Rp /mnt/builder/chrome /mnt/builder/chrome.tmp&quot;</span><br />
<span class="kw1">fi</span><br />
<span class="kw2">su</span> &#8211; builder <span class="re5">-c</span> <span class="st0">&quot;export PATH=<span class="es2">$PATH</span>:/mnt/builder/chromeos/depot_tools/; /mnt/builder/chromeos/chromiumos/chromiumos.git/src/scripts/build_chrome.sh &#8211;chrome_dir /mnt/builder/chrome.tmp/&quot;</span></div>
</div>
<p><br/></p>
<p>Finally the place where the OS actually gets compiled. The majority of this is right out of the build instructions. One thing to understand here is that the build process creates a chroot environment and then maps the source into that before the build starts. The build is done inside the chroot environment so the build script has to create a temporary script with all the commands that need to run there. One other note here is that the images filesystem created above has to be mapped into the chroot environment and I did that by setting it up as a loopback device then mounting it from inside the chroot environment. After the build is complete I can then get to the filesystem outside of the chroot environment to grab the final image. If you want to change the test user, the system password or get rid of either of those options this is the section to change.</p>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;"><span class="kw2">su</span> &#8211; builder <span class="re5">-c</span> <span class="st0">&quot;/mnt/builder/chromeos/chromiumos/chromiumos.git/src/scripts/make_chroot.sh &#8211;mirror http://localhost/packages/ &#8211;chroot /mnt/builder/chroot &#8211;replace&quot;</span></p>
<p><span class="kw2">cat</span> <span class="sy0">&gt;</span> <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>chroot<span class="sy0">/</span>tmp<span class="sy0">/</span>go.sh <span class="sy0">&lt;&lt;</span> __EOF__<br />
<span class="co0">#!/bin/sh</span><br />
<span class="kw2">rm</span> <span class="re5">-f</span> <span class="sy0">/</span>tmp<span class="sy0">/</span>go.sh</p>
<p><span class="kw3">cd</span> ..<span class="sy0">/</span>platform<span class="sy0">/</span>pam_google <span class="sy0">&amp;&amp;</span> .<span class="sy0">/</span>enable_localaccount.sh gogo<br />
<span class="kw3">cd</span> -<br />
<span class="kw3">echo</span> <span class="st0">&quot;gogo&quot;</span> <span class="sy0">|</span> .<span class="sy0">/</span>set_shared_user_password.sh</p>
<p>.<span class="sy0">/</span>build_platform_packages.sh<br />
.<span class="sy0">/</span>build_kernel.sh</p>
<p><span class="kw2">sudo</span> <span class="kw2">mount</span> <span class="sy0">/</span>dev<span class="sy0">/</span>loop6 <span class="sy0">/</span>home<span class="sy0">/</span>builder<span class="sy0">/</span>trunk<span class="sy0">/</span>src<span class="sy0">/</span>build<span class="sy0">/</span>images<span class="sy0">/</span><br />
<span class="kw2">sudo</span> <span class="kw2">chown</span> builder.adm <span class="sy0">/</span>home<span class="sy0">/</span>builder<span class="sy0">/</span>trunk<span class="sy0">/</span>src<span class="sy0">/</span>build<span class="sy0">/</span>images</p>
<p>.<span class="sy0">/</span>build_image.sh <span class="re5">&#8211;mirror</span> http:<span class="sy0">//</span>localhost<span class="sy0">/</span>packages<span class="sy0">/</span></p>
<p><span class="kw2">sudo</span> <span class="kw2">umount</span> <span class="sy0">/</span>home<span class="sy0">/</span>builder<span class="sy0">/</span>trunk<span class="sy0">/</span>src<span class="sy0">/</span>build<span class="sy0">/</span>images<span class="sy0">/</span></p>
<p><span class="kw3">exit</span><br />
__EOF__</p>
<p><span class="kw2">chown</span> builder.builder <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>chroot<span class="sy0">/</span>tmp<span class="sy0">/</span>go.sh<br />
<span class="kw2">chmod</span> +x <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>chroot<span class="sy0">/</span>tmp<span class="sy0">/</span>go.sh</p>
<p><span class="kw2">su</span> &#8211; builder <span class="re5">-c</span> <span class="st0">&quot;/mnt/builder/chromeos/chromiumos/chromiumos.git/src/scripts/enter_chroot.sh &#8211;chroot /mnt/builder/chroot /tmp/go.sh&quot;</span></p>
<p><span class="kw2">mkdir</span> <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>mnt<span class="sy0">/</span><br />
<span class="kw2">mount</span> <span class="sy0">/</span>dev<span class="sy0">/</span>loop6 <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>mnt<br />
<span class="re2">NEWEST</span>=<span class="sy0">`</span><span class="kw2">ls</span> <span class="re5">-tadr</span> <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>mnt<span class="sy0">/*/</span> <span class="sy0">|</span> <span class="kw2">tail</span> <span class="re5">-1</span> <span class="sy0">|</span> <span class="kw2">head</span> -1<span class="sy0">`</span><br />
<span class="re2">BUILDID</span>=<span class="sy0">`</span><span class="kw2">basename</span> <span class="re1">$NEWEST</span><span class="sy0">`</span><br />
<span class="kw2">dd</span> <span class="kw1">if</span>=<span class="sy0">/</span>dev<span class="sy0">/</span>zero <span class="re2">of</span>=<span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span><span class="re1">$BUILDID</span>.usb.img <span class="re2">bs</span>=1 <span class="re2">count</span>=0 <span class="re2">seek</span>=4G<br />
losetup <span class="sy0">/</span>dev<span class="sy0">/</span>loop7 <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span><span class="re1">$BUILDID</span>.usb.img<br />
<span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>chromeos<span class="sy0">/</span>chromiumos<span class="sy0">/</span>chromiumos.git<span class="sy0">/</span>src<span class="sy0">/</span>scripts<span class="sy0">/</span>image_to_usb.sh <span class="re5">-y</span> <span class="re5">&#8211;from</span>=<span class="re1">$NEWEST</span> <span class="re5">&#8211;to</span>=<span class="sy0">/</span>dev<span class="sy0">/</span>loop7<br />
losetup <span class="re5">-d</span> <span class="sy0">/</span>dev<span class="sy0">/</span>loop7<br />
<span class="kw2">umount</span> <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span>mnt<br />
<span class="kw2">bzip2</span> <span class="re5">-9</span> <span class="sy0">/</span>mnt<span class="sy0">/</span>builder<span class="sy0">/</span><span class="re1">$BUILDID</span>.usb.img</div>
</div>
<p><br/></p>
<p>A final bit of glue looks for command line arguments ties everything together.</p>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;"><span class="re2">SKIP_SYNC</span>=<span class="kw2">false</span><br />
<span class="re2">SKIP_CHROME_BUILD</span>=<span class="kw2">false</span><br />
<span class="re2">SKIP_CHROME_OS_BUILD</span>=<span class="kw2">false</span></p>
<p><span class="kw1">set</span> <span class="re5">&#8211;</span> $<span class="br0">&#40;</span><span class="kw2">getopt</span> <span class="re5">-l</span> skip-sync,skip-chrome-build,skip-chrome-os-build abc: <span class="st0">&quot;$@&quot;</span><span class="br0">&#41;</span><br />
<span class="kw1">while</span> <span class="br0">&#91;</span> <span class="re4">$#</span> <span class="re5">-gt</span> 0 <span class="br0">&#93;</span><br />
<span class="kw1">do</span><br />
&nbsp; <span class="kw1">case</span> <span class="st0">&quot;$1&quot;</span> <span class="kw1">in</span><br />
&nbsp; &nbsp; <span class="br0">&#40;</span>&#8211;skip-sync<span class="br0">&#41;</span> <span class="re2">SKIP_SYNC</span>=<span class="kw2">true</span>; <span class="kw3">break</span><span class="sy0">;;</span><br />
&nbsp; &nbsp; <span class="br0">&#40;</span>&#8211;skip-chrome-build<span class="br0">&#41;</span> <span class="re2">SKIP_CHROME_BUILD</span>=<span class="kw2">true</span>; <span class="kw3">break</span><span class="sy0">;;</span><br />
&nbsp; &nbsp; <span class="br0">&#40;</span>&#8211;skip-chrome-os-build<span class="br0">&#41;</span> <span class="re2">SKIP_CHROME_OS_BUILD</span>=<span class="kw2">true</span>; <span class="kw3">break</span><span class="sy0">;;</span><br />
&nbsp; &nbsp; <span class="br0">&#40;</span>&#8211;<span class="br0">&#41;</span> <span class="kw3">shift</span>; <span class="kw3">break</span><span class="sy0">;;</span><br />
&nbsp; &nbsp; <span class="br0">&#40;</span>&#8211;<span class="sy0">*</span><span class="br0">&#41;</span> <span class="kw3">echo</span> <span class="st0">&quot;$0: error &#8211; unrecognized option $1&quot;</span> 1<span class="sy0">&gt;&amp;</span><span class="nu0">2</span>; <span class="kw3">exit</span> <span class="nu0">1</span><span class="sy0">;;</span><br />
&nbsp; &nbsp; <span class="br0">&#40;</span><span class="sy0">*</span><span class="br0">&#41;</span> &nbsp;<span class="kw3">break</span><span class="sy0">;;</span><br />
&nbsp; <span class="kw1">esac</span><br />
&nbsp; <span class="kw3">shift</span><br />
<span class="kw1">done</span></p>
<p>install_prereqs<br />
create_build_user</p>
<p><span class="kw1">if</span> <span class="br0">&#91;</span> <span class="st0">&quot;<span class="es2">$SKIP_SYNC</span>&quot;</span> <span class="sy0">!</span>= <span class="st0">&quot;true&quot;</span> <span class="br0">&#93;</span><br />
<span class="kw1">then</span><br />
&nbsp; sync_apt<br />
&nbsp; sync_chrome<br />
&nbsp; sync_chrome_os<br />
<span class="kw1">fi</span></p>
<p><span class="kw1">if</span> <span class="br0">&#91;</span> <span class="st0">&quot;<span class="es2">$SKIP_CHROME_BUILD</span>&quot;</span> <span class="sy0">!</span>= <span class="st0">&quot;true&quot;</span> <span class="br0">&#93;</span><br />
<span class="kw1">then</span><br />
&nbsp; build_chrome<br />
<span class="kw1">fi</span></p>
<p><span class="kw1">if</span> <span class="br0">&#91;</span> <span class="st0">&quot;<span class="es2">$SKIP_CHROME_BUILD</span>&quot;</span> <span class="sy0">!</span>= <span class="st0">&quot;true&quot;</span> <span class="br0">&#93;</span><br />
<span class="kw1">then</span><br />
&nbsp; build_chrome_os<br />
<span class="kw1">fi</span></div>
</div>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ioncannon?a=lpcjxicsJnQ:nQeX7MTeF8w:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=lpcjxicsJnQ:nQeX7MTeF8w:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=lpcjxicsJnQ:nQeX7MTeF8w:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=lpcjxicsJnQ:nQeX7MTeF8w:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=lpcjxicsJnQ:nQeX7MTeF8w:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=lpcjxicsJnQ:nQeX7MTeF8w:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ioncannon/~4/lpcjxicsJnQ" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/system-administration/771/building-chromium-and-chrome-os-with-ec2/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.ioncannon.net/system-administration/771/building-chromium-and-chrome-os-with-ec2/</feedburner:origLink></item>
		<item>
		<title>Upgrade to Fedora 12 from Fedora 11</title>
		<link>http://feedproxy.google.com/~r/ioncannon/~3/XAiHMjGzO9A/</link>
		<comments>http://www.ioncannon.net/system-administration/719/upgrade-to-fedora-12-from-fedora-11/#comments</comments>
		<pubDate>Wed, 18 Nov 2009 11:56:18 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[system administration]]></category>
		<category><![CDATA[Fedora]]></category>
		<category><![CDATA[linux]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=719</guid>
		<description><![CDATA[Fedora 12 was just released and it is time to upgrade again of course. I almost thought this was going to be a version to yawn at but then I saw that there was going to be a new version of Fedora based on Moblin and it seemed exciting again. Of course that isn&#039;t the [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://docs.fedoraproject.org/release-notes/f12/en-US/html/">Fedora 12</a> was just released and it is time to upgrade again of course. I almost thought this was going to be a version to yawn at but then I saw that there was going to be a new version of Fedora based on <a href="http://moblin.org/">Moblin</a> and it seemed exciting again. Of course that isn&#039;t the only thing being upgraded in the latest version of Fedora. Some of the more notable changes in this version:</p>
<ul>
<li>Updated window managers <a href="http://www.fedoraproject.org/wiki/Features/Gnome2.28">Gnome 2.28</a>, <a href="http://www.fedoraproject.org/wiki/Features/KDE43">KDE 4.3</a> and <a href="http://www.fedoraproject.org/wiki/Features/FedoraMoblin">Fedora Moblin</a></li>
<li><a href="http://fedoraproject.org/wiki/Features/Presto">Delta RPM support</a></li>
<li><a href="http://www.fedoraproject.org/wiki/Features/F12X86Support">i686 as the base architecture</a></li>
<li>Lots of virtualization changes: <a href="http://www.fedoraproject.org/wiki/Features/KSM">KSM</a>, <a href="http://www.fedoraproject.org/wiki/Features/KVM_Huge_Page_Backed_Memory">KVM huge page support</a>, <a href="http://www.fedoraproject.org/wiki/Features/KVM_NIC_Hotplug>KVM NIC hotplug</a>, <a href="http://www.fedoraproject.org/wiki/Features/KVM_qcow2_Performance">KVM QCow2 performance improvements</a>, <a href="http://www.fedoraproject.org/wiki/Features/KVM_Stable_Guest_ABI">KVM Stable Guest ABI</a>, <a href="http://www.fedoraproject.org/wiki/Features/libguestfs">libguestfs</a>, <a href="http://www.fedoraproject.org/wiki/Features/Network_Interface_Management">Virtual network management</a> and <a href="http://www.fedoraproject.org/wiki/Features/VirtPrivileges">improved virtual privileges</a> to name a few</li>
<li>An easier to use bug reporting interface <a href="http://www.fedoraproject.org/wiki/Features/ABRTF12">Abrt 1.0</a></li>
<li><a href="http://www.fedoraproject.org/wiki/Features/BetterWebcamSupportF12">Better Webcam Support</a></li>
</ul>
<p>You can find the complete list of <a href="http://www.fedoraproject.org/wiki/Releases/12/FeatureList">Fedora 12 enhancements</a> as well if you want more details.</p>
<p><span id="more-719"></span></p>
<p>I&#039;m again starting with <a href="http://fedoraproject.org/wiki/Features/PreUpgrade">PreUpdate</a> since it worked well last time. The steps are pretty much the same as last time but I did have more problems after the upgrade:</p>
<ol>
<li>You have to be at Fedora11 before you try this. If you aren&#039;t there follow the <a href="http://www.ioncannon.net/system-administration/358/upgrading-to-fedora-11-from-fedora-10/">steps to get to Fedora11</a>.</li>
<li>yum clean all</li>
<li>yum udpate</li>
<li>Make sure to back up your xorg.conf since it can disappear with the upgrade.</li>
<li>preupgrade-cli &#034;Fedora 12 (Constantine)&#034; The total download for the upgrade from Fedora 11 to Fedora 12 was 1.1GB for me</li>
<li>After I rebooted the first time I got an error that the /boot directory didn&#039;t have enough space. It turns out I needed about 27M of free space so I ended up having to delete old kernels until I had 28M of free space.</li>
<li>reboot and wait</li>
</ol>
<p>I lucked out again and didn&#039;t have to remove anything to fix dependency issues. I recompiled my existing NVIDIA driver but I ran into an issue with the nouveau NVIDIA driver that comes with Fedora 12. I needed to remove the nouveau package, rebuild initrd and reboot before I could build the NVIDIA driver:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">yum remove xorg-x11-drv-nouveau.i686<br />
rm /boot/initramfs-2.6.31.5-127.fc12.i686.img<br />
mkinitrd /boot/initramfs-2.6.31.5-127.fc12.i686.img 2.6.31.5-127.fc12.i686</div>
</div>
<p>If that is too much work you can also get the NVIDIA driver from <a href="http://www.atrpms.net/">atrpms</a> but you will still need to remove the nouveau driver first. I also needed to pull down the Fedora 12 version of <a href="http://www.virtualbox.org/wiki/Linux_Downloads">Virtualbox</a> but so far that was all.</p>
<p>If you prefer the yum upgrade option here are the steps for that as well:</p>
<ol>
<li>yum clean all</li>
<li>yum update</li>
<li>rpm -Uvh http://mirrors.kernel.org/fedora/releases/12/Fedora/i386/os/Packages/fedora-release-notes-12.0.0-4.fc12.noarch.rpm http://mirrors.kernel.org/fedora/releases/12/Fedora/i386/os/Packages/fedora-release-12-1.noarch.rpm</li>
<li>yum clean all</li>
<li>yum -y update</li>
<li>You may need to resolve dependencies and then do another yum -y update</li>
<li>reboot</li>
</ol>
<p>I had to resolve dependencies to get this to work. I had to remove tigervnc-server-1.0.0-2.fc11.i586 and VirtualBox-3.0.10_54097_fedora11-1.i586. The resulting update was about 1.4G so it took a little while to apply.</p>
<p>I&#039;ve also put together a few <a href="http://www.ioncannon.net/projects/miscellaneous-projects/fedora-12-screenshots-and-videos/">videos and screenshots</a> if you want to get a quick preview of what the different versions available look like. You can also view the videos on Youtube: <a href="http://www.youtube.com/watch?v=W6rTVTyjQ1Q&#038;feature=player_embedded">Moblin</a>, <a href="http://www.youtube.com/watch?v=F0UlVnoUtbw&#038;feature=player_embedded">KDE</a> and <a href="http://www.youtube.com/watch?v=GZ8tSxMJTr8&#038;feature=player_embedded">Gnome</a></p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ioncannon?a=XAiHMjGzO9A:aRfm3BcDxeA:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=XAiHMjGzO9A:aRfm3BcDxeA:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=XAiHMjGzO9A:aRfm3BcDxeA:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=XAiHMjGzO9A:aRfm3BcDxeA:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=XAiHMjGzO9A:aRfm3BcDxeA:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=XAiHMjGzO9A:aRfm3BcDxeA:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ioncannon/~4/XAiHMjGzO9A" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/system-administration/719/upgrade-to-fedora-12-from-fedora-11/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.ioncannon.net/system-administration/719/upgrade-to-fedora-12-from-fedora-11/</feedburner:origLink></item>
		<item>
		<title>Full Text Search with Sphinx</title>
		<link>http://feedproxy.google.com/~r/ioncannon/~3/G_kGmfpWI_A/</link>
		<comments>http://www.ioncannon.net/programming/685/full-text-search-with-sphinx/#comments</comments>
		<pubDate>Tue, 20 Oct 2009 08:30:28 +0000</pubDate>
		<dc:creator>carson</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[sphinx]]></category>

		<guid isPermaLink="false">http://www.ioncannon.net/?p=685</guid>
		<description><![CDATA[While developing my GeeQE iPhone application I decided I needed a way to let users search posts so I started looking around for a simple search engine that I could use with PHP. I took a look at a number of different options like MySQL Full Text search, Sphinx, Solr and others based on Lucene. [...]]]></description>
			<content:encoded><![CDATA[<p>While developing my <a href="http://www.ioncannon.net/projects/geeqe/">GeeQE</a> iPhone application I decided I needed a way to let users search posts so I started looking around for a simple search engine that I could use with PHP. I took a look at a number of different options like <a href="http://dev.mysql.com/doc/refman/5.1/en/fulltext-search.html">MySQL Full Text search</a>, <a href="http://www.sphinxsearch.com/">Sphinx</a>, <a href="http://lucene.apache.org/solr/">Solr</a> and others based on <a href="http://lucene.apache.org/">Lucene</a>. After looking at what it would take to get started with each I decided to go with Sphinx. Sphinx looked like it would be the easiest and quickest to set up, didn&#039;t require a lot of resources to run in an idle state and would integrate with PHP easily.</p>
<p>This post goes over how I went about configuring Sphinx and gives an example of how to integrate it with PHP. I&#039;m using MySQL as the data store filled with the <a href="http://blog.stackoverflow.com/2009/06/stack-overflow-creative-commons-data-dump/">Stack Overflow CC data dump</a> although it should be easy to adapt the instructions to other data sources. To follow along just download a copy of the data dump and use my <a href="http://code.google.com/p/geeqe/source/browse/trunk/scripts/schema.sql">schema</a> and <a href="http://code.google.com/p/geeqe/source/browse/trunk/scripts/load.rb">loader</a> to get the same MySQL database.</p>
<p><span id="more-685"></span></p>
<p>I&#039;ve broken the setup down into the following 4 steps:</p>
<ol>
<li><a href="#sphinx-config">Configuring Sphinx</a></li>
<li><a href="#sphinx-index">Building an Index with Sphinx</a></li>
<li><a href="#sphinx-search">Searching with Sphinx</a></li>
<li><a href="#sphinx-php-api">Using the Sphinx PHP API</a></li>
</ol>
<p>As a side note, Sphinx is available as a package under Fedora 11 and I assume the same of other Linux distributions so it shouldn&#039;t be too hard to install. I am using version 0.9.8.1 compiled from source.</p>
<p><a name="sphinx-config"><b>Configuring Sphinx</b></a></p>
<p>The Sphinx configuration file can be be broken down into different parts with some of those parts being used by the indexer and some by the search service. I have broken the configuration file I am using into different sections but I include the full file for download at the end. The first segment of the configuration describes where to get the data for the index, in this case it is a MySQL database:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">###############################################<br />
## source for the data to be indexed<br />
###############################################</p>
<p>source sosrc<br />
{<br />
&nbsp; &nbsp; &nbsp; &nbsp; type&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = mysql</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; sql_host&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = localhost<br />
&nbsp; &nbsp; &nbsp; &nbsp; sql_user&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = souser<br />
&nbsp; &nbsp; &nbsp; &nbsp; sql_pass&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; =<br />
&nbsp; &nbsp; &nbsp; &nbsp; sql_db&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = so_2009_10<br />
&nbsp; &nbsp; &nbsp; &nbsp; sql_port&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = 3306</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; sql_query &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = SELECT id, owner_id, UNIX_TIMESTAMP(created) AS date_added, title, body_text FROM post WHERE post_type_id = 1</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; sql_attr_uint &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = owner_id<br />
&nbsp; &nbsp; &nbsp; &nbsp; sql_attr_timestamp&nbsp; &nbsp; &nbsp; = date_added</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; sql_ranged_throttle &nbsp; &nbsp; = 0</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; # document info query, ONLY for CLI search (ie. testing and debugging)<br />
&nbsp; &nbsp; &nbsp; &nbsp; sql_query_info&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = SELECT * FROM post WHERE id=$id<br />
}</div>
</div>
<p><br/></p>
<p>There are a few notable parts to the above. The field &#034;sql_query&#034; defines the query that grabs data from the database for the index. The &#034;sql_attr_*&#034; values point out the columns in the data that will be used in search queries later to sort or group by, more on that in the searching section.</p>
<p>The next section defines the index:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">###############################################<br />
## base search index<br />
###############################################</p>
<p>index so_2009_10<br />
{<br />
&nbsp; &nbsp; &nbsp; &nbsp; source&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = sosrc<br />
&nbsp; &nbsp; &nbsp; &nbsp; path&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = ./data/so_2009_10<br />
&nbsp; &nbsp; &nbsp; &nbsp; docinfo &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = extern<br />
&nbsp; &nbsp; &nbsp; &nbsp; mlock &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = 0</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; morphology&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = none</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; min_word_len&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = 1<br />
&nbsp; &nbsp; &nbsp; &nbsp; charset_type&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = sbcs</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; html_strip&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = 1<br />
}</p>
<p>###############################################<br />
## index that extends the original index<br />
###############################################</p>
<p>index so_2009_10stemmed : so_2009_10<br />
{<br />
&nbsp; &nbsp; &nbsp; &nbsp; path&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = ./data/so_2009_10stemmed<br />
&nbsp; &nbsp; &nbsp; &nbsp; morphology&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = stem_en<br />
}</div>
</div>
<p><br/></p>
<p>In the above there are two index definitions. The first index is very basic and has no morphology defined. The second index is derived from the first index and uses a <a href="http://en.wikipedia.org/wiki/Stemming">stemming</a> morphology. I&#039;m also having any HTML stripped out of the content since there is some in the data and it doesn&#039;t need to be searchable in the index.</p>
<p>The next block in the configuration file defines how the indexer will run. Here I&#039;m limiting the amount of memory used to 32M as an example of what could be done:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">###############################################<br />
## indexer settings<br />
###############################################</p>
<p>indexer<br />
{<br />
&nbsp; &nbsp; &nbsp; &nbsp; mem_limit &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = 32M<br />
}</div>
</div>
<p><br/></p>
<p>The final part of the configuration file sets options for the search daemon:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">###############################################<br />
## searchd settings<br />
###############################################</p>
<p>searchd<br />
{<br />
&nbsp; &nbsp; &nbsp; &nbsp; port&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = 3312<br />
&nbsp; &nbsp; &nbsp; &nbsp; log &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = /tmp/searchd.log<br />
&nbsp; &nbsp; &nbsp; &nbsp; query_log &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = /tmp/query.log</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; read_timeout&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = 5<br />
&nbsp; &nbsp; &nbsp; &nbsp; max_children&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = 30<br />
&nbsp; &nbsp; &nbsp; &nbsp; pid_file&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = /tmp/searchd.pid<br />
&nbsp; &nbsp; &nbsp; &nbsp; max_matches &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = 100<br />
&nbsp; &nbsp; &nbsp; &nbsp; seamless_rotate &nbsp; &nbsp; &nbsp; &nbsp; = 1<br />
&nbsp; &nbsp; &nbsp; &nbsp; preopen_indexes &nbsp; &nbsp; &nbsp; &nbsp; = 0<br />
&nbsp; &nbsp; &nbsp; &nbsp; unlink_old&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; = 1<br />
}</div>
</div>
<p><br/></p>
<p>You can download the full configuration file here: <a href="/examples/sphinx.conf">sphinx.conf</a></p>
<p>The above configuration gets you the basics. If you want to find out more you can check out the <a href="http://www.sphinxsearch.com/docs/current.html#indexing">indexing</a> documentation.</p>
<p><a name="sphinx-index"><b>Building an Index with Sphinx</b></a></p>
<p>The complexity of building and maintaining an index goes up with the frequency of the updates that are required to keep it up to date. Luckily the data set I&#039;m working with gets updated once a month so it is fairly static. Because the data is updated monthly I only have to update the index once a month so this makes the indexing simple.</p>
<p>This is the command I use to create the index:</p>
<pre>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;">indexer <span class="re5">--all</span> <span class="re5">--config</span> sphinx.conf</div>
</div>
</pre>
<p>Indexing the entire data set I have only takes a few minutes so re-indexing speed isn&#039;t an issue for me and the resulting index is in the 250M range as of this post so size isn&#039;t necessarily an issue either. However I currently create the index on one machine and then upload it to the server so a 250M transfer could become a bandwidth hog if I needed to update the index more often. If I were able to get deltas from the data dump I would look into using the <a href="http://www.sphinxsearch.com/docs/current.html#index-merging">index merging</a> feature and then transferring the delta index.</p>
<p><a name="sphinx-search"><b>Searching with Sphinx</b></a></p>
<p>Sphinx offers a lot of flexibility in configuring how a search is run and I found myself needing that flexibility to get better results. The search command line interface (see the <a href="http://www.sphinxsearch.com/docs/current.html#ref-search">reference</a> for all the options) can be used to test a few of the options but you can&#039;t do everything with it that you can with the various APIs available.</p>
<p>The first place I started changing defaults with the search is in how it matches the search words. The default is to find all words:</p>
<pre>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;">search <span class="re5">--config</span> sphinx.conf <span class="st0">&quot;php full text search&quot;</span></div>
</div>
</pre>
<p>Here is an example of the output you get from the CLI search:</p>
<div class="codesnip-container" >
<div class="text codesnip" style="font-family:monospace;">Copyright (c) 2001-2008, Andrew Aksyonoff</p>
<p>using config file &#039;sphinx.conf&#039;&#8230;<br />
index &#039;so_2009_10&#039;: query &#039;php full text search &#039;: returned 19 matches of 19 total in 0.061 sec</p>
<p>displaying matches:<br />
1. document=553055, weight=6, owner_id=2287, date_added=Mon Feb 16 11:45:01 2009<br />
&nbsp; &nbsp; &nbsp; &nbsp; id=553055<br />
&nbsp; &nbsp; &nbsp; &nbsp; post_type_id=1<br />
&nbsp; &nbsp; &nbsp; &nbsp; accepted_answer_id=553269<br />
&nbsp; &nbsp; &nbsp; &nbsp; parent_id=(null)<br />
&nbsp; &nbsp; &nbsp; &nbsp; score=6<br />
&nbsp; &nbsp; &nbsp; &nbsp; view_count=604<br />
&nbsp; &nbsp; &nbsp; &nbsp; body_text=truncated&#8230; body text would be here<br />
&nbsp; &nbsp; &nbsp; &nbsp; owner_id=2287<br />
&nbsp; &nbsp; &nbsp; &nbsp; last_editor_user_id=2287<br />
&nbsp; &nbsp; &nbsp; &nbsp; last_editor_display_name=PConroy<br />
&nbsp; &nbsp; &nbsp; &nbsp; last_edit_date=2009-02-18 10:54:44<br />
&nbsp; &nbsp; &nbsp; &nbsp; last_activity_date=2009-03-25 19:42:34<br />
&nbsp; &nbsp; &nbsp; &nbsp; title=Best full text search for mysql?<br />
&nbsp; &nbsp; &nbsp; &nbsp; answer_count=5<br />
&nbsp; &nbsp; &nbsp; &nbsp; comment_count=0<br />
&nbsp; &nbsp; &nbsp; &nbsp; favorite_count=7<br />
&nbsp; &nbsp; &nbsp; &nbsp; created=2009-02-16 11:45:01<br />
&#8230;</div>
</div>
<p><br/></p>
<p>The output shows you the &#034;document id&#034; as well as the weight and any &#034;sql_attr_&#034; values. I used the CLI search to get the weights for each search change I made before I started working with the API. Please note that the extended information in the above example output only shows up if the sql_query_info query is set correctly in the configuration file.</p>
<p>Next I tested with &#034;any&#034; and &#034;extended search version 2&#034; before settling on extended version 2:</p>
<pre>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;">search <span class="re5">--any</span> <span class="re5">--config</span> sphinx.conf <span class="st0">&quot;php full text search&quot;</span>

search <span class="re5">--ext2</span> <span class="re5">--config</span> sphinx.conf <span class="st0">&quot;php full text search&quot;</span></div>
</div>
</pre>
<p>The CLI search will also perform sorts. It is important to note that you can only sort on values that were indexed using the &#034;sql_attr_&#034;* configuration options. Here is an example of sorting by an indexed value:</p>
<pre>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;">search <span class="re5">--sortby</span> <span class="st0">&quot;date_added desc&quot;</span> <span class="re5">--config</span> sphinx.conf <span class="st0">&quot;php regex&quot;</span></div>
</div>
</pre>
<p>This is what happens when you try to sort based on an un-index attribute:</p>
<pre>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;">search <span class="re5">--sortby</span> <span class="st0">&quot;id desc&quot;</span> <span class="re5">--config</span> sphinx.conf <span class="st0">&quot;php regex&quot;</span>

Sphinx 0.9.8.1-release <span class="br0">&#40;</span>r1533<span class="br0">&#41;</span>
Copyright <span class="br0">&#40;</span>c<span class="br0">&#41;</span> 2001-2008, Andrew Aksyonoff

using config <span class="kw2">file</span> <span class="st_h">'sphinx.conf'</span>...
index <span class="st_h">'so_2009_10'</span>: search error: failed to create sorting queue: sort-by attribute <span class="st_h">'id'</span> not found.</div>
</div>
</pre>
<p>One of the things that the CLI search interface can&#039;t do is change <a href="http://www.sphinxsearch.com/docs/current.html#weighting">weightings</a> for the different data points. I found that weighting the title more than the body made sense and that is reflected in the PHP code that follows in the API section.  </p>
<p>The last part of searching to cover is searchd server. This is the integration point for the various Sphinx APIs and I assume it is running for the examples in the PHP API section. If the configuration example given in the first section is used all that needs to be done is to start the daemon. The following is a simple init script I use to start and stop searchd:</p>
<pre>
<div class="codesnip-container" >
<div class="bash codesnip" style="font-family:monospace;"><span class="co0">#!/bin/sh</span>

<span class="kw1">case</span> <span class="st0">&quot;$1&quot;</span> <span class="kw1">in</span>
<span class="st_h">'start'</span><span class="br0">&#41;</span>
searchd <span class="re5">--config</span> <span class="sy0">/</span>etc<span class="sy0">/</span>sphinx<span class="sy0">/</span>sphinx.conf
<span class="sy0">;;</span>
<span class="st_h">'stop'</span><span class="br0">&#41;</span>
<span class="kw2">killall</span> <span class="re5">-9</span> searchd;
<span class="sy0">;;</span>
<span class="sy0">*</span><span class="br0">&#41;</span>
<span class="kw3">echo</span> <span class="st0">&quot;Usage: $0 { start | stop}&quot;</span>
<span class="kw3">exit</span> 1
<span class="sy0">;;</span>
<span class="kw1">esac</span>
<span class="kw3">exit</span> <span class="nu0">0</span>
<span class="co0">#</span></div>
</div>
</pre>
<p><a name="sphinx-php-api"><b>Using the Sphinx PHP API</b></a></p>
<p>The Sphinx PHP API is included in the <a href="http://www.sphinxsearch.com/downloads.html">Sphinx source</a>. The API is contained in one file named sphinxapi.php that is located in the api directory of the source. Make sure the library is in a place where it can be included before trying out the examples. There is also some <a href="http://www.sphinxsearch.com/wiki/doku.php?id=php_api_docs">documentation</a> for the PHP API.</p>
<p>In the following example I&#039;m running the search query then serializing it to JSON format. Notice that the title is given a weight of 70 and the body_text is given a weight of 30 so the results will focus more on the title than what is in the body:</p>
<div class="codesnip-container" >
<div class="php codesnip" style="font-family:monospace;"><span class="kw2">&lt;?php</span></p>
<p><span class="kw1">require_once</span><span class="br0">&#40;</span><span class="st_h">&#039;sphinxapi.php&#039;</span><span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="co1">// Create the client, tell it where the server </span><br />
<span class="co1">// is and how long to wait for a response.</span><br />
<span class="re0">$sphinxClient</span> <span class="sy0">=</span> <span class="kw2">new</span> SphinxClient<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">SetServer</span><span class="br0">&#40;</span> <span class="st_h">&#039;localhost&#039;</span><span class="sy0">,</span> 3312 <span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">SetConnectTimeout</span><span class="br0">&#40;</span> 1 <span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="co1">// This gives the title more weight than the </span><br />
<span class="co1">// body text for searches.</span><br />
<span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">SetFieldWeights</span><span class="br0">&#40;</span><a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st_h">&#039;title&#039;</span> <span class="sy0">=&gt;</span> <span class="nu0">70</span><span class="sy0">,</span> <span class="st_h">&#039;body_text&#039;</span> <span class="sy0">=&gt;</span> 30<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="co1">// Use the exteneded v2 match type</span><br />
<span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">SetMatchMode</span><span class="br0">&#40;</span> SPH_MATCH_EXTENDED2 <span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="co1">// Set the maximum number of search results to return</span><br />
<span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">SetLimits</span><span class="br0">&#40;</span> 0<span class="sy0">,</span> 20<span class="sy0">,</span> 1000 <span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="co1">// Set how to rank the weighted values</span><br />
<span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">SetRankingMode</span><span class="br0">&#40;</span> SPH_RANK_PROXIMITY_BM25 <span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="co1">// Give me back the results as an array</span><br />
<span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">SetArrayResult</span><span class="br0">&#40;</span> <span class="kw4">true</span> <span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="re0">$searchQuery</span> <span class="sy0">=</span> <span class="re0">$_GET</span><span class="br0">&#91;</span><span class="st_h">&#039;query&#039;</span><span class="br0">&#93;</span><span class="sy0">;</span><br />
<span class="re0">$searchResults</span> <span class="sy0">=</span> <span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">Query</span><span class="br0">&#40;</span> <span class="re0">$searchQuery</span><span class="sy0">,</span> <span class="st_h">&#039;*&#039;</span> <span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="re0">$jhash</span> <span class="sy0">=</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="kw1">if</span> <span class="br0">&#40;</span> <span class="re0">$searchResults</span> <span class="sy0">===</span> <span class="kw4">false</span> <span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="re0">$jhash</span><span class="br0">&#91;</span><span class="st_h">&#039;status&#039;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st_h">&#039;failed&#039;</span><span class="sy0">;</span><br />
&nbsp; <span class="re0">$jhash</span><span class="br0">&#91;</span><span class="st_h">&#039;status_message&#039;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">GetLastError</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<span class="kw1">else</span><br />
<span class="br0">&#123;</span><br />
&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span> <span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">GetLastWarning</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="re0">$jhash</span><span class="br0">&#91;</span><span class="st_h">&#039;status&#039;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st_h">&#039;warning&#039;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="re0">$jhash</span><span class="br0">&#91;</span><span class="st_h">&#039;status_message&#039;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re0">$sphinxClient</span><span class="sy0">-&gt;</span><span class="me1">GetLastWarning</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="kw1">else</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="re0">$jhash</span><span class="br0">&#91;</span><span class="st_h">&#039;status&#039;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="st_h">&#039;good&#039;</span><span class="sy0">;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="re0">$jhash</span><span class="br0">&#91;</span><span class="st_h">&#039;result_total&#039;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re0">$searchResults</span><span class="br0">&#91;</span><span class="st_h">&#039;total&#039;</span><span class="br0">&#93;</span><span class="sy0">;</span><br />
&nbsp; <span class="re0">$jhash</span><span class="br0">&#91;</span><span class="st_h">&#039;result_found&#039;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re0">$searchResults</span><span class="br0">&#91;</span><span class="st_h">&#039;total_found&#039;</span><span class="br0">&#93;</span><span class="sy0">;</span></p>
<p>&nbsp; <span class="re0">$jhash_matches</span> <span class="sy0">=</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span> <a href="http://www.php.net/is_array"><span class="kw3">is_array</span></a><span class="br0">&#40;</span><span class="re0">$searchResults</span><span class="br0">&#91;</span><span class="st0">&quot;matches&quot;</span><span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="br0">&#41;</span><br />
&nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="re0">$row_ids</span> <span class="sy0">=</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">foreach</span> <span class="br0">&#40;</span> <span class="re0">$searchResults</span><span class="br0">&#91;</span><span class="st0">&quot;matches&quot;</span><span class="br0">&#93;</span> <span class="kw1">as</span> <span class="re0">$docinfo</span> <span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <a href="http://www.php.net/array_push"><span class="kw3">array_push</span></a><span class="br0">&#40;</span><span class="re0">$row_ids</span><span class="sy0">,</span> <a href="http://www.php.net/mysql_real_escape_string"><span class="kw3">mysql_real_escape_string</span></a><span class="br0">&#40;</span><span class="re0">$docinfo</span><span class="br0">&#91;</span><span class="st_h">&#039;id&#039;</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; <span class="re0">$jhash</span><span class="br0">&#91;</span><span class="st_h">&#039;matches&#039;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re0">$jhash_matches</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span></p>
<p><span class="kw1">echo</span> <a href="http://www.php.net/json_encode"><span class="kw3">json_encode</span></a><span class="br0">&#40;</span><span class="re0">$jhash</span><span class="br0">&#41;</span><span class="sy0">;</span></p>
<p><span class="sy1">?&gt;</span></div>
</div>
<p>Although I picked PHP there are a wide range of language specific libraries available.</p>
<p>Overall it didn&#039;t seem too difficult to set up Sphinx and have it serving search results quickly. I liked that it is very light weight and doesn&#039;t need a lot of resources or require a lot of extra parts to be installed.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/ioncannon?a=G_kGmfpWI_A:hUSqB_tE5N0:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=G_kGmfpWI_A:hUSqB_tE5N0:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/ioncannon?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=G_kGmfpWI_A:hUSqB_tE5N0:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=G_kGmfpWI_A:hUSqB_tE5N0:gIN9vFwOqvQ" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/ioncannon?a=G_kGmfpWI_A:hUSqB_tE5N0:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/ioncannon?i=G_kGmfpWI_A:hUSqB_tE5N0:F7zBnMyn0Lo" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/ioncannon/~4/G_kGmfpWI_A" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ioncannon.net/programming/685/full-text-search-with-sphinx/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://www.ioncannon.net/programming/685/full-text-search-with-sphinx/</feedburner:origLink></item>
	</channel>
</rss><!-- Dynamic page generated in 1.328 seconds. --><!-- Cached page generated by WP-Super-Cache on 2010-03-17 02:14:56 -->
