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

<channel>
	<title>mark shroyer, dot com</title>
	<atom:link href="http://markshroyer.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://markshroyer.com</link>
	<description>this is where I keep my things</description>
	<lastBuildDate>Fri, 04 Jan 2013 05:04:38 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>Debugging the OpenBSD kernel via QEMU</title>
		<link>http://markshroyer.com/2013/01/debugging-openbsd-via-qemu/</link>
		<comments>http://markshroyer.com/2013/01/debugging-openbsd-via-qemu/#comments</comments>
		<pubDate>Fri, 04 Jan 2013 05:03:52 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Unix]]></category>

		<guid isPermaLink="false">http://markshroyer.com/?p=1269</guid>
		<description><![CDATA[Recently I had to track down a minor bug in the OpenBSD kernel. I tapped QEMU and GDB as debugging tools for the task, running on Ubuntu 12.04 as the host OS. This combination worked extremely well, so for the &#8230; <a href="http://markshroyer.com/2013/01/debugging-openbsd-via-qemu/">Continue reading <span class="meta-nav">&#187;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Recently I had to track down <a href="http://marc.info/?l=openbsd-tech&#038;m=135649170822492&#038;w=2">a minor bug in the OpenBSD kernel</a>.  I tapped QEMU and GDB as debugging tools for the task, running on Ubuntu 12.04 as the host OS.  This combination worked extremely well, so for the record here&#8217;s how I set it all up.</p>
<p>OpenBSD comes equipped with two kernel debugging mechanisms: ddb and kgdb.  <a href="http://www.openbsd.org/cgi-bin/man.cgi?query=ddb&#038;apropos=0&#038;sektion=0&#038;manpath=OpenBSD+Current&#038;arch=i386&#038;format=html">ddb(4)</a> is an in-kernel debugger, enabled by default in the GENERIC kernel, and can be invoked either explicitly from the console or automatically in the event of a panic.  It is analogous to the Linux debugger kdb in that it can be used to set breakpoints and examine the stack or register state, but (like kdb) it is not a source-level debugger.</p>
<p>For source debugging there is <a href="http://www.openbsd.org/cgi-bin/man.cgi?query=kgdb&#038;apropos=0&#038;sektion=0&#038;manpath=OpenBSD+Current&#038;arch=i386&#038;format=html">kgdb(7)</a>, which offers the ability to remotely debug the kernel by way of a GDB stub running over a serial port; this is similar to the Linux debugger kgdboc.  However, kgdb it is not available in the GENERIC kernel, and it imposes an additional set of configurations and debugger latencies on the user.  If your debugging task is amenable to running OpenBSD within a virtual machine, as mine was, then there is an easier and better way&#8230;</p>
<p><span id="more-1269"></span></p>
<p>The VM monitor QEMU provides its own GDB stub useful for debugging virtualized systems.  For source-level debugging this still requires obtaining a kernel compiled with the appropriate debug info, but the stub makes no additional requirements of the guest OS and performs very well.  The necessary QEMU tools can be installed on Ubuntu 12.04 as follows:</p>
<pre>
$ sudo apt-get install qemu-kvm qemu-utils
</pre>
<p>To start with, you&#8217;ll need an OpenBSD installation in a QEMU virtual machine.  Prepare your virtual disk image (20GB will be more than enough):</p>
<pre>
$ qemu-img create -f qcow2 ~/obsd52.qcow2 20G
</pre>
<p>Next, download the OpenBSD i386 install CD image from a mirror, such as:</p>
<p><a href="ftp://ftp3.usa.openbsd.org/pub/OpenBSD/5.2/i386/install52.iso">ftp://ftp3.usa.openbsd.org/pub/OpenBSD/5.2/i386/install52.iso</a></p>
<p>Now boot a VM from the OpenBSD installation CD, with the QCOW2 image that you just created as your virtual disk image.  Aside from setting a system RAM size of 256 MB, the options specified here will provide a userspace NAT virtual network interface for the VM:</p>
<pre>
$ qemu-system-i386 -m 256M -net nic -net user -cdrom ~/install52.iso \
      ~/obsd52.qcow2
</pre>
<p>Wait for the CD image to boot and then install OpenBSD in the <a href="http://www.openbsd.org/faq/faq4.html#Install">usual manner</a>.  When prompted, specify that the SSH server should be started by default, as this will be needed shortly.  Reboot into the new guest OS when the installer has finished.</p>
<p><a href="http://markshroyer.com/2013/01/debugging-openbsd-via-qemu/qemu-openbsd/" rel="attachment wp-att-1362"><img src="http://markshroyer.com/wp-content/uploads/2013/01/qemu-openbsd.png" alt="OpenBSD in QEMU" width="600" height="357" class="aligncenter size-full wp-image-1362" /></a></p>
<p>OpenBSD isn&#8217;t yet ready for debugging, though – although the system boots, its kernel does not provide the debug symbols needed by GDB.  So on the guest system now, download and install the system kernel source code from an FTP mirror:</p>
<pre>
obsd# cd /tmp
obsd# ftp ftp://ftp3.usa.openbsd.org/pub/OpenBSD/5.2/sys.tar.gz
obsd# cd /usr/src
obsd# tar xzf /tmp/sys.tar.gz
</pre>
<p>Make a new kernel configuration based on the i386 GENERIC config, then edit this config to turn on debug symbol generation by adding the line <code>makeoptions DEBUG="-g"</code> under the <code>option</code> lines:</p>
<pre>
obsd# cd /usr/src/sys/arch/i386/conf
obsd# cp GENERIC DEBUG
obsd# vi DEBUG
</pre>
<p>Now you can build and install the new kernel.  Specifying the <code>COPTS</code> environment variable here overrides the default optimization level <code>-O2</code>, which will make control flow much easier to follow while single-stepping in the debugger:</p>
<pre>
obsd# config DEBUG
obsd# cd ../compile/DEBUG
obsd# make depend
obsd# COPTS="-O0" make
obsd# make install
</pre>
<p>Halt the OpenBSD guest and exit QEMU, and then restart the VM with additional options <code>-s</code> and <code>-redir</code>, to enable the GDB stub and access to the VM&#8217;s SSH service respectively:</p>
<pre>
$ qemu-system-i386 -m 256M -net nic -net user -s \
      -redir tcp:2022::22 ~/obsd52.qcow2
</pre>
<p>When the guest is booted, SCP from the host to copy the kernel source tree and build output.</p>
<pre>
$ scp -pr -P2022 localhost:/usr/src/sys ~/obsd52-sys
</pre>
<p>With debug output enabled the OpenBSD kernel build produces an unstripped ELF kernel image named <code>bsd.gdb</code>, which is suitable for use by GDB.  Load this into GDB and then attach to QEMU&#8217;s debug stub (which defaults to TCP port 1234):</p>
<pre>
$ cd ~/obsd52-sys/arch/i386/compile/DEBUG
$ gdb bsd.gdb
(gdb) target remote :1234
</pre>
<p>The VM will halt when you connect the debugger, leaving you free to set system call breakpoints or inspect state before continuing.</p>
]]></content:encoded>
			<wfw:commentRss>http://markshroyer.com/2013/01/debugging-openbsd-via-qemu/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Keyboard navigation in Emacs GDB mode</title>
		<link>http://markshroyer.com/2012/11/emacs-gdb-keyboard-navigation/</link>
		<comments>http://markshroyer.com/2012/11/emacs-gdb-keyboard-navigation/#comments</comments>
		<pubDate>Sat, 24 Nov 2012 22:12:13 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://markshroyer.com/?p=1235</guid>
		<description><![CDATA[I love Emacs GDB mode, but I always found it annoying that there is no given key binding (or function which could be directly mapped into a key binding) for switching between the different views given by gdb-many-windows. The usual &#8230; <a href="http://markshroyer.com/2012/11/emacs-gdb-keyboard-navigation/">Continue reading <span class="meta-nav">&#187;</span></a>]]></description>
				<content:encoded><![CDATA[<p>I love Emacs <a href="http://users.snap.net.nz/~nickrob/multi-threaded.png" title="GDB mode screenshot" target="_blank">GDB mode</a>, but I always found it annoying that there is no given key binding (or function which could be directly mapped into a key binding) for switching between the different views given by <code>gdb-many-windows</code>.  The usual window and buffer switching functions are insufficient because the GDB windows are flagged as dedicated (so <code>switch-buffer</code> refuses to swap them in place), and in the case of the Locals/Registers and Breakpoints/Threads windows, the buffer you want to visit doesn&#8217;t necessarily even exist until you click that button.</p>
<p>This went from mildly annoying to a major headache when I needed to run the debugger in a remote Emacs session, over SSH from Mac OS X&#8217;s Terminal.app which does not support xterm mouse emulation.  So I wrote this <code>gdb-select-window</code> function and corresponding key bindings to finally get the desired behavior&#8230;</p>
<script src="https://gist.github.com/4141475.js?file=gdb-select-window.el"></script><noscript><pre><code class="language-emacs lisp emacs lisp">;; For the consistency of gdb-select-window's calling convention...
(defun gdb-comint-buffer-name ()
  (buffer-name gud-comint-buffer))
(defun gdb-source-buffer-name ()
  (buffer-name (window-buffer gdb-source-window)))

(defun gdb-select-window (header)
  &quot;Switch directly to the specified GDB window.
Moves the cursor to the requested window, switching between
`gdb-many-windows' \&quot;tabs\&quot; if necessary in order to get there.

Recognized window header names are: 'comint, 'locals, 'registers,
'stack, 'breakpoints, 'threads, and 'source.&quot;

  (interactive &quot;Sheader: &quot;)

  (let* ((header-alternate (case header
                             ('locals      'registers)
                             ('registers   'locals)
                             ('breakpoints 'threads)
                             ('threads     'breakpoints)))
         (buffer (intern (concat &quot;gdb-&quot; (symbol-name header) &quot;-buffer&quot;)))
         (buffer-names (mapcar (lambda (header)
                                 (funcall (intern (concat &quot;gdb-&quot;
                                                          (symbol-name header)
                                                          &quot;-buffer-name&quot;))))
                               (if (null header-alternate)
                                   (list header)
                                 (list header header-alternate))))
         (window (if (eql header 'source)
                     gdb-source-window
                   (or (get-buffer-window (car buffer-names))
                       (when (not (null (cadr buffer-names)))
                         (get-buffer-window (cadr buffer-names)))))))

    (when (not (null window))
      (let ((was-dedicated (window-dedicated-p window)))
        (select-window window)
        (set-window-dedicated-p window nil)
        (when (member header '(locals registers breakpoints threads))
          (switch-to-buffer (gdb-get-buffer-create buffer))
          (setq header-line-format (gdb-set-header buffer)))
        (set-window-dedicated-p window was-dedicated))
      t)))

;; Use global keybindings for the window selection functions so that they
;; work from the source window too...
(mapcar (lambda (setting)
          (lexical-let ((key    (car setting))
                        (header (cdr setting)))
            (global-set-key (concat &quot;\C-c\C-g&quot; key) #'(lambda ()
                                                        (interactive)
                                                        (gdb-select-window header)))))
        '((&quot;c&quot; . comint)
          (&quot;l&quot; . locals)
          (&quot;r&quot; . registers)
          (&quot;u&quot; . source)
          (&quot;s&quot; . stack)
          (&quot;b&quot; . breakpoints)
          (&quot;t&quot; . threads)))
</code></pre></noscript>
<p>Put this in your <code>~/.emacs.el</code> or <code>init.el</code> and fire up <code>gdb-many-windows</code>, and then you&#8217;ll be able to quickly switch between the GDB windows with the following keyboard shortcuts:</p>
<ul>
<li><code>C-c C-g c</code> &#8212; comint (GDB command) buffer</li>
<li><code>C-c C-g l</code> &#8212; locals buffer</li>
<li><code>C-c C-g r</code> &#8212; registers buffer</li>
<li><code>C-c C-g u</code> &#8212; source window</li>
<li><code>C-c C-g s</code> &#8212; stack buffer</li>
<li><code>C-c C-g b</code> &#8212; breakpoints buffer</li>
<li><code>C-c C-g t</code> &#8212; threads buffer</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://markshroyer.com/2012/11/emacs-gdb-keyboard-navigation/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Both true and false: a Zen moment with C</title>
		<link>http://markshroyer.com/2012/06/c-both-true-and-false/</link>
		<comments>http://markshroyer.com/2012/06/c-both-true-and-false/#comments</comments>
		<pubDate>Wed, 27 Jun 2012 11:15:22 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Code]]></category>

		<guid isPermaLink="false">http://markshroyer.com/?p=1091</guid>
		<description><![CDATA[Update: Discussion on Hacker News I ran into a really fun bug at work yesterday, where I discovered that my C program was branching down logically inconsistent code paths. After drinking another cup of coffee and firing up GDB I &#8230; <a href="http://markshroyer.com/2012/06/c-both-true-and-false/">Continue reading <span class="meta-nav">&#187;</span></a>]]></description>
				<content:encoded><![CDATA[<p><em>Update: <a href="http://news.ycombinator.com/item?id=4166791">Discussion on Hacker News</a></em></p>
<p>I ran into a really fun bug at work yesterday, where I discovered that my C program was branching down logically inconsistent code paths.  After drinking another cup of coffee and firing up GDB I realized that somehow, a boolean variable in my code was simultaneously testing as both true and not true.</p>
<p>While I cannot reproduce the actual source code here, the effect was that code like</p>
<p><!-- gist id=3001231 file=snippet.c --></p>
<pre class="brush: cpp; title: ; notranslate">
bool p;
 
/* ... */
 
if ( p )
    puts(&quot;p is true&quot;);
 
if ( ! p )
    puts(&quot;p is false&quot;);
</pre>
<p>would produce the output:</p>
<pre>
p is true
p is false
</pre>
<p>So what&#8217;s going on here?</p>
<p><span id="more-1091"></span></p>
<p>Well it turns out that the authors of the C language specification (and the people who went on to implement compilers for it) were serious about the concept of <a href="http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html" title="What Every C Programmer Should Know About Undefined Behavior">undefined behavior</a>.  In particular, the result of attempting to use an uninitialized variable is undefined.</p>
<p>And in this case that&#8217;s exactly what happened: I had failed to properly initialize some memory.  Easy, bug fixed.  But what I think is interesting are the reasons this code failed in precisely the way that it did.  In order to investigate that, we need to get specific.</p>
<p>On 64-bit Linux (Ubuntu 12.04), compiling the following program:</p>
<p><!-- gist id=3001231 file=bool1.c --></p>
<pre class="brush: cpp; title: ; notranslate">
#include &lt;stdio.h&gt;
#include &lt;stdbool.h&gt;
 
int main(int argc, char *argv[])
{
    volatile bool p;
 
    if ( p )
        puts(&quot;p is true&quot;);
    else
        puts(&quot;p is not true&quot;);
 
    if ( ! p )
        puts(&quot;p is false&quot;);
    else
        puts(&quot;p is not false&quot;);
 
    return 0;
}
</pre>
<p>with GCC 4.6.3, using the command line:</p>
<pre>
$ gcc bool1.c -g0 -O0 -fno-dwarf2-cfi-asm -masm=intel -S -o bool1.asm
</pre>
<p>produces this (truncated) assembly language:</p>
<p><!-- gist id=3001231 file=bool1.asm --></p>
<pre>
        .file   "bool1.c"
        .intel_syntax noprefix
        .section        .rodata
.LC0:
        .string "p is true"
.LC1:
        .string "p is not true"
.LC2:
        .string "p is false"
.LC3:
        .string "p is not false"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        push    rbp
.LCFI0:
        mov     rbp, rsp
.LCFI1:
        sub     rsp, 32
.LCFI2:
        mov     DWORD PTR [rbp-20], edi
        mov     QWORD PTR [rbp-32], rsi
        movzx   eax, BYTE PTR [rbp-1]
        test    al, al
        je      .L2
        mov     edi, OFFSET FLAT:.LC0
        call    puts
        jmp     .L3
.L2:
        mov     edi, OFFSET FLAT:.LC1
        call    puts
.L3:
        movzx   eax, BYTE PTR [rbp-1]
        xor     eax, 1
        test    al, al
        je      .L4
        mov     edi, OFFSET FLAT:.LC2
        call    puts
        jmp     .L5
.L4:
        mov     edi, OFFSET FLAT:.LC3
        call    puts
.L5:
        mov     eax, 0
        leave
.LCFI3:
        ret
</pre>
<p>To perform the test <code>if ( p )</code> here, first the stack variable is loaded into a 32-bit register with <code>movzx eax, BYTE PTR [rbp-1]</code>, and then we use the instruction <code>test al, al</code> which sets the zero flag (ZF) if the lower eight bits of this value are zero.  Next we execute the conditional jump <code>je .L2</code>, which jumps to print &#8220;p is not true&#8221; if ZF was set; otherwise we don&#8217;t jump, and &#8220;p is true&#8221; gets printed instead.</p>
<p>Next let&#8217;s examine the second test, <code>if ( ! p )</code>, at label <code>.L3</code>.  This starts out the same by loading the boolean variable into register <code>eax</code>, but notice how the negation is handled.  Rather than reorder the jumps or use <code>jne</code> instead of <code>je</code>, the compiler explicitly negates the boolean by performing a bitwise exclusive-or: <code>xor eax, 1</code>.</p>
<p>Normally this would be fine &#8212; a <code>bool</code> variable is only supposed to contain a value of zero or one, in which case its value can be negated by XOR with 1.  When you cast to a <code>bool</code> at runtime, the compiler generates code to ensure only one or zero gets stored.  For instance, the cast in this program:</p>
<p><!-- gist id=3001231 file=cast.c --></p>
<pre class="brush: cpp; title: ; notranslate">
#include &lt;stdbool.h&gt;
 
volatile char c = 0xff;
volatile bool p;
 
int main(int argc, char* argv[])
{
    p = (bool)c;
    return 0;
}
</pre>
<p>is implemented as the following four instructions:</p>
<p><!-- gist id=3001231 file=cast.asm --></p>
<pre>
        movzx   eax, BYTE PTR c[rip]
        test    al, al
        setne   al
        mov     BYTE PTR p[rip], al
</pre>
<p>wherein <code>setne</code> sets the register <code>al</code> to exactly 1 if the <code>char</code> contained any nonzero value, before saving the register&#8217;s 8-bit value to the boolean variable.</p>
<p>But the compiler affords us no such protection if we accidentally use an uninitialized value as a boolean.  It doesn&#8217;t have to, it&#8217;s not the compiler&#8217;s responsibility; the result of using an uninitialized stack variable is undefined.  And so if we somehow wind up with a value of e.g. <code>0x60</code> stored at the address of a <code>bool</code> variable (as I saw during my troubleshooting yesterday), both the variable and its negation (via exclusive or with 1) will be nonzero, and therefore test as true.</p>
<p>Interestingly, enabling optimization (<code>-O2</code>) in GCC causes the compiler to factor out the XOR and instead reorder the jumps, meaning this program actually behaves <em>more</em> robustly under compiler optimization (for certain definitions of &#8220;robust&#8221; anyway):</p>
<p><!-- gist id=3001231 file=bool1_O2.asm --></p>
<pre>
        .file   "bool1.c"
        .intel_syntax noprefix
        .section        .rodata.str1.1,"aMS",@progbits,1
.LC0:
        .string "p is true"
.LC1:
        .string "p is not true"
.LC2:
        .string "p is false"
.LC3:
        .string "p is not false"
        .section        .text.startup,"ax",@progbits
        .p2align 4,,15
        .globl  main
        .type   main, @function
main:
.LFB22:
        sub     rsp, 24
.LCFI0:
        movzx   eax, BYTE PTR [rsp+15]
        test    al, al
        je      .L2
        mov     edi, OFFSET FLAT:.LC0
        call    puts
.L3:
        movzx   eax, BYTE PTR [rsp+15]
        test    al, al
        je      .L7
        mov     edi, OFFSET FLAT:.LC3
        call    puts
.L5:
        xor     eax, eax
        add     rsp, 24
.LCFI1:
        ret
</pre>
<p>And of course when we compare <code>char</code>, <code>int</code>, or other multiple-bit values for truthiness, the compiler makes no such assumption that the value can be logically negated by bitwise XOR; instead it uses <code>jne</code> in place of the <code>je</code> instruction.  (Maybe someone with more knowledge of the compiler can say why GCC with <code>-O0</code> uses an <code>xor</code> at all, when testing the negation of a <code>bool</code>.)</p>
]]></content:encoded>
			<wfw:commentRss>http://markshroyer.com/2012/06/c-both-true-and-false/feed/</wfw:commentRss>
		<slash:comments>59</slash:comments>
		</item>
		<item>
		<title>Pointfree style in Python</title>
		<link>http://markshroyer.com/2011/11/pointfree-style-in-python/</link>
		<comments>http://markshroyer.com/2011/11/pointfree-style-in-python/#comments</comments>
		<pubDate>Mon, 14 Nov 2011 11:12:12 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Code]]></category>

		<guid isPermaLink="false">http://markshroyer.com/?p=1071</guid>
		<description><![CDATA[I&#8217;ve been getting back into Python lately&#8230; I just wrote a small module that provides support for pointfree style programming with a combination of automatic partial function/method application and operator overloading for function composition: See the overview for a lengthier &#8230; <a href="http://markshroyer.com/2011/11/pointfree-style-in-python/">Continue reading <span class="meta-nav">&#187;</span></a>]]></description>
				<content:encoded><![CDATA[<p>I&#8217;ve been getting back into Python lately&#8230; I just wrote a small module that provides support for <a href="http://www.haskell.org/haskellwiki/Pointfree">pointfree style</a> programming with a combination of automatic partial function/method application and operator overloading for function composition:</p>
<pre class="brush: python; title: ; notranslate">
from pointfree import *
from operator import add

fn = pfmap(len) \
     &gt;&gt; pfmap(lambda n: n**2) \
     &gt;&gt; pfreduce(add, initial=0)

fn([&quot;foo&quot;, &quot;barr&quot;, &quot;bazzz&quot;]) # == 50
</pre>
<p>See <a href="http://markshroyer.com/docs/pointfree/latest/overview.html">the overview</a> for a lengthier introduction. You can install the pointfree module from the Python Package Index:</p>
<pre class="code">$ pip install pointfree</pre>
<p>Links:</p>
<ul>
<li><a href="http://markshroyer.com/docs/pointfree/latest/">Module documentation</a></li>
<li><a href="https://github.com/markshroyer/pointfree/">Github project page</a></li>
<li><a href="https://pypi.python.org/pypi/pointfree/">Python Package Index page</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://markshroyer.com/2011/11/pointfree-style-in-python/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Martin Kleppmann: Accounting for Computer Scientists</title>
		<link>http://markshroyer.com/2011/03/accounting-compsci/</link>
		<comments>http://markshroyer.com/2011/03/accounting-compsci/#comments</comments>
		<pubDate>Tue, 08 Mar 2011 03:43:01 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://markshroyer.com/?p=1055</guid>
		<description><![CDATA[I just found a great blog post by Martin Kleppmann titled Accounting for Computer Scientists, a succinct introduction to double-entry accounting (although he doesn&#8217;t refer to it as such) in terms of basic graph theory: Eventually I figured it out: &#8230; <a href="http://markshroyer.com/2011/03/accounting-compsci/">Continue reading <span class="meta-nav">&#187;</span></a>]]></description>
				<content:encoded><![CDATA[<p>I just found a great blog post by Martin Kleppmann titled <a href="http://martin.kleppmann.com/2011/03/07/accounting-for-computer-scientists.html">Accounting for Computer Scientists</a>, a succinct introduction to double-entry accounting (although he doesn&#8217;t refer to it as such) in terms of basic graph theory:</p>
<blockquote><p>Eventually I figured it out: basic accounting is just graph theory. The traditional ways of representing financial information hide that structure astonishingly well, but once I had figured out that it was just a graph, it suddenly all made sense.</p></blockquote>
<p>He goes on to illustrate how a profit-and-loss statement and a balance sheet can be visualized on a simple DAG.  Good stuff&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://markshroyer.com/2011/03/accounting-compsci/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>OpenBSD Router Guide</title>
		<link>http://markshroyer.com/2011/02/openbsd-router-guide/</link>
		<comments>http://markshroyer.com/2011/02/openbsd-router-guide/#comments</comments>
		<pubDate>Mon, 07 Feb 2011 05:22:03 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Unix]]></category>

		<guid isPermaLink="false">http://markshroyer.com/?p=1041</guid>
		<description><![CDATA[I&#8217;ve been using OpenBSD as a NAT router for a few years now. Here&#8217;s a guide showing how I got it up and running, in case anyone is interested in doing the same.]]></description>
				<content:encoded><![CDATA[<p>I&#8217;ve been using OpenBSD as a NAT router for a few years now. <a href="http://markshroyer.com/guides/router/">Here&#8217;s a guide</a> showing how I got it up and running, in case anyone is interested in doing the same.</p>
]]></content:encoded>
			<wfw:commentRss>http://markshroyer.com/2011/02/openbsd-router-guide/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Updating your music collection with PowerShell</title>
		<link>http://markshroyer.com/2011/01/updating-music-with-powershell/</link>
		<comments>http://markshroyer.com/2011/01/updating-music-with-powershell/#comments</comments>
		<pubDate>Sun, 02 Jan 2011 23:28:40 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://markshroyer.com/?p=932</guid>
		<description><![CDATA[I&#8217;ve been banging my head against the wall on account of different music players which can&#8217;t play the subset of songs I have encoded as either Vorbis or AAC. So I&#8217;m slowly converting my entire music library over to MP3, &#8230; <a href="http://markshroyer.com/2011/01/updating-music-with-powershell/">Continue reading <span class="meta-nav">&#187;</span></a>]]></description>
				<content:encoded><![CDATA[<p>I&#8217;ve been banging my head against the wall on account of different music players which can&#8217;t play the subset of songs I have encoded as either Vorbis or AAC. So I&#8217;m slowly converting my entire music library over to MP3, which works everywhere, even if it&#8217;s less efficient.</p>
<p>But before I go digging through my old CDs I need to identify which albums I have to re-encode. My music is organized in folders by artist and then album, e.g.:</p>
<p><code>~\Music\Library\Pixies\Surfer Rosa\Where Is My Mind.mp3</code></p>
<p>So this means I effectively have to list the names of folders containing non-MP3 music files. Fortunately Windows PowerShell makes this a one-liner (though admittedly it&#8217;s a pretty long line; the backtick is PowerShell&#8217;s line continuation syntax):</p>
<pre>
ls -r Music\Library `
| ?{ $_.PSIsContainer -And ( $_.GetFiles() `
| ?{ $_.Name -Match "\.(m4[ap]|ogg|wma)$" } ) } `
| %{ New-Object PSObject -Property `
@{ Artist = (gi $_.PSParentPath).Name; Album = $_.Name } }
</pre>
<p>This will give you a nice list of non-MP3 albums, like:</p>
<pre>
Album              Artist
-----              ------
That's Your Fire   Aloha
Noble Beast        Andrew Bird
Believe It Mammals Bats &#038; Mice
Charm School       Bishop Allen
[...]
</pre>
<p>And thanks to PowerShell&#8217;s object-oriented pipes, this is done without having to worry about any quoting or whitespace issues.</p>
<p>The more I learn PowerShell, the less I find myself relying on Cygwin for simple Windows administration tasks. I think Microsoft did a nice job balancing all the different requirements they had to meet with PowerShell. At the very least, it&#8217;s good to finally see a strong command shell built into Windows.</p>
]]></content:encoded>
			<wfw:commentRss>http://markshroyer.com/2011/01/updating-music-with-powershell/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PXE booting OpenBSD on an ALIX via Ubuntu Live CD</title>
		<link>http://markshroyer.com/2010/12/openbsd-alix-via-ubuntu/</link>
		<comments>http://markshroyer.com/2010/12/openbsd-alix-via-ubuntu/#comments</comments>
		<pubDate>Sat, 01 Jan 2011 02:38:55 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Unix]]></category>

		<guid isPermaLink="false">http://markshroyer.com/?p=879</guid>
		<description><![CDATA[Update: I&#8217;ve expanded the contents of this post into a full guide to running an OpenBSD router on an ALIX board. This is a quick guide to booting the OpenBSD installer on a PC Engines ALIX board with tinyBIOS (such &#8230; <a href="http://markshroyer.com/2010/12/openbsd-alix-via-ubuntu/">Continue reading <span class="meta-nav">&#187;</span></a>]]></description>
				<content:encoded><![CDATA[<div style="margin: 1em 0; padding: 1em; background: #edeadc; font-style: italic;"><strong>Update:</strong> I&#8217;ve expanded the contents of this post into a <a href="/guides/router/">full guide to running an OpenBSD router on an ALIX board</a>.</div>
<p>This is a quick guide to booting the OpenBSD installer on a PC Engines ALIX board with tinyBIOS (such as the <a href="http://www.pcengines.ch/alix2d3.htm">ALIX 2d3</a>) via PXE, using just the following:</p>
<ul>
<li>PC with two network interfaces. One of these needs to be Ethernet, and the other must connect to the Internet. For example, any standard PC laptop with both WiFi and Ethernet adapters will work if there&#8217;s a WiFi Internet connection available.</li>
<li>Null modem cable</li>
<li>Ethernet crossover cable</li>
<li>USB-serial adapter (unless your PC has a built-in RS-232 port)</li>
<li><a href="http://www.ubuntu.com/desktop/get-ubuntu/download">Ubuntu Linux 10.10 desktop live CD</a></li>
</ul>
<p>Thanks to the versatility of the Ubuntu live CD (specifically the use of AUFS to provide a writable root directory in RAM), you can set up the necessary PXE boot server without making any permanent changes to your PC.</p>
<h3>Ubuntu packages</h3>
<p>Boot the Ubuntu live CD and quit the installer. Ensure that Ubuntu has a working Internet connection, then enable the &#8220;universe&#8221; package repository by uncommenting the corresponding lines in <code>/etc/apt/sources.list.</code> Now open a terminal and run the following commands to install prerequisite packages:</p>
<pre>$ sudo -s
# apt-get update
# apt-get install dhcp3-server tftpd xinetd cu</pre>
<h3>Network configuration &amp; NAT</h3>
<p>Run this command to configure a static address on the Ethernet interface:</p>
<pre># ifconfig eth0 up 192.168.2.1 netmask 255.255.255.0</pre>
<p>I&#8217;ve found you may also need to configure the static address in the &#8220;Network Connections&#8221; dialog (under Preferences in the System menu) to prevent Network Manager from getting in the way. This is sort of hackish, but we only need it to work for the duration of the install.</p>
<p>Now enable routing and configure a simple NAT using iptables so that the ALIX board can access the internet through your PC&#8217;s wireless connection:</p>
<pre># echo 1 &gt; /proc/sys/net/ipv4/ip_forward
# iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
# iptables -A FORWARD -i eth0 -j ACCEPT
# iptables -A FORWARD -i wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT</pre>
<p>Connect your PC&#8217;s Ethernet port to the first port on your ALIX board using the crossover cable. On the ALIX 2d3, the first port is the one adjacent to the USB ports.</p>
<h3>DHCP server</h3>
<p>Replace <code>/etc/dhcp3/dhcpd.conf</code> with the following contents:</p>
<pre>authoritative;

shared-network LOCAL-NET {
    option domain-name-servers 8.8.8.8;

    subnet 192.168.2.0 netmask 255.255.255.0 {
        option routers 192.168.2.1;
	filename "pxeboot";
	range 192.168.2.100 192.168.2.200;
	default-lease-time 600;
	max-lease-time 7200;
    }
}</pre>
<p>Also, edit the file <code>/etc/default/dhcp3-server</code> so that the last line reads:</p>
<pre>INTERFACES="eth0"</pre>
<p>Now you can start the DHCP server.</p>
<pre># /etc/init.d/dhcp3-server start</pre>
<h3>TFTP server</h3>
<p>Create an xinetd file <code>/etc/xinetd.d/tftp</code> as:</p>
<pre>service tftp
{
    socket_type = dgram
    protocol = udp
    wait = yes
    user = root
    server = /usr/sbin/in.tftpd
    server_args = -s /tftpboot
}</pre>
<p>Next create the aforementioned directory <code>/tftpboot</code>. Download the files <code>bsd.rd</code> and <code>pxeboot</code> from the <code>/4.8/i386/</code> directory on your favorite <a href="http://www.openbsd.org/ftp.html">OpenBSD mirror</a> and copy them into this directory.</p>
<p>Restart xinetd to load the new configuration.</p>
<pre># /etc/init.d/xinetd restart</pre>
<h3>Serial console</h3>
<p>Connect your laptop&#8217;s serial port (or plugged-in USB-serial adapter) to the ALIX board&#8217;s serial port with your null modem cable, then use the <code>cu</code> command to connect to the serial console. For example, if you&#8217;re using a USB adapter and your ALIX&#8217;s BIOS has the default serial port settings:</p>
<pre># cu -e -o -s 38400 -l /dev/ttyUSB0</pre>
<h3>PXE boot</h3>
<p>With your serial console ready, plug in the ALIX board&#8217;s power adapter, and you should see the board begin to boot. While the memory check is being performed, press the &#8216;s&#8217; key to enter the tinyBIOS settings, and verify that PXE boot is enabled (if it isn&#8217;t, press &#8216;e&#8217; to toggle it).</p>
<p>After exiting the BIOS settings menu, the board will reboot. It should find your PXE server and bring you to an OpenBSD boot menu. Enter the following at this menu:</p>
<pre>boot&gt; stty com0 38400
boot&gt; set tty com0
boot&gt; bsd.rd</pre>
<p>The installer will boot from the <code>bsd.rd</code> image that you downloaded. Now perform the installation as normal, but remember to configure the serial port as your system console in the installer.</p>
]]></content:encoded>
			<wfw:commentRss>http://markshroyer.com/2010/12/openbsd-alix-via-ubuntu/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Advanced Kindle store search</title>
		<link>http://markshroyer.com/2010/12/kindle-search/</link>
		<comments>http://markshroyer.com/2010/12/kindle-search/#comments</comments>
		<pubDate>Thu, 30 Dec 2010 04:31:53 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://markshroyer.com/?p=873</guid>
		<description><![CDATA[I just found a great web site providing a better Kindle content search than what&#8217;s baked into Amazon: eReaderIQ.com. You can search by price, publication date, reading level, and whether the book you want is in the public domain, among &#8230; <a href="http://markshroyer.com/2010/12/kindle-search/">Continue reading <span class="meta-nav">&#187;</span></a>]]></description>
				<content:encoded><![CDATA[<p>I just found a great web site providing a better Kindle content search than what&#8217;s baked into Amazon: <a href="http://www.ereaderiq.com/">eReaderIQ.com</a>. You can search by price, publication date, reading level, and whether the book you want is in the public domain, among other things.</p>
<p>For example, <a href="http://www.amazon.com/s?ie=UTF8&amp;node=154606011&amp;p_15=public%20domain&amp;p_36=0-0&amp;kc=AG56TWVU5XWC2&amp;redirect=true">this query</a> lists only free Kindle books in the public domain. Very handy.</p>
]]></content:encoded>
			<wfw:commentRss>http://markshroyer.com/2010/12/kindle-search/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Microsoft Outlook ruins my evening</title>
		<link>http://markshroyer.com/2010/12/outlook-ruins-evening/</link>
		<comments>http://markshroyer.com/2010/12/outlook-ruins-evening/#comments</comments>
		<pubDate>Fri, 10 Dec 2010 02:44:19 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://markshroyer.com/?p=564</guid>
		<description><![CDATA[I had a funny experience yesterday. I don&#8217;t typically use Microsoft Outlook with my home email account. But it was bundled with the copy of Office 2010 that I installed a few months ago, and I figured: heck, if I &#8230; <a href="http://markshroyer.com/2010/12/outlook-ruins-evening/">Continue reading <span class="meta-nav">&#187;</span></a>]]></description>
				<content:encoded><![CDATA[<p>I had a funny experience yesterday.</p>
<p>I don&#8217;t typically use Microsoft Outlook with my home email account. But it was bundled with the copy of Office 2010 that I installed a few months ago, and I figured: heck, if I have it anyway I might as well hook it up to my personal IMAP account. So I configured Outlook, played with it a bit, then promptly forgot all about it.</p>
<p>Fast forward to yesterday evening. I launch my copy of Outlook for the first time in months in order to try something with the calendar, but then I get distracted and walk away from the computer. Five minutes later my cell phone gets a message from my FreeBSD server:</p>
<blockquote><p>EMAIL SERVER HIJACKED &#8211; FIREWALLING POSTFIX</p></blockquote>
<p>Oh. Fun.</p>
<p>A slight digression before I can get on with the story: I try my very best to be a good Internet citizen, and that of course means not allowing oneself to become a spam relay. Because this email server exists only for my personal use, it was simple to write a Perl script that monitors my Postfix logs and, if it sees anything grossly out of the ordinary sent out through the server, reconfigures PF to block outbound connections to TCP port 25. It seemed the responsible thing to do, especially since some PHP applications on my web server have permission to relay through this Postfix instance; and let&#8217;s face it, WordPress has a less than stellar security track record.</p>
<p>This script has never given me a false positive. So this was definitely cause for concern.</p>
<p>I shelled into my VPS and went straight for the mail logs. I was dismayed to find more than twenty entries like this:</p>
<pre>Dec  8 23:35:38 frodo postfix/smtpd[13146]: 13DB43F4DC: \
    client=redacted[xxx.xxx.xxx.xxx], sasl_method=LOGIN, sasl_username=redacted
Dec  8 23:35:39 frodo postfix/cleanup[13148]: 13DB43F4DC: \
    replace: header Received: from noatun (redacted [xxx.xxx.xxx.xxx]) \
(Authenticated sender: redacted)
by frodo.paleogene.net (Postfix) with ESMTPSA id 13DB43F4DC
for &lt;redacted@example from redacted[xxx.xxx.xxx.xxx]; \
    from=&lt;redacted@example.com&gt; to=&lt;redacted@example.com&gt; \
    proto=ESMTP helo=&lt;noatun&gt;: Received: from auth-client.paleogene.net \
    (auth-client.paleogene.net [206.125.175.178])
(Authenticated sender: hidden)
by frodo.paleogene.net (Postfix) with ESMTPSA id 13DB43F4DC
for &lt;redacted@example.com&gt;; Wed,  8 Dec 2010 23:35:37 -0500 (EST)</pre>
<p>All of them sent within seconds of one another, and all of them to random Gmail accounts and other recipients that I absolutely did not recognize. Worse, the messages had been sent through a properly SASL authenticated connection from my laptop (HELO noatun). To quote that old horror flick: <em>They&#8217;re coming from inside the house!</em></p>
<p>I&#8217;ve heard tell of Windows viruses that will quietly send spam through the user&#8217;s configured Outlook mail account, so that the messages originate from a legitimate mail server and have a better chance of skipping recipients&#8217; spam folders. I have no idea whether this is actually a common occurrence in the real world, but it&#8217;s plausible enough, and I couldn&#8217;t think of any better explanation why 24 messages had been sent out from my computer in a matter of seconds, all to recipients I&#8217;d never heard of. I still could not fathom how a spam bot might have made its way onto my laptop despite my precautions, but better safe than sorry&#8230;</p>
<p>Process Explorer and TCPView failed to reveal anything suspicious, so I shut down the laptop, yanked out its hard drive, hooked that up to another machine via a USB-SATA adapter, and started a full offline virus scan.</p>
<p>Meanwhile I kept poking around on the mail server. I had a bit of luck when I checked <code>/var/spool/postfix/deferred</code> &#8212; a few messages had been caught in the Postfix outbound queue when my script added its firewall rule. I was morbidly curious what had been sent.</p>
<p>&#8230;it was a freaking <a title="E-mail tracking - Wikipedia" href="http://en.wikipedia.org/wiki/E-mail_tracking#Read-receipts">Message Disposition Notification</a>.</p>
<p>Now sure, I knew all about email read receipts, but I&#8217;d immediately ruled them out as the cause of this behavior because all of my email clients, Outlook included, are configured to at least ask my permission before sending one:</p>
<div id="attachment_668" class="wp-caption aligncenter" style="width: 580px"><a rel="attachment wp-att-668" href="http://markshroyer.com/2010/12/outlook-ruins-evening/outlook-tracking/"><img class="size-full wp-image-668  " title="Outlook tracking options" src="http://markshroyer.com/wp-content/uploads/2010/12/outlook-tracking.png" alt="Outlook tracking options, showing &quot;Ask each time whether to send a read receipt&quot;" width="570" height="285" /></a><p class="wp-caption-text">Outlook completely ignores this</p></div>
<p>And anyway, I hadn&#8217;t read or deleted any email messages in Outlook, so why would it want to send any MDNs in the first place?</p>
<p>I did some googling and the pieces slowly began falling into place. It turns out there&#8217;s a horrific bug in recent Outlook versions&#8217; handling of read receipts: unread messages deleted from an IMAP folder can send a &#8220;not read&#8221; MDN, <a href="http://www.emaildiscussions.com/showthread.php?t=51114">even if you&#8217;ve explicitly configured Outlook not to do so</a>.</p>
<p>And as I had just discovered, it isn&#8217;t even necessary to delete the messages from within Outlook itself to trigger this behavior. Months ago when I set up Outlook for my IMAP account, I was subscribed to several Debian and other technical mailing lists that I&#8217;ve since left (these days I prefer to use the <a title="Gmane -- Mail To News And Back Again" href="http://gmane.org/">Gmane</a> NNTP gateway to read such lists). Apparently when Outlook started up and noticed that these lists&#8217; mail folders no longer existed on my server, it decided to send a &#8220;not read&#8221; notification for each unread message that requested one in Outlook&#8217;s <em>old cached copy</em> of the folders.</p>
<p>Some searching of the mailing list archives confirmed my hypothesis. Every recipient of one of these mystery messages had posted something to one of these lists, presumably (probably unknowingly) including a <code>Disposition-Notification-To:</code> header in their posts.</p>
<p>I can&#8217;t find the words to describe just how wrongheaded this behavior is. Not only is it semantically incorrect &#8212; how can Outlook assume that just because a message has been deleted from the IMAP server, it wasn&#8217;t first read in some other mail client before being purged? &#8212; but it&#8217;s a horrendous privacy risk too, and a boon to email address harvesters.</p>
<p>And in this case, it led me to waste two hours of my time as I had to diagnose this unsolicited, seemingly spam bot-like burst of messages that went out through my server.</p>
<p>As much as it has improved over the years, I think this proves Outlook 2010 is still entirely untrustworthy as an email client. Avoid using it if at all possible.</p>
]]></content:encoded>
			<wfw:commentRss>http://markshroyer.com/2010/12/outlook-ruins-evening/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Ars Technica &#8220;Getting to QED&#8221;</title>
		<link>http://markshroyer.com/2010/11/etting-to-qed/</link>
		<comments>http://markshroyer.com/2010/11/etting-to-qed/#comments</comments>
		<pubDate>Thu, 25 Nov 2010 01:48:23 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://markshroyer.com/?p=550</guid>
		<description><![CDATA[Ars Technica has a four-part introductory series on logic and argumentation, entitled &#8220;Getting to QED.&#8221; In some cases, there is a way to tell good arguments from bad using what is called informal logic. This name distinguishes it from formal &#8230; <a href="http://markshroyer.com/2010/11/etting-to-qed/">Continue reading <span class="meta-nav">&#187;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Ars Technica has a four-part introductory series on logic and argumentation, entitled &#8220;<a href="http://arstechnica.com/tech-policy/guides/2010/11/getting-to-qed-part-1.ars/">Getting to QED</a>.&#8221;</p>
<blockquote><p>In some cases, there is a way to tell good arguments from bad using what is called informal logic. This name distinguishes it from formal logic, which is used in mathematics; natural language is less precise than mathematics, and does not always follow the same rules. Perhaps more surprisingly, the name also reflects the fact that there is a lot of disagreement over what it means. Informal logic is actually a fairly young discipline, developed in the 1960s, and intended to apply new techniques from formal logic to argument and critical thinking. Philosophers are still wrestling with this application, and there are several competing schools of thought.</p>
<p>For all the differences, there are some core elements upon which everyone agrees. This article is a tutorial introducing these basic ideas of informal logic. In particular, we focus on deductive reasoning, which is one of the cornerstones of analytical thought. Hopefully you will come away from this article with the tools to distinguish a productive debate from an acrimonious flame war.</p></blockquote>
<p>This should be required reading for anyone who&#8217;s engaged in debate on a mailing list or web forum.</p>
]]></content:encoded>
			<wfw:commentRss>http://markshroyer.com/2010/11/etting-to-qed/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Jaimie&#8217;s giant robot project</title>
		<link>http://markshroyer.com/2010/11/giant-robot/</link>
		<comments>http://markshroyer.com/2010/11/giant-robot/#comments</comments>
		<pubDate>Sun, 14 Nov 2010 04:31:27 +0000</pubDate>
		<dc:creator>Mark</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://markshroyer.com/?p=537</guid>
		<description><![CDATA[This guy is the best kind of crazy&#8230; How am I just finding this now? He&#8217;s made major progress since filming that video; check out his YouTube channel for more. Seriously man, thumbs up&#8230;]]></description>
				<content:encoded><![CDATA[<p><a href="http://jamius.com/">This guy</a> is the best kind of crazy&#8230;</p>
<p style="text-align: center;"><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="480" height="385" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/86Krv3gE-c4?fs=1&amp;hl=en_US" /><param name="allowfullscreen" value="true" /><embed type="application/x-shockwave-flash" width="480" height="385" src="http://www.youtube.com/v/86Krv3gE-c4?fs=1&amp;hl=en_US" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p>How am I just finding this now? He&#8217;s made major progress since filming that video; check out <a title="YouTube - JMEMantzel's Channel" href="http://www.youtube.com/user/JMEMantzel">his YouTube channel</a> for more. Seriously man, thumbs up&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://markshroyer.com/2010/11/giant-robot/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
<!-- This Quick Cache file was built for (  markshroyer.com/feed/ ) in 0.48253 seconds, on May 19th, 2013 at 1:04 am UTC. -->
<!-- This Quick Cache file will automatically expire ( and be re-built automatically ) on May 19th, 2013 at 2:04 am UTC -->
<!-- +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<!-- Quick Cache Is Fully Functional :-) ... A Quick Cache file was just served for (  markshroyer.com/feed/ ) in 0.00054 seconds, on May 19th, 2013 at 2:01 am UTC. -->