<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Posts on Andrei Ostanin</title>
		<link>https://ostanin.org/posts/</link>
		<description>Recent content in Posts on Andrei Ostanin</description>
		<generator>Hugo -- gohugo.io</generator>
		<language>en-us</language>
		<copyright>This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.</copyright>
		<lastBuildDate>Sat, 21 Jun 2014 13:10:56 +0000</lastBuildDate>
		<atom:link href="https://ostanin.org/posts/index.xml" rel="self" type="application/rss+xml" />
		
		<item>
			<title>Teensy&#43;&#43; 2.0 EPROM Reader</title>
			<link>https://ostanin.org/2014/06/21/teensy-2-0-eprom-reader/</link>
			<pubDate>Sat, 21 Jun 2014 13:10:56 +0000</pubDate>
			
			<guid>https://ostanin.org/2014/06/21/teensy-2-0-eprom-reader/</guid>
			<description>Need to read a 16 bit parallel EPROM and have a Teensy++ 2.0 and some shift registers handy? Perfect!
I bought a dead NAOMI game cartridge (Crackin&amp;rsquo; DJ) and started on a quest to repair it. Sometimes it would start booting then lock up, sometimes it would reject the cartridge entirely, sometimes it would show up in the test menu and fail all ROM, and sometimes it wouldn&amp;rsquo;t show up at all.</description>
			<content type="html"><![CDATA[<p>Need to read a 16 bit parallel EPROM and have a <a href="https://www.pjrc.com/teensy/">Teensy++ 2.0</a> and some shift registers handy? Perfect!</p>

<p>I bought a dead <a href="http://segaretro.org/Sega_NAOMI">NAOMI</a> game cartridge (Crackin&rsquo; DJ) and started on a quest to repair it. Sometimes it would start booting then lock up, sometimes it would reject the cartridge entirely, sometimes it would show up in the test menu and fail all ROM, and sometimes it wouldn&rsquo;t show up at all. Since the game description, ROM CRCs and startup code all live in a 32 Mbit <a href="http://www.datasheetlib.com/datasheet/263291/m27c322_stmicroelectronics.html">M27C322</a> EPROM (which is now 14 years old) that seemed like a likely place to start. Let&rsquo;s dump it!</p>

<p><img src="/images/2014/Jun/photo.jpg" alt="The setup in action" /></p>

<p>The address 20 address lines are output through three 74HC595 shift registers while the data bus and control lines are connected directly to the EPROM. A Python script dumps it in around 45 minutes.</p>

<p>Sadly, the EPROM dump matched the one from MAME perfectly. Now I have more troubleshooting to do. But, that means that the EPROM reader works perfectly! Get the code and schematics on <a href="https://github.com/aostanin/teensy2-eprom_reader">GitHub</a>.</p>
]]></content>
		</item>
		
		<item>
			<title>Playing with Mac OS X on KVM</title>
			<link>https://ostanin.org/2014/02/11/playing-with-mac-os-x-on-kvm/</link>
			<pubDate>Tue, 11 Feb 2014 15:06:32 +0000</pubDate>
			
			<guid>https://ostanin.org/2014/02/11/playing-with-mac-os-x-on-kvm/</guid>
			<description>I always wanted to have an always-on Mac OS X machine which I could run Jenkins on to build and test iOS apps. While I have an old MacBook Pro I could use, I&amp;rsquo;d rather not have another computer running 24&amp;frasl;7, especially one which would receive so little use.
While OS X runs in VMWare (with some hacks) and VirtualBox, it can also run on KVM with a few patches thanks to Gabriel Somlo&amp;rsquo;s excellent work.</description>
			<content type="html"><![CDATA[

<p>I always wanted to have an always-on Mac OS X machine which I could run Jenkins on to build and test iOS apps. While I have an old MacBook Pro I could use, I&rsquo;d rather not have another computer running <sup>24</sup>&frasl;<sub>7</sub>, especially one which would receive so little use.</p>

<p>While OS X runs in VMWare (with some hacks) and VirtualBox, it can also run on KVM with a few patches thanks to <a href="http://www.contrib.andrew.cmu.edu/~somlo/OSXKVM/">Gabriel Somlo&rsquo;s</a> excellent work. He just updated his site around a week ago and mentions that Mountain Lion runs well. Seems like the perfect time to try it out!</p>

<h1 id="prerequisites">Prerequisites</h1>

<h2 id="patched-qemu-kvm-seabios">Patched QEMU + KVM + SeaBIOS&hellip;</h2>

<p>Either follow the excellent instructions on <a href="http://www.contrib.andrew.cmu.edu/~somlo/OSXKVM/">Gabriel Somlo&rsquo;s site</a> to patch and compile the required components yourself, or if you happen to use Ubuntu 12.04, I&rsquo;ve prepared some <a href="https://mega.nz/#!H9NjGLgZ!7JhDJ_NWzVdRlUI8Ee4WZdiqlOJyRuSQrA6qlmJpG6E">binaries</a> which should speed the process along.</p>

<h3 id="using-the-pre-compiled-binaries">Using the Pre-Compiled Binaries</h3>

<p>First, you&rsquo;ll need <code>libpixman-1-0</code> to run QEMU.</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ sudo apt-get install libpixman-1-0</code></pre></div>
<p>Download and unpack the binaries.</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ tar xvJf osx-kvm.tar.xz
$ <span class="nb">cd</span> osx-kvm</code></pre></div>
<p>Copy and load the patched KVM modules.</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ sudo modprobe -r kvm_intel
$ sudo cp kvm-kmod/kvm*.ko /lib/modules/<span class="sb">`</span>uname -r<span class="sb">`</span>/kernel/arch/x86/kvm/
$ dmesg <span class="p">|</span> tail -n1
<span class="o">[</span><span class="m">11987</span>.998234<span class="o">]</span> loaded kvm module <span class="o">(</span><span class="k">for</span>-linus<span class="o">)</span></code></pre></div>
<h2 id="blank-drive-image">Blank Drive Image</h2>

<p>You can use <code>qemu-img</code> to create a blank drive image or use a zvol, actual device, whatever.</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ ./bin/qemu-img create -f qcow2 mac_hdd.img 20G
Formatting <span class="s1">&#39;mac_hdd.img&#39;</span>, <span class="nv">fmt</span><span class="o">=</span>qcow2 <span class="nv">size</span><span class="o">=</span><span class="m">21474836480</span> <span class="nv">encryption</span><span class="o">=</span>off <span class="nv">cluster_size</span><span class="o">=</span><span class="m">65536</span> <span class="nv">lazy_refcounts</span><span class="o">=</span>off</code></pre></div>
<h2 id="mac-os-x-installation-disc-image">Mac OS X Installation Disc Image</h2>

<p>I&rsquo;m using Mac OS X Mountain Lion 10.8.5 for this. It&rsquo;s important to have a clean disk image, not a hacked osx86 image because some of the kexts on the hacked images will cause a kernel panic on boot. It&rsquo;s best to use the <code>InstallESD.dmg</code> located inside of the <code>Install OS X Mountain Lion.app</code> bundle.</p>

<p>The image needs to be an ISO file. If you have a DMG image then you can use Disk Utility&rsquo;s &ldquo;Convert&rdquo; feature to make an ISO image (<code>.cdr</code> by default, but it&rsquo;s the same thing).</p>

<h2 id="vnc-client">VNC Client</h2>

<p><a href="http://sourceforge.net/projects/chicken/">Chicken</a> is a great VNC client for OS X. It even supports creating an SSH tunnel automatically which is very handy.</p>

<h2 id="osk-key">OSK Key</h2>

<p>The AppleSMC device needs a valid OSK key to function. This key is the same in all Macs and can be easily retrieved.</p>

<p>For OS X users, the <a href="http://www.osxbook.com/book/bonus/chapter7/tpmdrmmyth/">Mac OS X Internals</a> website has the source code to a program which can retrieve the key. I&rsquo;ve included a binary of it in the <a href="https://mega.nz/#!H9NjGLgZ!7JhDJ_NWzVdRlUI8Ee4WZdiqlOJyRuSQrA6qlmJpG6E">above download</a>.</p>

<p>For Linux on Mac users, <a href="http://www.contrib.andrew.cmu.edu/~somlo/OSXKVM/">Gabriel Somlo&rsquo;s site</a> has a C program which can retrieve this key.</p>

<h1 id="installing">Installing</h1>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">sudo bin/qemu-system-x86_64 -enable-kvm <span class="se">\
</span><span class="se"></span>	-m <span class="m">2048</span> -cpu core2duo -machine q35 <span class="se">\
</span><span class="se"></span>	-smp <span class="m">2</span> <span class="se">\
</span><span class="se"></span>	-usbdevice keyboard -usbdevice mouse <span class="se">\
</span><span class="se"></span>	-vga std <span class="se">\
</span><span class="se"></span>	-device isa-applesmc,osk<span class="o">=</span><span class="s2">&#34;INSERT OSK KEY HERE&#34;</span> <span class="se">\
</span><span class="se"></span>	-bios bios-mac.bin -kernel ./chameleon_svn2360_boot <span class="se">\
</span><span class="se"></span>	-device ide-drive,bus<span class="o">=</span>ide.2,drive<span class="o">=</span>MacHDD <span class="se">\
</span><span class="se"></span>	-drive <span class="nv">id</span><span class="o">=</span>MacHDD,if<span class="o">=</span>none,cache<span class="o">=</span>none,file<span class="o">=</span>./mac_hdd.img <span class="se">\
</span><span class="se"></span>	-device ide-drive,bus<span class="o">=</span>ide.0,drive<span class="o">=</span>MacDVD <span class="se">\
</span><span class="se"></span>	-drive <span class="nv">id</span><span class="o">=</span>MacDVD,if<span class="o">=</span>none,snapshot<span class="o">=</span>on,file<span class="o">=</span>./mountain-lion-10.8.5.iso -boot <span class="nv">once</span><span class="o">=</span>d</code></pre></div>
<p>Now connect over VNC and you should see the Chameleon boot prompt.</p>

<p><img src="/images/2014/Feb/chameleon_boot_prompt.png" alt="Chameleon boot prompt" /></p>

<p>You can use <code>-v</code> for a verbose boot which can aid in troubleshooting. If you get a kernel panic here, make sure your OS X iso image is clean, not a hacked osx86 image.</p>

<p>If your terminal is filled with <code>usb-kbd: warning: key event queue full</code> and the keyboard doesn&rsquo;t respond, just try again. While the keyboard works reliably once OS X boots, it sometimes has problems on the Chameleon boot prompt.</p>

<p>Hopefully the installer should just start right up.</p>

<p><img src="/images/2014/Feb/installer_welcome_screen.png" alt="Installer welcome screen" /></p>

<p>Continue with the installer and start up Disk Utility to partition your drive.</p>

<p><img src="/images/2014/Feb/disk_utility_partitioning.png" alt="Disk Utility partitioning" /></p>

<p>Close Disk Utility and continue with the Reinstall OS X option. Install to the disk partition you just created.</p>

<p><img src="/images/2014/Feb/installer_choose_drive.png" alt="Installer choose drive" /></p>

<p>The installer will reboot and put you back on the Chameleon prompt. Choose the <code>(Installer) Macintosh HD</code> partition to continue with the install.</p>

<p><img src="/images/2014/Feb/chameleon_after_installer_reboot.png" alt="Chameleon after installer reboot" /></p>

<p>The install will continue and reboot once more. Choose the <code>Macintosh HD</code> drive in Chameleon again to configure the install. I skipped setting up an Apple ID and registering.</p>

<p>Now you should have a mostly functional Mac OS X installation!</p>

<p><img src="/images/2014/Feb/successful_install.png" alt="Successful Install" /></p>

<p>&hellip;though as you can see, it&rsquo;s not exactly perfect.</p>

<h1 id="configuring">Configuring</h1>

<h2 id="energy-saver">Energy Saver</h2>

<p>Disable &ldquo;Computer sleep&rdquo; and &ldquo;Display sleep&rdquo; in the System Preferences. Resuming from sleep doesn&rsquo;t work and will cause the virtual machine to lock up.</p>

<p><img src="/images/2014/Feb/energy_saver.png" alt="Energy Saver" /></p>

<h2 id="absolute-mouse-positioning">Absolute Mouse Positioning</h2>

<p>Most KVM guests can make use of the <code>tablet</code> device to synchronize the position of the mouse with it&rsquo;s position over the VNC window. OS X, unfortunately, does not support this device.</p>

<p>If you enable OS X&rsquo;s internal screen sharing and use it in place of QEMU&rsquo;s VNC server then mouse positioning works perfectly. You can allow normal VNC viewers to connect by clicking on &ldquo;Computer Settings&hellip;&rdquo; in the Screen Sharing options.</p>

<p><img src="/images/2014/Feb/enabling_vnc.png" alt="Enabling VNC" /></p>

<h2 id="networking">Networking</h2>

<p>The patches to QEMU include fixes to make the <code>e1000</code> device work under OS X. To enable it for bridged networking, <a href="https://help.ubuntu.com/community/KVM/Networking#Creating_a_network_bridge_on_the_host">add a network bridge for KVM</a> just add something like the following to the QEMU command:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">-net nic,model<span class="o">=</span>e1000,netdev<span class="o">=</span>net0,macaddr<span class="o">=</span>DE:AD:BE:EF:CA:FE -netdev tap,id<span class="o">=</span>net0</code></pre></div>
<h3 id="virtio">Virtio</h3>

<p>There is a <a href="https://github.com/pmj/virtio-net-osx">virtio-net driver for Mac OS X</a> which works great. Just install the driver, shutdown, and change the NIC model from <code>e1000</code> to <code>virtio</code> in the QEMU command.</p>

<p><img src="/images/2014/Feb/virtio_nic.png" alt="Virtio NIC" /></p>

<h2 id="display">Display</h2>

<p>QEMU emulates a Cirrus Logic GD5446 video card by default. Which this works fine over QEMU&rsquo;s VNC server (though with a low color depth), the display glitches when using OS X&rsquo;s Screen Sharing feature.</p>

<p><img src="/images/2014/Feb/cirrus_corrupt_display.png" alt="Cirrus Corrupt Display" /></p>

<p>QEMU also supports the <code>vmware</code> video card. There&rsquo;s even a <a href="http://sourceforge.net/projects/vmsvga2/">VMsvga2 driver</a> for it on OS X. However, this driver caused a kernel panic on boot so this option too fails.</p>

<p>The best option that I have found is to use the <code>std</code> video card.</p>

<h2 id="chimera">Chimera</h2>

<p><a href="http://www.tonymacx86.com/wiki/index.php/Chimera">Chimera</a> is a bootloader which was forked from Chameleon. It&rsquo;s easy to install and will allow us to boot to OS X automatically and remove the <code>-kernel</code> argument to QEMU. <a href="http://www.tonymacx86.com/downloads.php?do=file&amp;id=195">Download</a> and install Chimera to get started.</p>

<h3 id="org-chameleon-boot-plist"><code>org.chameleon.boot.plist</code></h3>

<p>To automatically boot into OS X without needing to press enter at the prompt, edit (create) the <a href="http://www.tonymacx86.com/wiki/index.php/Chimera">org.chameleon.boot.plist</a> file at <code>/Extra/org.chameleon.boot.plist</code>. A simple example to automatically boot would be:</p>
<div class="highlight"><pre class="chroma"><code class="language-xml" data-lang="xml"><span class="cp">&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;</span>
<span class="cp">&lt;!DOCTYPE plist PUBLIC &#34;-//Apple//DTD PLIST 1.0//EN&#34; &#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd&#34;&gt;</span>
<span class="nt">&lt;plist</span> <span class="na">version=</span><span class="s">&#34;1.0&#34;</span><span class="nt">&gt;</span>
<span class="nt">&lt;dict&gt;</span>
	<span class="nt">&lt;key&gt;</span>Timeout<span class="nt">&lt;/key&gt;</span>
	<span class="nt">&lt;string&gt;</span>5<span class="nt">&lt;/string&gt;</span>
	<span class="nt">&lt;key&gt;</span>EthernetBuiltIn<span class="nt">&lt;/key&gt;</span>
	<span class="nt">&lt;string&gt;</span>Yes<span class="nt">&lt;/string&gt;</span>
	<span class="nt">&lt;key&gt;</span>PCIRootUID<span class="nt">&lt;/key&gt;</span>
	<span class="nt">&lt;string&gt;</span>1<span class="nt">&lt;/string&gt;</span>
<span class="nt">&lt;/dict&gt;</span></code></pre></div>
<p>The <code>EthernetBuiltIn</code> and <code>PCIRootUID</code> keys fix the &ldquo;Your device or computer could not be verified. Contact support for assistance.&rdquo; error when logging in to the App Store.</p>

<h3 id="smbios-plist"><code>smbios.plist</code></h3>

<p>There&rsquo;s another configuration file for Chimera, <code>/Extra/smbios.plist</code>. While I haven&rsquo;t found a need to edit it personally (the App Store works fine without it), it can be used to change what model Mac the system identifies itself as as well as its serial number. There&rsquo;s plenty of <a href="http://www.tonymacx86.com/wiki/index.php/Smbios.plist">documentation</a> as well as <a href="http://www.macbreaker.com/2012/03/champlist-vs-chameleon-wizard-vs.html">apps which help you edit the file</a>.</p>

<h2 id="running">Running</h2>

<p>After the above steps, here is the command I use to boot the virtual machine:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">sudo bin/qemu-system-x86_64 -enable-kvm <span class="se">\
</span><span class="se"></span>	-m <span class="m">2048</span> -cpu core2duo -machine q35 <span class="se">\
</span><span class="se"></span>	-smp <span class="m">2</span> <span class="se">\
</span><span class="se"></span>	-usbdevice keyboard -usbdevice mouse <span class="se">\
</span><span class="se"></span>	-vga std <span class="se">\
</span><span class="se"></span>	-device isa-applesmc,osk<span class="o">=</span><span class="s2">&#34;INSERT OSK KEY HERE&#34;</span> <span class="se">\
</span><span class="se"></span>	-bios bios-mac.bin <span class="se">\
</span><span class="se"></span>	-device ide-drive,bus<span class="o">=</span>ide.2,drive<span class="o">=</span>MacHDD <span class="se">\
</span><span class="se"></span>	-drive <span class="nv">id</span><span class="o">=</span>MacHDD,if<span class="o">=</span>none,cache<span class="o">=</span>none,file<span class="o">=</span>./mac_hdd.img <span class="se">\
</span><span class="se"></span>	-net nic,model<span class="o">=</span>virtio,netdev<span class="o">=</span>net0,macaddr<span class="o">=</span>DE:AD:BE:EF:CA:FE -netdev tap,id<span class="o">=</span>net0</code></pre></div>
<h1 id="conclusion">Conclusion</h1>

<p>Overall, I&rsquo;m very satisfied with running OS X on KVM. It performs more than well enough for my needs, seems to be stable, and doesn&rsquo;t seem to waste excessive CPU cycles on the host.</p>
]]></content>
		</item>
		
		<item>
			<title>Routers and Their Lies</title>
			<link>https://ostanin.org/2014/02/11/on-routers-and-lies/</link>
			<pubDate>Tue, 11 Feb 2014 12:07:21 +0000</pubDate>
			
			<guid>https://ostanin.org/2014/02/11/on-routers-and-lies/</guid>
			<description>I used to use a Buffalo WZR-HP-G302H router. It sucked. Wi-Fi would drop completely every 2-3 hours until I power cycled the thing. And the default Buffalo firmware is complete garbage.
While many overseas models support the Buffalo-branded DD-WRT &amp;ldquo;professional&amp;rdquo; firmware, the Japanese models don&amp;rsquo;t. Not only that, they also have locked bootloaders and can&amp;rsquo;t be flashed over TFTP, don&amp;rsquo;t provide a serial console during boot, and don&amp;rsquo;t accept unencrypted firmware in their web interface.</description>
			<content type="html"><![CDATA[<p>I used to use a Buffalo WZR-HP-G302H router. It sucked. Wi-Fi would drop completely every 2-3 hours until I power cycled the thing. And the default Buffalo firmware is complete garbage.</p>

<p>While many overseas models support the Buffalo-branded DD-WRT &ldquo;professional&rdquo; firmware, the Japanese models don&rsquo;t. Not only that, they also have locked bootloaders and can&rsquo;t be flashed over TFTP, don&rsquo;t provide a serial console during boot, and don&rsquo;t accept unencrypted firmware in their web interface.</p>

<p>This was a couple of years ago now, so there wasn&rsquo;t the wealth of information on this router as there is now. I later found out that you can <a href="http://www.dd-wrt.com/phpBB2/viewtopic.php?p=745189">access a hidden maintenance page and unlock the bootloader</a>. Then, <a href="http://wiki.openwrt.org/toh/buffalo/wzr-hp-g300nh2#revision.a1a0.and.wzr-hp-g302h.a1a0.japanese.variant">OpenWRT&rsquo;s page about the router</a> says that OpenWRT should work if you recompile it with the erase size for the SPI flash changed from 128 KB to 64 KB. So I tried it. It didn&rsquo;t boot anymore.</p>

<p>I wanted to open up the router and hook up a USB-serial adapter to see what exactly caused it to fail but the T10 S2 screws (those diamond shaped ones with the thing in the middle) required an expensive screwdriver and it just wasn&rsquo;t worth it at the time. I tried reflashing it over TFTP but I couldn&rsquo;t find any firmware for it which worked. It was still set to the Japanese region and the Japanese firmware is distributed in an encrypted form only. I gave up and bought a new router.</p>

<p>Now a few months ago, having acquired the necessary screwdriver, I decided to finally hook up the serial console and revive the router. There was no need, upon opening it up the router revealed its true nature&hellip;</p>

<p><img src="/images/2014/Feb/wzr_hp_g302h_internal.jpg" alt="WZR-HP-G302H internal" /></p>

<p>WZR-HP-G300NH!?</p>

<p><img src="/images/2014/Feb/wzr_hp_g302h_external.jpg" alt="WZR-HP-G302H external" /></p>

<p>It definitely says WZR-HP-G302H on the case&hellip;</p>

<p>Flashing it with OpenWRT&rsquo;s WZR-HP-G300NH firmware worked perfectly. Now I have a nice Linux device with Wi-Fi just waiting for a project.</p>
]]></content>
		</item>
		
		<item>
			<title>Plex Media Server in Docker</title>
			<link>https://ostanin.org/2013/09/14/plex-media-server-in-docker/</link>
			<pubDate>Sat, 14 Sep 2013 03:00:00 +0000</pubDate>
			
			<guid>https://ostanin.org/2013/09/14/plex-media-server-in-docker/</guid>
			<description>Docker is the reason I switched from SmartOS back to Linux. The ability to manage applications with a workflow similar to git is very attractive. It solves most of the problems I had with SmartOS including complicated backups and needing KVM for some applications which don&amp;rsquo;t run under Solaris zones.
For me, the biggest problem with Docker right now is that the networking support is inflexible. While I managed to run applications like SABnzbd, Sick Beard, Couch Potato, and even rTorrent + ruTorrent among others with ease, Plex Media Server uses Avahi and it&amp;rsquo;s own GDM network discovery protocols to broadcast its existence to clients.</description>
			<content type="html"><![CDATA[

<p><a href="http://docker.io/">Docker</a> is the reason I switched from SmartOS back to Linux. The ability to manage applications with a workflow similar to git is very attractive. It solves most of the problems I had with SmartOS including complicated backups and needing KVM for some applications which don&rsquo;t run under Solaris zones.</p>

<p>For me, the biggest problem with Docker right now is that the networking support is inflexible. While I managed to run applications like SABnzbd, Sick Beard, Couch Potato, and even rTorrent + ruTorrent among others with ease, Plex Media Server uses Avahi and it&rsquo;s own GDM network discovery protocols to broadcast its existence to clients. This requires the Plex server and clients to run on the same subnet which isn&rsquo;t a configuration that Docker supports easily. After a few tries, I managed to get it to work with some ugly hacks.</p>

<h1 id="setting-up-a-network-bridge-on-the-host">Setting Up a Network Bridge on the Host</h1>

<p>Ubuntu&rsquo;s <a href="http://help.ubuntu.com/community/KVM/Networking">KVM networking guide</a> comes in handy here. Following it, I created a <code>br0</code> bridge with the following configuration added to the host&rsquo;s <code>/etc/network/interfaces</code>:</p>

<pre><code>auto br0
iface br0 inet dhcp
  bridge_ports eth0
  bridge_stp off
  bridge_fd 0
  bridge_maxwait 0
</code></pre>

<p>After restarting networking (<code>service networking restart</code>), the new bridge showed up in <code>ifconfig</code>:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">host$ ifconfig
br0       Link encap:Ethernet  HWaddr 1c:6f:65:d8:af:fd
          inet addr:192.168.1.131  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::1e6f:65ff:fed8:affd/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:710009 errors:0 dropped:0 overruns:0 frame:0
          TX packets:664838 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:643136133 <span class="o">(</span><span class="m">643</span>.1 MB<span class="o">)</span>  TX bytes:198627145 <span class="o">(</span><span class="m">198</span>.6 MB<span class="o">)</span></code></pre></div>
<p>Now we need some way to use this bridge in the Plex container.</p>

<h1 id="wrestling-with-docker-s-networking-configuration">Wrestling With Docker&rsquo;s Networking Configuration</h1>

<p>My first approach was to try using <a href="https://github.com/jpetazzo/pipework">Pipeline</a> for the network configuration. My approach was to use Pipework on the host as follows:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">host$ docker run -d plex
bcb765ea1b73
host$ pipework br0 bcb765ea1b73 <span class="m">192</span>.168.1.10</code></pre></div>
<p>And from the point of view of the container, here is the network configuration before running Pipework:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">root@bcb765ea1b73:/# ifconfig
eth0      Link encap:Ethernet  HWaddr <span class="m">82</span>:48:50:71:6c:55
          inet addr:172.17.0.8  Bcast:172.17.255.255  Mask:255.255.0.0
          inet6 addr: fe80::8048:50ff:fe71:6c55/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:195 errors:0 dropped:0 overruns:0 frame:0
          TX packets:83 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:267817 <span class="o">(</span><span class="m">267</span>.8 KB<span class="o">)</span>  TX bytes:5723 <span class="o">(</span><span class="m">5</span>.7 KB<span class="o">)</span>

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:0 <span class="o">(</span><span class="m">0</span>.0 B<span class="o">)</span>  TX bytes:0 <span class="o">(</span><span class="m">0</span>.0 B<span class="o">)</span>the point of view of the container:</code></pre></div>
<p>And after running Pipework:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">root@bcb765ea1b73:/# ifconfig
eth0      Link encap:Ethernet  HWaddr <span class="m">82</span>:48:50:71:6c:55
          inet addr:172.17.0.8  Bcast:172.17.255.255  Mask:255.255.0.0
          inet6 addr: fe80::8048:50ff:fe71:6c55/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:195 errors:0 dropped:0 overruns:0 frame:0
          TX packets:83 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:267817 <span class="o">(</span><span class="m">267</span>.8 KB<span class="o">)</span>  TX bytes:5723 <span class="o">(</span><span class="m">5</span>.7 KB<span class="o">)</span>

eth1      Link encap:Ethernet  HWaddr <span class="m">06</span>:15:b2:28:fe:a5
          inet addr:192.168.1.10  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::415:b2ff:fe28:fea5/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:3 errors:0 dropped:0 overruns:0 frame:0
          TX packets:3 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:238 <span class="o">(</span><span class="m">238</span>.0 B<span class="o">)</span>  TX bytes:238 <span class="o">(</span><span class="m">238</span>.0 B<span class="o">)</span>

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:0 <span class="o">(</span><span class="m">0</span>.0 B<span class="o">)</span>  TX bytes:0 <span class="o">(</span><span class="m">0</span>.0 B<span class="o">)</span></code></pre></div>
<p>The <code>eth1</code> interface was added and given an IP address in the same subnet as my LAN. However, since Plex doesn&rsquo;t seem to have any configuration to specify which network interface to broadcast on. I used route in the container to make <code>eth1</code> the default interface and set the gateway:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">root@bcb765ea1b73:/# route add default gw <span class="m">192</span>.168.1.1 eth1</code></pre></div>
<p>Now I could open up a web browser to <code>http://192.168.1.10/web</code> and see that Plex was indeed running. However, I couldn&rsquo;t seem to publish the server to myPlex (UPnP not working?) and the server wouldn&rsquo;t show up in Plex clients. There&rsquo;s probably some proper way to get this to work, but since I&rsquo;m not competent with networking I decided to do an ugly hack to make it all work.</p>

<h1 id="ugly-hack">Ugly Hack</h1>

<p>Docker allows us to specify LXC configuration options directly with the <code>-lxc-conf</code> parameter to <code>docker run</code>. You can also disable Docker&rsquo;s network configuration with the <code>-n=false</code> parameter. Combining these two I got the following <code>docker run</code> command:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">docker run -d -n<span class="o">=</span><span class="nb">false</span> <span class="se">\
</span><span class="se"></span>  -lxc-conf<span class="o">=</span><span class="s2">&#34;lxc.network.type = veth&#34;</span> <span class="se">\
</span><span class="se"></span>  -lxc-conf<span class="o">=</span><span class="s2">&#34;lxc.network.flags = up&#34;</span> <span class="se">\
</span><span class="se"></span>  -lxc-conf<span class="o">=</span><span class="s2">&#34;lxc.network.link = br0&#34;</span> <span class="se">\
</span><span class="se"></span>  -lxc-conf<span class="o">=</span><span class="s2">&#34;lxc.network.ipv4 = 192.168.1.10&#34;</span> <span class="se">\
</span><span class="se"></span>  -lxc-conf<span class="o">=</span><span class="s2">&#34;lxc.network.ipv4.gateway=192.168.1.1&#34;</span> <span class="se">\
</span><span class="se"></span>  plex</code></pre></div>
<p>Now Plex would show up perfectly in my browser, Plex clients, and publishing to myPlex worked perfectly. The image I prepared stores the Plex configuration in <code>/config</code> which I bind mount into persistent storage on the host. After bind mounting everything, this is the command I use to start Plex:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">docker run -d -n<span class="o">=</span><span class="nb">false</span> <span class="se">\
</span><span class="se"></span>  -v /srv/media/videos:/videos <span class="se">\
</span><span class="se"></span>  -v /srv/media/music:/music <span class="se">\
</span><span class="se"></span>  -v /tank/virt/config/plex:/config <span class="se">\
</span><span class="se"></span>  -lxc-conf<span class="o">=</span><span class="s2">&#34;lxc.network.type = veth&#34;</span> <span class="se">\
</span><span class="se"></span>  -lxc-conf<span class="o">=</span><span class="s2">&#34;lxc.network.flags = up&#34;</span> <span class="se">\
</span><span class="se"></span>  -lxc-conf<span class="o">=</span><span class="s2">&#34;lxc.network.link = br0&#34;</span> <span class="se">\
</span><span class="se"></span>  -lxc-conf<span class="o">=</span><span class="s2">&#34;lxc.network.ipv4 = 192.168.1.10&#34;</span> <span class="se">\
</span><span class="se"></span>  -lxc-conf<span class="o">=</span><span class="s2">&#34;lxc.network.ipv4.gateway=192.168.1.1&#34;</span> <span class="se">\
</span><span class="se"></span>  plex</code></pre></div>
<p>You can find the Dockerfile to build the container on <a href="https://github.com/aostanin/dockerfiles/tree/master/services/plexmediaserver">Github</a>.</p>
]]></content>
		</item>
		
		<item>
			<title>Some Chef and Gentoo Stuff</title>
			<link>https://ostanin.org/2013/09/08/some-chef-and-gentoo-stuff/</link>
			<pubDate>Sun, 08 Sep 2013 03:00:00 +0000</pubDate>
			
			<guid>https://ostanin.org/2013/09/08/some-chef-and-gentoo-stuff/</guid>
			<description>I experimented with using Puppet and later Chef to manage my home server and all the virtual machines on it. After automating the installation and management of most of the services I use (rutorrent, sabnzbd, sickbeard, couchpotato, gitlab, etc.), I decided that both Puppet and Chef were just too much trouble for my needs. They were too heavyweight for managing single instances of each service.
However, I wrote quite a bit of code during that time which may be of some use.</description>
			<content type="html"><![CDATA[

<p>I experimented with using Puppet and later Chef to manage my home server and all the virtual machines on it. After automating the installation and management of most of the services I use (rutorrent, sabnzbd, sickbeard, couchpotato, gitlab, etc.), I decided that both Puppet and Chef were just too much trouble for my needs. They were too heavyweight for managing single instances of each service.</p>

<p>However, I wrote quite a bit of code during that time which may be of some use. Though most of it is very specific to my setup, I published some of it on Github.</p>

<h1 id="chef-gentoo">chef-gentoo</h1>

<p>Gentoo Linux is my Linux distribution of choice. However, Chef doesn&rsquo;t have much support for Gentoo out of the box. In fact, portage support was outright broken when I first tried it (packages which have the same name in two different categories such as <code>dev-db/mysql</code> and <code>virtual/mysql</code> would always fail). I made a simple cookbook which fixed those issuses (based on a Chef bug report) and made it easy to add keywords, use flags, mask, and unmask for packages and manage eselect as well. You can check it out on <a href="https://github.com/aostanin/chef-gentoo">Github</a>.</p>

<h1 id="chef-dotfiles">chef-dotfiles</h1>

<p>Chef&rsquo;s <a href="http://community.opscode.com/cookbooks/users">Users</a> cookbook is a great way to add users from a Chef data bag. I wanted each user to have their dotfiles git repository checked out, linked to the home directory, and updated during each Chef run. The code is again on <a href="https://github.com/aostanin/chef-dotfiles">Github</a>.</p>

<h1 id="gentoo-install-script">Gentoo Install Script</h1>

<p>When testing my Chef cookbooks, I&rsquo;d find myself often reinstalling Gentoo from scratch in a virtual machine. To automate this process, I created a simple install script for Gentoo. While some things are hardcoded and it assumes you want an amd64 install, it may be of some use. The code is up on <a href="https://github.com/aostanin/gentoo_install_script">Github</a>.</p>

<p>I still have a few other projects I&rsquo;ve been meaning to upload for a while now. The biggest problem is writing meaningful README files for code I haven&rsquo;t touched in months.</p>
]]></content>
		</item>
		
		<item>
			<title>Importing a FreeBSD ZFS Pool in illumos</title>
			<link>https://ostanin.org/2013/06/14/importing-a-freebsd-zfs-pool-in-illumos/</link>
			<pubDate>Fri, 14 Jun 2013 03:00:00 +0000</pubDate>
			
			<guid>https://ostanin.org/2013/06/14/importing-a-freebsd-zfs-pool-in-illumos/</guid>
			<description>Before we begin, please remember that the usual warnings about data loss apply. If you do follow these directions, I highly recommend that you at least have a zpool with redundancy (raidz, mirror) and try this only on a single disk at first, to make sure the zpool is still importable in FreeBSD/Linux before you alter every single disk.
My ZFS pool has been through a lot. I created it over two years ago on FreeBSD.</description>
			<content type="html"><![CDATA[<p>Before we begin, please remember that the usual warnings about data loss apply. If you do follow these directions, I highly recommend that you at least have a zpool with redundancy (raidz, mirror) and try this only on a single disk at first, to make sure the zpool is still importable in FreeBSD/Linux before you alter every single disk.</p>

<p>My ZFS pool has been through a lot. I created it over two years ago on FreeBSD. Then when <a href="http://zfsonlinux.org/">ZFS on Linux</a> seemed to reach a usable state, I switched over to Gentoo Linux. And now, growing tired of managing the Gentoo install whose main purpose is just to launch LXC and KVM virtual machines, I decided to try out the illumos-based <a href="http://smartos.org/">SmartOS</a>.</p>

<p>I started out by trying an <a href="http://openindiana.org/">OpenIndiana</a> LiveCD to make sure I wouldn&rsquo;t have any problems importing my zpool. Little did I know&hellip;</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">root@openindiana:/jack# zpool import
   pool: tank
     id: <span class="m">12050779155201421632</span>
  state: UNAVAIL
 status: One or more devices are missing from the system.
 action: The pool cannot be imported. Attach the missing
        devices and try again.
   see: http://illumos.org/msg/ZFS-8000-3C
 config:

        tank          UNAVAIL  insufficient replicas
          raidz1-0    UNAVAIL  insufficient replicas
            c5t1d0p0  UNAVAIL  cannot open
            c5t2d0p0  UNAVAIL  cannot open
            c5t3d0p0  UNAVAIL  cannot open
            c5t4d0p0  UNAVAIL  cannot open
            c5t5d0p0  UNAVAIL  cannot open</code></pre></div>
<p>It turns out that Solaris, and thus illumos, adds an EFI label to all disks in a zpool, even if you use the whole drive. Some searching suggested that it would be possible to format a drive in Solaris, add an EFI label, resilver the zpool, and repeat with with the other drives, one at a time. Since resilvering my array would probably take ~12+ hours, having to resilver five times was not something I was looking forward to. I decided to try it out on one drive for now anyway.</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">root@openindiana:/jack# format -e
Searching <span class="k">for</span> disks...done

c5t1d0: configured with capacity of <span class="m">2794</span>.52GB
c5t2d0: configured with capacity of <span class="m">2794</span>.52GB
c5t3d0: configured with capacity of <span class="m">2794</span>.52GB
c5t4d0: configured with capacity of <span class="m">2794</span>.52GB


AVAILABLE DISK SELECTIONS:
       <span class="m">0</span>. c4t0d0 &lt;SanDisk-U3CruzerMicro-4.04 cyl <span class="m">473</span> alt <span class="m">2</span> hd <span class="m">128</span> sec <span class="m">32</span>&gt;
          /pci@0,0/pci1458,5006@1d/hub@1/storage@1/disk@0,0
       <span class="m">1</span>. c5t1d0 &lt;ATA-Hitachi HDS5C303-A580-2.73TB&gt;
          /pci@0,0/pci1458,b005@1f,2/disk@1,0
       <span class="m">2</span>. c5t2d0 &lt;ATA-Hitachi HDS5C303-A580-2.73TB&gt;
          /pci@0,0/pci1458,b005@1f,2/disk@2,0
       <span class="m">3</span>. c5t3d0 &lt;ATA-Hitachi HDS5C303-A580-2.73TB&gt;
          /pci@0,0/pci1458,b005@1f,2/disk@3,0
       <span class="m">4</span>. c5t4d0 &lt;ATA-Hitachi HDS5C303-A580-2.73TB&gt;
          /pci@0,0/pci1458,b005@1f,2/disk@4,0
       <span class="m">5</span>. c5t5d0 &lt;ATA-Hitachi HDS5C303-A580-2.73TB&gt;
          /pci@0,0/pci1458,b005@1f,2/disk@5,0
Specify disk <span class="o">(</span>enter its number<span class="o">)</span>: <span class="m">5</span>
selecting c5t5d0
<span class="o">[</span>disk formatted<span class="o">]</span>
No Solaris fdisk partition found.


FORMAT MENU:
        disk       - <span class="k">select</span> a disk
        <span class="nb">type</span>       - <span class="k">select</span> <span class="o">(</span>define<span class="o">)</span> a disk <span class="nb">type</span>
        partition  - <span class="k">select</span> <span class="o">(</span>define<span class="o">)</span> a partition table
        current    - describe the current disk
        format     - format and analyze the disk
        fdisk      - run the fdisk program
        repair     - repair a defective sector
        label      - write label to the disk
        analyze    - surface analysis
        defect     - defect list management
        backup     - search <span class="k">for</span> backup labels
        verify     - <span class="nb">read</span> and display labels
        inquiry    - show vendor, product and revision
        scsi       - independent SCSI mode selects
        cache      - enable, disable or query SCSI disk cache
        volname    - <span class="nb">set</span> <span class="m">8</span>-character volume name
        !&lt;cmd&gt;     - execute &lt;cmd&gt;, <span class="k">then</span> <span class="k">return</span>
        quit
format&gt; fdisk
No fdisk table exists. The default partition <span class="k">for</span> the disk is:

  a <span class="m">100</span>% <span class="s2">&#34;SOLARIS System&#34;</span> partition

Type <span class="s2">&#34;y&#34;</span> to accept the default partition,  otherwise <span class="nb">type</span> <span class="s2">&#34;n&#34;</span> to edit the
 partition table.
WARNING: Disk is larger than 2TB. Solaris partition will be limited to <span class="m">2</span> TB.
y
format&gt; label
<span class="o">[</span><span class="m">0</span><span class="o">]</span> SMI Label
<span class="o">[</span><span class="m">1</span><span class="o">]</span> EFI Label
Specify Label type<span class="o">[</span><span class="m">1</span><span class="o">]</span>:
Ready to label disk, <span class="k">continue</span>? y

format&gt; quit</code></pre></div>
<p>Now I expected the disk I just formatted to show up as corrupt, but to my surprise&hellip;</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">root@openindiana:/jack# zpool import
   pool: tank
     id: <span class="m">12050779155201421632</span>
  state: UNAVAIL
 status: One or more devices are missing from the system.
 action: The pool cannot be imported. Attach the missing
        devices and try again.
   see: http://illumos.org/msg/ZFS-8000-3C
 config:

        tank          UNAVAIL  insufficient replicas
          raidz1-0    UNAVAIL  insufficient replicas
            c5t1d0p0  UNAVAIL  cannot open
            c5t2d0p0  UNAVAIL  cannot open
            c5t3d0p0  UNAVAIL  cannot open
            c5t4d0p0  UNAVAIL  cannot open
            c5t5d0    ONLINE</code></pre></div>
<p>It seemed to work! Now, rebooting to Linux or FreeBSD and importing the zpool, it will resilver a very small part of the pool, lasting just seconds. Now just continue rebooting to Solaris, formatting a drive, rebooting to Linux or FreeBSD and importing the zpool, until you&rsquo;ve formatted every disk.</p>

<pre><code>root@openindiana:/jack# zpool import
   pool: tank
     id: 12050779155201421632
  state: ONLINE
 action: The pool can be imported using its name or numeric identifier.
 config:

        tank          ONLINE
          raidz1-0    ONLINE
            c5t1d0    ONLINE
            c5t2d0p0  ONLINE
            c5t3d0    ONLINE
            c5t4d0p0  ONLINE
            c5t5d0    ONLINE
root@openindiana:/jack# zpool import -f tank
root@openindiana:/jack# zpool status
  pool: tank
 state: ONLINE
  scan: resilvered 68K in 0h0m with 0 errors on Thu Jun 13 15:48:15 2013
config:

        NAME          STATE     READ WRITE CKSUM
        tank          ONLINE       0     0     0
          raidz1-0    ONLINE       0     0     0
            c5t1d0    ONLINE       0     0     0
            c5t2d0p0  ONLINE       0     0     0
            c5t3d0    ONLINE       0     0     0
            c5t4d0p0  ONLINE       0     0     0
            c5t5d0    ONLINE       0     0     0

errors: No known data errors
</code></pre>

<p>The only thing I&rsquo;m still concerned about is why some disks in the zpool specify the partition (p0) and others just specify the whole disk (d0). However, the zpool can now be imported in Linux, FreeBSD, and illumos without any problems.</p>

<p>Now to start playing around with SmartOS&hellip;</p>
]]></content>
		</item>
		
		<item>
			<title>Freerunning Toshiba TMP68HC000P-16 on a Breadboard</title>
			<link>https://ostanin.org/2013/06/06/freerunning-toshiba-tmp68hc000p-16-on-a-breadboard/</link>
			<pubDate>Thu, 06 Jun 2013 03:00:00 +0000</pubDate>
			
			<guid>https://ostanin.org/2013/06/06/freerunning-toshiba-tmp68hc000p-16-on-a-breadboard/</guid>
			<description>Building my own computer was always a dream of mine. I decided to take the first step by buying a Japanese Motorola 68000 clone, the Toshiba TMP68HC000P-16. Rated at 16 MHz and using CMOS technology, this seemed like one of the best versions of the 68000.
To start with, I tried to freerun the chip. Freerunning is where you hardwire the data lines so that the CPU continually executes the same instruction over an over while the program counter counts up through memory.</description>
			<content type="html"><![CDATA[<p>Building my own computer was always a dream of mine. I decided to take the first step by buying a Japanese Motorola 68000 clone, the Toshiba TMP68HC000P-16. Rated at 16 MHz and using CMOS technology, this seemed like one of the best versions of the 68000.</p>

<p>To start with, I tried to freerun the chip. Freerunning is where you hardwire the data lines so that the CPU continually executes the same instruction over an over while the program counter counts up through memory. For the 68000, connecting all the data lines to ground is a common way to freerun the CPU, making it execute <code>OR</code> instructions endlessly.</p>


<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="//www.youtube.com/embed/gfQNy5w2v1Q" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>


<p>In the video, I connected all the data lines to ground and all the address lines to LEDs. The interrupt/error related signals are also connected so as not to disrupt the CPU. The clock is an 8 MHz crystal oscillator (should be easier to debug with my cheap logic analyzer at this speed) and there is a reset button connected as well. You can see the LEDs count up as the program counter is incremented.</p>

<p>I ordered some SRAM, EEPROMs, and a CPLD for the glue logic. Though I don&rsquo;t have much free time to work on this project, hopefully I&rsquo;ll have more updates in the future.</p>
]]></content>
		</item>
		
		<item>
			<title>Batch Convert Keynote Files to PDF with AppleScript</title>
			<link>https://ostanin.org/2013/05/31/batch-convert-keynote-files-to-pdf-with-applescript/</link>
			<pubDate>Fri, 31 May 2013 03:00:00 +0000</pubDate>
			
			<guid>https://ostanin.org/2013/05/31/batch-convert-keynote-files-to-pdf-with-applescript/</guid>
			<description>I switched from Keynote to just writing presentations in Markdown and using Pandoc to convert them to PDF files with beamer. Markdown lends itself well to version control so I decided to store all my presentations in a git repository.
I already had a couple of years worth of Keynote presentations which I would never touch again. Since Keynote file are almost 8x larger than their PDF counterparts, I wanted to convert all my old presentations to PDF before sticking them under version control as well.</description>
			<content type="html"><![CDATA[<p>I switched from Keynote to just writing presentations in Markdown and using <a href="http://johnmacfarlane.net/pandoc/">Pandoc</a> to convert them to PDF files with beamer. Markdown lends itself well to version control so I decided to store all my presentations in a git repository.</p>

<p>I already had a couple of years worth of Keynote presentations which I would never touch again. Since Keynote file are almost 8x larger than their PDF counterparts, I wanted to convert all my old presentations to PDF before sticking them under version control as well. Here&rsquo;s a script which finds every <code>*.key</code> and <code>.pptx</code> recursively under the current directory and converts it to a PDF.</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="cp">#! /bin/sh
</span><span class="cp"></span>
<span class="k">for</span> FILE in <span class="sb">`</span>find <span class="s2">&#34;</span><span class="si">${</span><span class="nv">PWD</span><span class="si">}</span><span class="s2">&#34;</span> -name <span class="se">\*</span>.pptx -or -name <span class="se">\*</span>.key<span class="sb">`</span><span class="p">;</span> <span class="k">do</span>
  <span class="nv">DIR</span><span class="o">=</span><span class="sb">`</span>dirname <span class="nv">$FILE</span><span class="sb">`</span>
  <span class="nb">echo</span> Converting <span class="nv">$FILE</span> to PDF
  osascript <span class="s">&lt;&lt; EOF
</span><span class="s">tell application &#34;System Events&#34;
</span><span class="s">  tell application &#34;Keynote&#34;
</span><span class="s">    activate
</span><span class="s">    open &#34;${FILE}&#34;
</span><span class="s">  end tell
</span><span class="s">  tell process &#34;Keynote&#34;
</span><span class="s">    click menu item &#34;PDF…&#34; of menu of menu bar item &#34;Share&#34; of menu bar 1
</span><span class="s">    click button &#34;Next…&#34; of sheet 1 of window 1
</span><span class="s">    keystroke &#34;G&#34; using {command down, shift down}
</span><span class="s">    keystroke &#34;${DIR}&#34;
</span><span class="s">    click button &#34;Go&#34; of sheet 1 of sheet 1 of window 1
</span><span class="s">    click button &#34;Export&#34; of sheet 1 of window 1
</span><span class="s">  end tell
</span><span class="s">  do shell script &#34;killall Keynote&#34;
</span><span class="s">end tell
</span><span class="s">EOF</span>
<span class="k">done</span></code></pre></div>
<p>This is my first go at AppleScript (or a shell script/AppleScript hybrid of some sort) so there may be some better ways to approach the problem. For instance, telling Keynote to quite would result in the script hanging with Keynote asking me if I wanted to save or not. And since <code>tell application &quot;Keynote&quot; to quit</code> didn&rsquo;t seem to return until Keynote quit, making it impossible to dismiss the save dialog on the next line. I use <code>killall Keynote</code> to get around this problem.</p>
]]></content>
		</item>
		
		<item>
			<title>Using OpenVPN for Select Sites with OpenWRT</title>
			<link>https://ostanin.org/2012/09/13/using-openvpn-for-select-sites-with-openwrt/</link>
			<pubDate>Thu, 13 Sep 2012 03:00:00 +0000</pubDate>
			
			<guid>https://ostanin.org/2012/09/13/using-openvpn-for-select-sites-with-openwrt/</guid>
			<description>I love watching Hulu from time to time, but living in Japan means I need to use a proxy or VPN located in the US to view it. I used to use a PPTP VPN server on a VPS to view Hulu, routing all traffic through the VPN. This had some obvious drawbacks in that Hulu playback was slower, the VPN configuration had to be done on each computer, and I couldn&amp;rsquo;t use things like BitTorrent at the same time as watching videos on Hulu.</description>
			<content type="html"><![CDATA[

<p>I love watching Hulu from time to time, but living in Japan means I need to use a proxy or VPN located in the US to view it. I used to use a PPTP VPN server on a VPS to view Hulu, routing all traffic through the VPN. This had some obvious drawbacks in that Hulu playback was slower, the VPN configuration had to be done on each computer, and I couldn&rsquo;t use things like BitTorrent at the same time as watching videos on Hulu. While this was fine when I&rsquo;d just watch videos on my laptop once every few weeks, I wanted a more transparent solution once an HTPC and an OpenWRT-capable router entered the equation.</p>

<p>The solution I came up with is simply connecting to an OpenVPN server from my OpenWRT router and selectively routing certain IP addresses through the VPN. The great thing about this setup is that it&rsquo;s completely transparent for anyone on the LAN. Any computer can stream videos from Hulu without any configuration.</p>

<h1 id="openvpn-server">OpenVPN Server</h1>

<p>I won&rsquo;t cover setting up an OpenVPN server in any detail, as that&rsquo;s already covered in countless other pages on the Internet. I will, however, offer a few hints from my experience setting up an OpenVPN server on FreeBSD.</p>

<p>The VPN doesn&rsquo;t have to be fast. I used an old FreeBSD server of mine still running in my parents house on a slow cable connection.</p>

<p>I used a combination of <a href="http://www.secure-computing.net/wiki/index.php/FreeBSD_OpenVPN_Server/Routed">these</a> <a href="http://www.freebsddiary.org/openvpn-easy-rsa.php">two</a> guides to create a bridged VPN. Here&rsquo;s my basic OpenVPN config file:</p>

<pre><code>port 1194
proto udp

dev tun

ca /usr/local/etc/openvpn/keys/ca.crt
cert /usr/local/etc/openvpn/keys/example.crt
key /usr/local/etc/openvpn/keys/example.key
dh /usr/local/etc/openvpn/keys/dh1024.pem

server 10.8.0.0 255.255.255.0

duplicate-cn

keepalive 10 120

comp-lzo

persist-key
persist-tun

status /var/log/openvpn/openvpn-status.log

verb 3
</code></pre>

<p>I then set up pf using the following <code>pf.conf</code> to NAT the VPN clients and give them access to the Internet:</p>

<pre><code>ext_if=&quot;vr0&quot;
vpn_if=&quot;tun0&quot;
vpn_network=&quot;10.8.0.0/24&quot;

nat on $ext_if from $vpn_network to any -&gt; ($ext_if)
</code></pre>

<h1 id="openvpn-client-on-openwrt">OpenVPN Client on OpenWRT</h1>

<p>I mostly followed the guide <a href="http://wiki.openwrt.org/doc/howto/vpn.client.openvpn.tun">here</a> to set up the client. For reference, I listed all the changes to my config files on the router below.</p>

<h2 id="etc-config-network"><code>/etc/config/network</code></h2>

<pre><code>config interface 'vpn'
        option ifname 'tun0'
        option defaultroute '0'
        option peerdns '0'
        option proto 'none'
</code></pre>

<h2 id="etc-config-firewall"><code>/etc/config/firewall</code></h2>

<pre><code>config zone
        option input 'ACCEPT'
        option forward 'REJECT'
        option output 'ACCEPT'
        option name 'vpn'
        option masq '1'
        option network 'vpn'

config forwarding
        option dest 'lan'
        option src 'vpn'

config forwarding
        option dest 'vpn'
        option src 'lan'
</code></pre>

<h2 id="etc-config-openvpn"><code>/etc/config/openvpn</code></h2>

<pre><code>config openvpn 'example'
        option enabled '1'
        option client '1'
        option dev 'tun'
        option proto 'udp'
        option nobind '1'
        option persist_tun '1'
        option persist_key '1'
        option ca '/etc/openvpn/example.ca'
        option cert '/etc/openvpn/example.cert'
        option key '/etc/openvpn/example.key'
        option comp_lzo '1'
        option verb '3'
        option float '1'
        option remote 'example.us.ostanin.org'
        option client '1'
</code></pre>

<h1 id="selective-routing">Selective Routing</h1>

<p>Now, we can route IP addresses used by Hulu through the VPN. There are two ways to do this, either have the OpenWRT client call <code>route</code> to add the routes, or have the OpenVPN server push the routes to the client. I choose to push the routes from the server, as this would allow me to connect to the server from outside my LAN and still enjoy Hulu without any additional configuration.</p>

<p>OpenVPN allows you to add routes to the client using the <code>push &quot;route X.X.X.X 255.255.255.255&quot;</code> directive in it&rsquo;s config file. So we just need to add all the necessary IP addresses for Hulu into the OpenVPN server&rsquo;s config file and we&rsquo;re good to go. I wrote a python script to automate the process:</p>
<div class="highlight"><pre class="chroma"><code class="language-python" data-lang="python"><span class="ch">#! /usr/bin/env python</span>

<span class="n">SITES</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s1">&#39;hulu&#39;</span><span class="p">:</span> <span class="p">[</span>
        <span class="s1">&#39;hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;www.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;static.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;ads.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;assets.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;t2.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;urlcheck.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;secure.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;a.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;b.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;c.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;d.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;e.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;f.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;g.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;h.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;i.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;j.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;k.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;l.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;m.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;n.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;o.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;p.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;q.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;r.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;s.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;t.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;u.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;v.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;w.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;x.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;y.hulu.com&#39;</span><span class="p">,</span>
        <span class="s1">&#39;z.hulu.com&#39;</span><span class="p">,</span>
    <span class="p">]</span>  
<span class="p">}</span>

<span class="kn">import</span> <span class="nn">socket</span>
<span class="kn">import</span> <span class="nn">sys</span>

<span class="n">sites</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>

<span class="k">for</span> <span class="n">site</span> <span class="ow">in</span> <span class="n">sites</span><span class="p">:</span>
    <span class="k">if</span> <span class="n">site</span> <span class="ow">in</span> <span class="n">SITES</span><span class="p">:</span>
        <span class="k">print</span> <span class="s1">&#39;###&#39;</span><span class="p">,</span> <span class="n">site</span>
        <span class="k">for</span> <span class="n">host</span> <span class="ow">in</span> <span class="n">SITES</span><span class="p">[</span><span class="n">site</span><span class="p">]:</span>
            <span class="k">try</span><span class="p">:</span>
                <span class="n">addrs</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">gethostbyname_ex</span><span class="p">(</span><span class="n">host</span><span class="p">)[</span><span class="mi">2</span><span class="p">]</span>
                <span class="k">print</span> <span class="s1">&#39;#&#39;</span><span class="p">,</span> <span class="n">host</span>
                <span class="k">for</span> <span class="n">addr</span> <span class="ow">in</span> <span class="n">addrs</span><span class="p">:</span>
                    <span class="k">print</span> <span class="s1">&#39;push &#34;route&#39;</span><span class="p">,</span> <span class="n">addr</span><span class="p">,</span> <span class="s1">&#39;255.255.255.255&#34;&#39;</span>
            <span class="k">except</span><span class="p">:</span>
                <span class="k">pass</span></code></pre></div>
<p>When run with <code>./routemaker.py hulu</code>, it will print out a list of all <code>push</code> directives needed to watch Hulu. Here is an example run:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ ./routemaker.py hulu
<span class="c1">### hulu</span>
<span class="c1"># hulu.com</span>
push <span class="s2">&#34;route 60.254.153.10 255.255.255.255&#34;</span>
push <span class="s2">&#34;route 60.254.153.25 255.255.255.255&#34;</span>
<span class="c1"># www.hulu.com</span>
push <span class="s2">&#34;route 210.149.135.22 255.255.255.255&#34;</span>
push <span class="s2">&#34;route 210.149.135.45 255.255.255.255&#34;</span>
<span class="c1"># static.hulu.com</span>
push <span class="s2">&#34;route 23.3.104.33 255.255.255.255&#34;</span>
push <span class="s2">&#34;route 23.3.104.8 255.255.255.255&#34;</span>
<span class="c1"># ads.hulu.com</span>
push <span class="s2">&#34;route 23.3.104.65 255.255.255.255&#34;</span>
push <span class="s2">&#34;route 23.3.104.59 255.255.255.255&#34;</span>
<span class="c1"># assets.hulu.com</span>
push <span class="s2">&#34;route 23.3.104.42 255.255.255.255&#34;</span>
push <span class="s2">&#34;route 23.3.104.75 255.255.255.255&#34;</span>
<span class="c1"># t2.hulu.com</span>
push <span class="s2">&#34;route 23.3.104.32 255.255.255.255&#34;</span>
push <span class="s2">&#34;route 23.3.104.9 255.255.255.255&#34;</span>
<span class="c1"># urlcheck.hulu.com</span>
push <span class="s2">&#34;route 208.91.157.69 255.255.255.255&#34;</span>
<span class="c1"># secure.hulu.com</span>
push <span class="s2">&#34;route 118.215.182.31 255.255.255.255&#34;</span>
<span class="c1"># a.hulu.com</span>
push <span class="s2">&#34;route 23.3.104.59 255.255.255.255&#34;</span>
push <span class="s2">&#34;route 23.3.104.65 255.255.255.255&#34;</span>
<span class="c1"># c.hulu.com</span>
push <span class="s2">&#34;route 210.149.135.21 255.255.255.255&#34;</span>
push <span class="s2">&#34;route 210.149.135.14 255.255.255.255&#34;</span>
<span class="c1"># g.hulu.com</span>
push <span class="s2">&#34;route 208.91.157.28 255.255.255.255&#34;</span>
<span class="c1"># i.hulu.com</span>
push <span class="s2">&#34;route 208.91.157.16 255.255.255.255&#34;</span>
<span class="c1"># m.hulu.com</span>
push <span class="s2">&#34;route 23.3.104.66 255.255.255.255&#34;</span>
push <span class="s2">&#34;route 23.3.104.56 255.255.255.255&#34;</span>
<span class="c1"># p.hulu.com</span>
push <span class="s2">&#34;route 23.3.104.43 255.255.255.255&#34;</span>
push <span class="s2">&#34;route 23.3.104.81 255.255.255.255&#34;</span>
<span class="c1"># r.hulu.com</span>
push <span class="s2">&#34;route 210.149.135.29 255.255.255.255&#34;</span>
push <span class="s2">&#34;route 210.149.135.44 255.255.255.255&#34;</span>
<span class="c1"># s.hulu.com</span>
push <span class="s2">&#34;route 23.3.104.65 255.255.255.255&#34;</span>
push <span class="s2">&#34;route 23.3.104.33 255.255.255.255&#34;</span>
<span class="c1"># t.hulu.com</span>
push <span class="s2">&#34;route 208.91.157.68 255.255.255.255&#34;</span>
<span class="c1"># u.hulu.com</span>
push <span class="s2">&#34;route 208.91.157.20 255.255.255.255&#34;</span></code></pre></div>
<p>You can now copy and paste that to your OpenVPN server config and restart the server. You could also automate it to run once a day and restart OpenVPN automatically if you so wish.</p>

<p>If everything worked, the Hulu routes should have been added to your client:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">root@router:~# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         <span class="m">182</span>.236.18.15   <span class="m">0</span>.0.0.0         UG    <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> pppoe-wan
<span class="m">10</span>.8.0.1        <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">10</span>.8.0.5        *               <span class="m">255</span>.255.255.255 UH    <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">23</span>.3.104.8      <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">23</span>.3.104.9      <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">23</span>.3.104.32     <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">23</span>.3.104.33     <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">23</span>.3.104.34     <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">23</span>.3.104.42     <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">23</span>.3.104.43     <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">23</span>.3.104.56     <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">23</span>.3.104.59     <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">23</span>.3.104.65     <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">23</span>.3.104.66     <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">23</span>.3.104.73     <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">23</span>.3.104.75     <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">23</span>.3.104.81     <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">23</span>.48.134.31    <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">60</span>.254.153.10   <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">60</span>.254.153.25   <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">182</span>.236.18.15   *               <span class="m">255</span>.255.255.255 UH    <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> pppoe-wan
<span class="m">192</span>.168.1.0     *               <span class="m">255</span>.255.255.0   U     <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> br-lan
<span class="m">208</span>.91.157.16   <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">208</span>.91.157.20   <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">208</span>.91.157.28   <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">208</span>.91.157.68   <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">208</span>.91.157.69   <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">210</span>.149.135.30  <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0
<span class="m">210</span>.149.135.53  <span class="m">10</span>.8.0.5        <span class="m">255</span>.255.255.255 UGH   <span class="m">0</span>      <span class="m">0</span>        <span class="m">0</span> tun0</code></pre></div>
<p>And there you have it! Hulu should now work on all devices within the LAN. You can make other sites work too by adding their hostnames to the script as well. Keep in mind that ads from Hulu will be sent through the VPN while videos themselves will not, resulting in very laggy ads if your VPN is slow like mine. If your Hulu client allows choosing a quality setting for ads (like the XBMC client does), set them to the lowest quality.</p>
]]></content>
		</item>
		
		<item>
			<title>Backing Up Simple Websites with Git</title>
			<link>https://ostanin.org/2012/01/28/backing-up-simple-websites-with-git/</link>
			<pubDate>Sat, 28 Jan 2012 03:00:00 +0000</pubDate>
			
			<guid>https://ostanin.org/2012/01/28/backing-up-simple-websites-with-git/</guid>
			<description>I&amp;rsquo;m hosting three different WordPress blogs for different people (this one included) and recently decided to switch away from my home server to a VPS. My home server has a RAID-Z array and important data is backed up remotely on a regular basis giving me the peace of mind that my data is safe, but I can&amp;rsquo;t be so sure about the VPS. Also, I might switch to a different VPS provider in the future and wanted to make deploying the blogs as easy as possible.</description>
			<content type="html"><![CDATA[

<p>I&rsquo;m hosting three different WordPress blogs for different people (this one included) and recently decided to switch away from my home server to a VPS. My home server has a RAID-Z array and important data is backed up remotely on a regular basis giving me the peace of mind that my data is safe, but I can&rsquo;t be so sure about the VPS. Also, I might switch to a different VPS provider in the future and wanted to make deploying the blogs as easy as possible. I came up with the following solution.</p>

<h1 id="goals">Goals</h1>

<ul>
<li>Daily backups usable for small websites with little traffic</li>
<li>Backups should be complete: all files, MySQL dump, logs</li>
<li>Flexibility</li>
</ul>

<h1 id="why-git">Why Git?</h1>

<p>Using git has many inherent advantages over simply copying files to a remote server.</p>

<ul>
<li>Daily backups will only store diffs, taking up little space</li>
<li>Using a VCS allows you to see history, merge fixes, branch, etc. if you need to (eg. for developing plugins/themes on one computer and easily merging them into your website)</li>
<li>Push backups to a remote server using http, https, ssh, or git protocols</li>
<li>Easily exclude files from backups using <code>.gitignore</code></li>
<li>Setting up a new server is just a <code>git clone</code> away</li>
<li>Know your backup succeeded just by browsing your git repository</li>
<li>More!</li>
</ul>

<p>So, why should we store the database backup and log files in git as well?</p>

<ul>
<li>Complete history of your website &ndash; restore the files AND database to any state you want</li>
<li>The diffs in each commit give you the traffic for the day and which rows were added to your database</li>
<li>No need for separate backup methods!</li>
</ul>

<p>Of course, git isn&rsquo;t the perfect solution for all websites. High-traffic websites may find their git repositories quickly grow in size when storing database backups and log files.</p>

<h1 id="structure">Structure</h1>

<p>To make things easier, I structured each of the sites in the following fashion:</p>

<pre><code>/usr/local/www/
`- example.com/
   |- logs/
   |  `- example.com-access.log
   |  `- example.com-error.log
   |- root/ # WordPress installation
   `- sql/
      `- example.com.sql # MySQL dump
</code></pre>

<p>Also, the MySQL username and database name for each site is the same as the URL, so &ldquo;example.com&rdquo; for this case.</p>

<h1 id="prepare-the-remote-repository-on-the-backup-server">Prepare the Remote Repository on the Backup Server</h1>

<p>Create an empty repository on the backup server.</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ <span class="nb">cd</span> /path/to/git/repositories
$ mkdir example.com.git
$ <span class="nb">cd</span> example.com.git
$ git init --bare --shared</code></pre></div>
<h1 id="add-data-to-the-repository">Add data to the repository</h1>

<p>We can clone the empty repository we just made and add the initial data to it. Do this on the server your site is currently hosted on.</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ git clone http://git.example.com/example.com
$ <span class="nb">cd</span> example.com
$ mkdir logs sql
$ touch logs/example.com-access.log logs/example.com-error.log
$ mysqldump -uexample.com -pPASSWORD --skip-extended-insert --skip-comments example.com &gt; sql/example.com.sql
<span class="c1"># Copy the WordPress install</span>
$ cp -r /path/to/site/root root</code></pre></div>
<p>I also made a simple Makefile to automate backing up. Edit it to suit your needs (especially the <code>MYSQL_</code> variables) and place it in the root of your repository.</p>
<div class="highlight"><pre class="chroma"><code class="language-make" data-lang="make"><span class="nv">SITE_NAME</span><span class="o">=</span>  <span class="k">$(</span>notdir <span class="k">$(</span>CURDIR<span class="k">))</span>
<span class="nv">MYSQL_USER</span><span class="o">=</span> <span class="k">$(</span>SITE_NAME<span class="k">)</span>
<span class="nv">MYSQL_PASS</span><span class="o">=</span> PASSWORD
<span class="nv">MYSQL_DB</span><span class="o">=</span>   <span class="k">$(</span>SITE_NAME<span class="k">)</span>

<span class="nf">.PHONY</span><span class="o">:</span> <span class="n">backup</span>
<span class="nf">backup</span><span class="o">:</span> <span class="n">sql_backup</span> <span class="n">git_commit</span> <span class="n">git_push</span>
  @echo Backup of <span class="k">$(</span>SITE_NAME<span class="k">)</span> finished successfully!

<span class="nf">.PHONY</span><span class="o">:</span> <span class="n">sql_backup</span>
<span class="nf">sql_backup</span><span class="o">:</span>
  @echo Backing up the database
  @mysqldump -u<span class="k">$(</span>MYSQL_USER<span class="k">)</span> -p<span class="k">$(</span>MYSQL_PASS<span class="k">)</span> --skip-extended-insert --skip-comments <span class="k">$(</span>MYSQL_DB<span class="k">)</span> &gt; sql/<span class="k">$(</span>SITE_NAME<span class="k">)</span>.sql

<span class="nf">.PHONY</span><span class="o">:</span> <span class="n">git_commit</span>
<span class="nf">git_commit</span><span class="o">:</span> <span class="n">git_commit_sql</span> <span class="n">git_commit_logs</span> <span class="n">git_commit_root</span>

<span class="nf">.PHONY</span><span class="o">:</span> <span class="n">git_commit_logs</span>
<span class="nf">git_commit_logs</span><span class="o">:</span>
  @echo Committing log files
  @-git commit logs -m <span class="s2">&#34;Updating logs&#34;</span>

<span class="nf">.PHONY</span><span class="o">:</span> <span class="n">git_commit_root</span>
<span class="nf">git_commit_root</span><span class="o">:</span>
  @echo Committing root
  @-git add root
  @-git commit root -m <span class="s2">&#34;Updating root&#34;</span>

<span class="nf">.PHONY</span><span class="o">:</span> <span class="n">git_commit_sql</span>
<span class="nf">git_commit_sql</span><span class="o">:</span>
  @echo Committing sql
  @-git commit sql -m <span class="s2">&#34;Updating sql dump&#34;</span>

<span class="nf">.PHONY</span><span class="o">:</span> <span class="n">git_push</span>
<span class="nf">git_push</span><span class="o">:</span>
  @echo Pushing commit
  @-git push
</code></pre></div>
<p>Now we can commit changes and push to our backup server. Remember that you can add a <code>.gitignore</code> to keep files or directories from being backed up.</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ git add .
$ git commit -m <span class="s2">&#34;initial commit&#34;</span>
$ git push origin master</code></pre></div>
<h1 id="deploying">Deploying</h1>

<p>Now when you want to deploy your site, set up the MySQL user and database then checkout the site from git.</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ <span class="nb">cd</span> /usr/local/www
$ git clone --shared http://git.example.com/example.com
$ <span class="nb">cd</span> example.com
$ mysql -uexample.com -pPASSWORD example.com &lt; sql/example.com.sql</code></pre></div>
<p>As long as your web server points to <code>/usr/local/www/example.com</code>, you should now be able to access your site!</p>

<p>If you want to test backups, just generate some log activity or do something which would update the database and run <code>make backup</code> from <code>/usr/local/www/example.com</code>.</p>

<h1 id="automating-backups">Automating Backups</h1>

<p>You can automate backups by using <code>cron</code>.</p>

<p>Access your crontab</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ crontab -e</code></pre></div>
<p>&hellip;and add the following line:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">@daily <span class="nb">cd</span> /usr/local/www/example.com <span class="o">&amp;&amp;</span> make backup <span class="p">&amp;</span>&gt; /dev/null</code></pre></div>
<p>If all goes well, you should see daily commits popping up on your backup server! If you ever lose data, you can go back to the deploying section and go back to your last backup.</p>
]]></content>
		</item>
		
	</channel>
</rss>
