<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Fred Clifts Blog</title><link href="http://www.clift.org/fred/" rel="alternate"></link><link href="http://www.clift.org/fred/feeds/atom.xml" rel="self"></link><id>http://www.clift.org/fred/</id><updated>2019-07-05T22:00:00-06:00</updated><entry><title>Two Little-Known KVM Options Save the Day</title><link href="http://www.clift.org/fred/two-little-known-kvm-options-save-the-day.html" rel="alternate"></link><updated>2019-07-05T22:00:00-06:00</updated><author><name>Fred Clift</name></author><id>tag:www.clift.org,2019-07-05:fred/two-little-known-kvm-options-save-the-day.html</id><summary type="html">&lt;h3&gt;Auto Restart KVM VMs while forcing KVM snapshot reversion&lt;/h3&gt;
&lt;p&gt;(this is mostly notes for my later self - feel free to drop me a line to ask questions)&lt;/p&gt;
&lt;h4&gt;TLDR My problem:&lt;/h4&gt;
&lt;p&gt;I have some stateless throwaway vm instances which discard all changes every power-off.  Also, the closed-source software in the vm has driver issues that occasionally require a reboot, detected from inside the vm and initiated as an OS reboot.  When rebooting, I want to simulate power-off/power-on to trigger the KVM snapshot discard.  And, I want to make sure that any vm that shuts down gets restarted.  How?  Obscure qemu-kvm options and libvirt hooks to the rescue - special use for -snapshot and -no-reboot.&lt;/p&gt;
&lt;h4&gt;More detail:&lt;/h4&gt;
&lt;p&gt;I use libvirt (virsh) to manually control some transient, sort of stateless KVM virtual machines. I need to periodically stop/start those vms automatically (from inside so I can do a clean service shutdown). Also I want to auto-restart  particular vms any time they shut down. The VMs run kvm-qemu snapshots (-snapshot option) that throw away all system changes at every power-off. &lt;/p&gt;
&lt;p&gt;I have a close-source application that occasionally gets into an error state that can not be resolved by restarting the software (driver issues).  It requires a full (virtual) system reboot.  &lt;/p&gt;
&lt;p&gt;A small monitor was written prior to my involvement in this project that can detect the unfixable error state, and initiate a nice clean service shutdown and reboot to resolve the problem.  In KVM this results in a warm boot which doesn't throw away the -snapshot saved changes.&lt;/p&gt;
&lt;p&gt;So, qemu-kvm has an option -no-reboot that forces process exit when the vm tries to do a warm boot.  This shuts off the VM but does not restart it.  I need to auto-restart vms that shut themselves down by trying to reboot.&lt;/p&gt;
&lt;p&gt;So, I really have three requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;qemu-kvm needs to be invoked with -snapshot (fresh image every power off)&lt;/li&gt;
&lt;li&gt;Reboots should really be a libvirt stop/start to get a 'cold boot' effect and throw away the snapshot,&lt;/li&gt;
&lt;li&gt;Servers can reboot themselves.  These warm boots are turned into a VM stop, and they need to be auto restarted asap&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While qemu-kvm has the support I need, the version of libvirt on Centos 7 that I use doesn't have direct support for either option.  I make a wrapper for qemu-kvm and then specify a custom emulator for these vms.  I use both -snapshot and -no-reboot like this:&lt;/p&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1
2&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
    &lt;span class="nb"&gt;exec&lt;/span&gt; /usr/libexec/qemu-kvm &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; -snapshot -no-reboot
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;And then we replace the &lt;emulator&gt; block in the vm definition.  This meets my first two requirements, but any system reboot or periodic shutdown will stay off.&lt;/p&gt;
&lt;p&gt;What can I do to make sure they are always running?  First mark them to start on host boot:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;virsh autostart foo
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;which makes them start when the host boots but I still need to make sure they get restarted if they stop.&lt;/p&gt;
&lt;p&gt;There are a few options I considered to solve this problem.  &lt;/p&gt;
&lt;p&gt;First, I could add some kind of 'forever' loop to the emulator script above that will just run the emulator again once it exits. e.g.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt; &lt;span class="cp"&gt;#!/bin/sh&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;exec&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;libexec&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;qemu&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;kvm&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;$@&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;snapshot&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;no&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;reboot&lt;/span&gt;
    &lt;span class="n"&gt;done&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Or Second, I could write some kind of supervisor that tries to start any stopped vms, like a standalone daemon, or an every-minute-run cronjob. &lt;/p&gt;
&lt;p&gt;Third, Libvirt supports event hooks. I can run my own code when certain events happen.  The hook would watch for shutdown events and then 'virsh start' a vm that just shut down.  This is the path I started with (because I use hooks for some autoscaling functionality too).  The problem is that there is a deadlock.  "virsh start foo" in a hook is running before the VM is actually stopped and the process will hang forever waiting for the shutdown to finish. &lt;/p&gt;
&lt;p&gt;I confess to taking the lazy way out.  I have a hook that gets shutdown events, forks child in the background and returns.  The child sleeps for 3 seconds - presumably enough for the vm to stop, and then does a 'virsh start'  like this:&lt;/p&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c"&gt;# install into /etc/libvirt/hooks/qemu&lt;/span&gt;
&lt;span class="c"&gt;# any vm that shuts down will be restarted&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$#&amp;quot;&lt;/span&gt;&lt;span class="s2"&gt; -lt 2 ]; then&lt;/span&gt;
&lt;span class="s2"&gt;    echo &amp;quot;&lt;/span&gt;usage: &lt;span class="nv"&gt;$0&lt;/span&gt; &amp;lt;domname&amp;gt; &amp;lt;event&amp;gt; end -&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;fi&lt;/span&gt;

&lt;span class="s2"&gt;# release event signifies shutdown is finished&lt;/span&gt;
&lt;span class="s2"&gt;if [ &amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot; == &amp;#39;release&amp;#39; ]; then&lt;/span&gt;
&lt;span class="s2"&gt;    sh -c &amp;quot;&lt;/span&gt;sleep 3&lt;span class="p"&gt;;&lt;/span&gt; virsh start &lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot; &amp;lt; /dev/null 2&amp;amp;&amp;gt;1 &amp;gt;/dev/null &amp;amp;&lt;/span&gt;
&lt;span class="s2"&gt;fi&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Dirty, but it works.  Note that Centos 7 does not by default have /etc/libvirt/hooks - you have to make that directory and restart libvirtd to pick up any hooks you add.&lt;/p&gt;
&lt;p&gt;If I didn't have other hooks code (for autoscaling), I probably would have gone with the first option above as it is a trivial few more lines of bash.&lt;/p&gt;
&lt;p&gt;Is there some other way I should have done this?  Drop me a line if you see something obvious I missed.&lt;/p&gt;</summary><category term="KVM libvirt"></category></entry><entry><title>Bashcpio - pure bash (almost) cpio archive extraction</title><link href="http://www.clift.org/fred/bashcpio-pure-bash-almost-cpio-archive-extraction.html" rel="alternate"></link><updated>2016-06-05T16:00:00-06:00</updated><author><name>Fred Clift</name></author><id>tag:www.clift.org,2016-06-05:fred/bashcpio-pure-bash-almost-cpio-archive-extraction.html</id><summary type="html">&lt;p&gt;Ok - let's get this out of the way.  Is this important? No.  Is it groundbreaking?
No.  Can I even explain how cool it is to non-technical friends? No. Could it
ever possibly be useful?  Maybe!  see below.&lt;/p&gt;
&lt;p&gt;I just spent a few evenings writing a pure-bash cpio extraction implementation.
My target was centos/fedora/redhat rpms, which means  gzip/xz compression, written 
with the so-called 'New-Ascii' header format.  The other formats wouldn't be that
hard to support but I would guess that 98% of any use of cpio in the year 2016
is related to some kind of rpm package.&lt;/p&gt;
&lt;p&gt;And so is born bashcpio - check it out on github:  &lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/minektur/bashcpio"&gt;https://github.com/minektur/bashcpio&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;pure bash?&lt;/h2&gt;
&lt;p&gt;Also, it's not quite 'pure' bash - but no bash script ever is.  I worked hard
to ensure that the external dependencies were kept to a bare minimum. &lt;/p&gt;
&lt;p&gt;In addition to an actual bash binary, you need a few external dependencies:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;dd, mkdir, dirname, chown, chmod, date, touch, ln
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;There are a couple that might be considered optional.  I could probably write some
tortured complicated code to replace dirname that has no dependencies on external
binaries, though I have not (yet?) done so.  If you don't care about time-stamps on 
files and directories that you extract, date and touch could both go away.  The 
rest though, are pretty difficult to avoid using if you want it to work at all.&lt;/p&gt;
&lt;p&gt;Bash (and some other shell utilities that you might consider, like awk and sed) all
have this nasty habit of "eating" null bytes in anything they read.  So for instance,
you can't place the the content of a binary file in a bash variable.  You'll get
everything up to the first null value.  This makes dealing with arbitrary binary
data with bash problematic at best.  If however, you can be reasonably sure that 
some portions of your file will be ascii, then you can carefully operate on those
parts with bash, using the assistance of dd.&lt;/p&gt;
&lt;p&gt;In this case, the header format I'm trying to read is guaranteed to be 100 bytes, 
followed by a C-style string, and a null terminator, followed by some padding, a 
bunch of arbitrary data and some padding.  The saving grace is that you know where
the ascii header starts, how long it is, and inside there is data about how long
the file name is, and how long the file is.  With that, you can read a header
and then safely read the filename, the sizes, and all the other header info.&lt;/p&gt;
&lt;p&gt;How?  I an pick apart the file with dd, carefully using the count= and skip= flags.&lt;/p&gt;
&lt;p&gt;All the rest of the external dependencies are for use in creating and modifying the
extracted files, setting ownership, group, permissions, mtime, etc.&lt;/p&gt;
&lt;h2&gt;Motivation?&lt;/h2&gt;
&lt;p&gt;I'm toying with the idea of adding a centos target to &lt;a href="https://github.com/dnschneid/crouton"&gt;crouton&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;created/maintained by David Schneider, with many many other contributors.&lt;/p&gt;
&lt;p&gt;Crouton is a tool for making (debian-derived) linux chroots that will run under
chromeos in dev mode.  (I'll talk more about this in some later blog post). Crouton
is an awesome tool and it's quite useful, but I use a lot of yum rpm and not a lot
of apt and deb.  &lt;/p&gt;
&lt;p&gt;On the chromeos devices there is a very minimal linux install that does little
besides running chrome browser full time.  Many common things you'd expect to
be at your fingertips at a bashprompt are missing.  Crouton fixes this.  And it
bootstraps itself using pretty much only bash and an 'ar' extractor written in bash,
since that is close to all that is available to start with.&lt;/p&gt;
&lt;p&gt;Seeing the clever ar.sh implementation, I got thinking about how one would do the
same with rpm based distros... and bashcpio was born.  I started with a careful
inventory of what shell binaries I had access to, and bash + dependencies does
the trick.  This of course does not give you a centos chroot - it is only one of
several necessary tools.  But I had fun making it work, except when I was trying to
get the padding right.  Ugh, how irritating.  I'm no expert bash programmer though
I know a lot more now than I used to.&lt;/p&gt;
&lt;h2&gt;Performance?&lt;/h2&gt;
&lt;p&gt;My initial implementation only took about 80x as long as the x86_64 debian trusty
binary that I got out of a crouton chroot.  The current implementation is much
faster now and it now only takes approximately 19 times as long at the C-binary.
To get a feel for it's performance, I grabbed the 350ish packages that make up a
current centos 7 minimal install.  It takes less than 4 minutes to unpack them.&lt;/p&gt;
&lt;p&gt;Please feel free to send me feedback on how crazy you think I am, or on what
glaring deficiency my code has.  I'm eager to talk about the ship I built in a bottle.&lt;/p&gt;</summary><category term="cpio"></category><category term="bash"></category><category term="crouton"></category><category term="chromeos"></category></entry><entry><title>Apple mail app TLS deficiencies revisited</title><link href="http://www.clift.org/fred/apple-mail-app-tls-deficiencies-revisited.html" rel="alternate"></link><updated>2015-10-02T09:00:00-06:00</updated><author><name>Fred Clift</name></author><id>tag:www.clift.org,2015-10-02:fred/apple-mail-app-tls-deficiencies-revisited.html</id><summary type="html">&lt;p&gt;A quick update:&lt;/p&gt;
&lt;p&gt;Mac OS X El Capitan has hit the streets so I retested.  The mail app still can not make any TLS connections using TLSv1.1 or 1.2.&lt;/p&gt;
&lt;p&gt;Also a /u/Tulsagrammer on Reddit pointed out to me that a the mystery of why this is so is even greater:  For (outbound) SMTP connections, the mail app can and does speak TLSv1.2.  It is ONLY imap connections that are limited to TLSv1.0.&lt;/p&gt;</summary><category term="security"></category></entry><entry><title>Frustration with Apple mail app on IOS and Yosemite</title><link href="http://www.clift.org/fred/frustration-with-apple-mail-app-on-ios-and-yosemite.html" rel="alternate"></link><updated>2015-09-10T09:00:00-06:00</updated><author><name>Fred Clift</name></author><id>tag:www.clift.org,2015-09-10:fred/frustration-with-apple-mail-app-on-ios-and-yosemite.html</id><summary type="html">&lt;p&gt;At &lt;a href="https://www.faxpipe.com"&gt;work&lt;/a&gt; I recently have been irritated by a problem that 
was exposed with Apple's mail app, both on IOS and Yosemite.  Among other things, I maintain an imap server
(using &lt;a href="http://www.dovecot.org"&gt;dovecot&lt;/a&gt; ) for our office email.  &lt;/p&gt;
&lt;h1&gt;First some background&lt;/h1&gt;
&lt;p&gt;Dovecot makes it easy to enable TLS, and disallow unencrypted logins.  It actually uses openssl under the hood, so whatever cyphers your version of ssl supports are the set dovecot will support. By default, dovecot has TLSv1.0, TLSv1.1, and TLSv1.2 
enabled by default.  In the web-browser world, for about the last year, there have been known crypto-attacks against TLSv1.0 and some concern about TLSv1.1. Recommendations for server operators have been to completely disable TLSv1.0.  For instance, Qualys has an awesome free &lt;a href="https://www.ssllabs.com/ssltest/"&gt;ssltest&lt;/a&gt; that gives poor grades to 1.0 deployments.&lt;/p&gt;
&lt;p&gt;More importantly, for my installation, the "Payment Card Industry" security standard - aka PCI - now strongly discourage the use of
what they call "SSL/early TLS".  See page 47 of &lt;a href="https://www.pcisecuritystandards.org/documents/PCI_DSS_v3-1.pdf"&gt;this pdf.&lt;/a&gt;  When you dig in to what this means, you find that  SSLv2 and SSLv3 (which predate TLS) and TLSv1.0 are specifically what they are talkign about.  These crypto standards are all pre-Y2K, and have been replaced by newer, 
better, more secure protocols.  Technically my email system is completely isolated from my payment-related systems, and thus
might get a pass during any PCI scan/audit.  But, to save the hassle, and argument about "compensating controls" from 3rd party vendors who help validate our compliance, I recently disabled TLSv1.0 on dovecot.  &lt;/p&gt;
&lt;p&gt;In dovecot this is easy.  In /etc/dovecot.conf.d/10-ssl.conf you add !TLSv1 to  your ssl_protocols.  Currently mine looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;ssl_protocols = !SSLv2 !SSLv3 !TLSv1
&lt;/pre&gt;&lt;/div&gt;


&lt;h1&gt;Now the Real Problem&lt;/h1&gt;
&lt;p&gt;As soon as I did this every iphone in the company, and every mac user using the mail app on Yosemite all were unable to get their email.  The IOS app gives a vauge error about not being able to "connect to the server" and the desktop app gives a 'timeout' error, and the mail connection doctor shows the connection appear to hang during setup.&lt;/p&gt;
&lt;p&gt;It turns out that mail.app can only speak TLSv1.0.  There is no support for 1.1 or 1.2.  TLSv1.0 was first defined as a spec in 1999, 1.1 in 2006 and 1.2 in 2008.  8 years ago.  Isn't it time for an update? I've found a few people complaining about this in Apple's support forums, but only recently has 1.0 been deprecated.  I expect more people will feel the pain as time progresses.&lt;/p&gt;
&lt;p&gt;To be fair to Apple, my 2 year old HTC One M7's mail app has the same issue - I'm told that versions 4.5 and later have TLSv1.2 support.&lt;/p&gt;
&lt;h1&gt;Testing Notes&lt;/h1&gt;
&lt;p&gt;While for https there are a few great free tools out there, for other tls-enabled protocols, it is much less fun.  Openssl
has a nice command line tool you can use for such things.  Note that the version on Yosemite is old.  Thus you'll need to
make sure and test from a more recent version.  I used the a linux server on the same network segment as my mail server. &lt;/p&gt;
&lt;p&gt;Here is how I tested:&lt;/p&gt;
&lt;p&gt;To make a simple connection test:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;openssl s_client -connect servername:143 -startls imap
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This will connect to "servername" on port 143 and initiate the imap version of starttls.  You should get a response back that gives details of the cert and any certificate chain, a copy of the server certificate, some debugging info and a block that might look something like this:&lt;/p&gt;
&lt;pre&gt;
    New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-GCM-SHA384
    Server public key is 2048 bit
    Secure Renegotiation IS supported
    Compression: NONE
    Expansion: NONE
    SSL-Session:
        Protocol  : TLSv1.2
        Cipher    : DHE-RSA-AES256-GCM-SHA384
        Session-ID: 03C689EDC9EC7F09BA904D44DAB8AB9E9CA835B4AED350E8147466FF62B211AF
        Session-ID-ctx:
        Master-Key: 5620257CDF5223C53F3A830B4078F78AAB40C2BDF2D3F41750879642E3C31A29A6BD8802CA8815B494141FBA76830C79
        Key-Arg   : None
        Krb5 Principal: None
        PSK identity: None
        PSK identity hint: None
        TLS session ticket lifetime hint: 300 (seconds)
        TLS session ticket:
        0000 - a4 dc 2e 63 45 fa 69 3c-06 13 23 43 67 09 a5 3c   ...cE.i&lt;..#Cg..&lt;
        0010 - fb e1 93 47 b8 17 95 f8-ee 84 cb 1d 89 c5 a3 a6   ...G............
        0020 - 8d 7e 20 94 f3 35 10 8d-de 55 eb 4b b2 64 9c 2b   .~ ..5...U.K.d.+
        0030 - 33 5a 5a 57 30 20 17 65-8f 20 43 7c 1d 32 ec fe   3ZZW0 .e. C|.2..
        0040 - 80 1c 1a d5 ae 17 97 13-78 b4 b1 01 43 43 27 1e   ........x...CC'.
        0050 - 9a 46 24 84 88 c7 37 9e-58 0e a7 b0 c0 a5 1f f8   .F$...7.X.......
        0060 - f4 90 3b 2c b8 00 65 55-51 8a 6d 36 0e 27 c1 5b   ..;,..eUQ.m6.'.[
        0070 - 03 c1 d9 25 a9 ae 2a 36-37 66 ad cf ea e4 72 b3   ...%..*67f....r.
        0080 - fb 9e 1b a7 6a 00 c3 96-b5 04 ea 0f f7 f9 5a 5f   ....j.........Z_
        0090 - ce d2 03 75 ec 6b 36 2c-01 94 ae 47 28 66 65 7d   ...u.k6,...G(fe}

        Start Time: 1441902734
        Timeout   : 300 (sec)
        Verify return code: 0 (ok)
    ---
    . OK Capability completed.
&lt;/pre&gt;

&lt;p&gt;At this point, if you know the imap protocol you can start typing commands.... For my purposes, seing . OK was enough to know
that I had connected.&lt;/p&gt;
&lt;p&gt;If you want to see if a specific version of tls is supported you can specify on the command line which version of tls to use:&lt;/p&gt;
&lt;pre&gt;
    openssl s_client -connect servername:143 -starttls imap -tls1

    ....
    SSL-Session:
        Protocol  : TLSv1
        Cipher    : DHE-RSA-AES256-SHA
        Session-ID: B73943E3C764F2A08120794941F3E314A758F8FB6D6D037B31B7ECF7F197C7ED
    ....
&lt;/pre&gt;

&lt;p&gt;That is of course if you haven't disabled TLSv1.0.&lt;/p&gt;
&lt;p&gt;If you have, the response looks something like:&lt;/p&gt;
&lt;pre&gt;
    openssl s_client -connect servername:143 -starttls imap -tls1

    CONNECTED(00000003)
    write:errno=104
    ---
    no peer certificate available
    ---
    No client certificate CA names sent
    ---
    SSL handshake has read 271 bytes and written 0 bytes
    ---
    New, (NONE), Cipher is (NONE)
    Secure Renegotiation IS NOT supported
    Compression: NONE
    Expansion: NONE
    SSL-Session:
        Protocol  : TLSv1
        Cipher    : 0000
        Session-ID:
        Session-ID-ctx:
        Master-Key:
        Key-Arg   : None
        Krb5 Principal: None
        PSK identity: None
        PSK identity hint: None
        Start Time: 1441903081
        Timeout   : 7200 (sec)
        Verify return code: 0 (ok)
    ---
&lt;/pre&gt;

&lt;p&gt;Openssl s_client also supports other tls-enbled protocols - see the documentation for details.&lt;/p&gt;
&lt;p&gt;Hopefully this will help you test your own servers.&lt;/p&gt;
&lt;h1&gt;Lousy Workaround&lt;/h1&gt;
&lt;p&gt;How to work around this?  Well, for us, a separate vlan, with a secondary IP address for the mail server, and special vpn access to that vlan.  Dovecot lets you have different ssl_protocol settings per IP that the daemon is listening on, so I have a block in my 10-ssl.conf that looks like this:&lt;/p&gt;
&lt;pre&gt;
    ssl_protocols = !SSLv2 !SSLv3 !TLSv1

    local 192.168.128.139 {
    ssl_protocols = !SSLv2 !SSLv3
    }
&lt;/pre&gt;

&lt;p&gt;which disabled TLSv1.0 on the public interface, but leaves it enabled on the more tightly controlled vlan.  Theoreticlly this is enough of a compensating control to keep the PCI auditors happy.  Yes, to check mail, my iphone users have to VPN in.  Ugh.  Hopefully Apple will fix this soon.&lt;/p&gt;</summary><category term="security"></category></entry><entry><title>What do you get from a $200 multimeter?</title><link href="http://www.clift.org/fred/what-do-you-get-from-a-200-multimeter.html" rel="alternate"></link><updated>2014-10-02T09:00:00-06:00</updated><author><name>Fred Clift</name></author><id>tag:www.clift.org,2014-10-02:fred/what-do-you-get-from-a-200-multimeter.html</id><summary type="html">&lt;p&gt;I was recently discussing with friends how I like Apple computers (e.g. the MacBook Pro I use for work) but have a hard time
justifying the extra cost when in many cases I can get something 'good enough' for half the price.&lt;/p&gt;
&lt;p&gt;And sometime around that conversation, the fact that I own and use a &lt;a href="http://en-us.fluke.com/products/digital-multimeters/fluke-87v-digital-multimeter.html"&gt;Fluke 87-V&lt;/a&gt;
 multimeter came up. &lt;/p&gt;
&lt;p&gt;&lt;img alt="fluke87-v" src="/fred/images/fluke87.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Considering the fact that I can drive 5 minutes from my house and buy a &lt;a href="http://www.harborfreight.com/7-function-multimeter-98025.html"&gt;cheap multimeter&lt;/a&gt;, 
I had to stop and think.  I do own several cheapies that I use regularly (e.g. one lives in my toolbox where it gets beat up a lot, one in the trailer, etc)&lt;/p&gt;
&lt;p&gt;I do have training in electronics, but it's not what I do for a day job.  My uses of a multimeter - do they justify 30x the cost for the fluke?  So I decided
to put together a list of what I have recently used the fluke for that I can't use the cheapys for.  &lt;/p&gt;
&lt;p&gt;I've had a couple of projects in the last few weeks that have involved using a multimeter:  Repairing a pinball machine, and helping my son diagnose a malfunctioning robot-kit he
was assembling.&lt;/p&gt;
&lt;p&gt;Here are some of the things I got for my money: &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Measures frequency (e.g. clock on PIC controller on robot)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Measures max/min/average from when I press a button to when I press it again - used to see if an AC relay was tripping while i was behind the pinball machine messing with connections in the backbox. I've also used this to help diagnose battery, cable, starter issues on my car. I can see the lowest voltage the battery produced under starter load.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;INSTANT continuity checking/beeping - hook one end with alligator clip to solenoid terminal on flipper, quickly drag other lead across many contacts on remote connector/wire-bundle to find the right wire in seconds, without even having to look at the meter...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Quality leads that I can use on high-voltage stuff and not worry that I'm going to injure myself&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;High enough voltage on 'diode-check' mode to actually light up an LED, or in this case an IR-LED on the robot, while viewing it with my cell-phone-camera to see it light up (seriously fun - point your crappy-cell-phone camera at your IR remote control and see the leds light - &lt;a href="https://www.youtube.com/watch?v=_m7454Xt-OI"&gt;https://www.youtube.com/watch?v=_m7454Xt-OI&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Capacitance testing&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Auto-ranging is SOOOO nice - there is ONE DC setting - not 8 different DC ranges. The fluke is not the fastest on selecting the best range - I've seen faster, but I dont want to afford meters that are faster.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I didn't think this would be such a big deal... but AUTO-POWER-OFF has saved my batteries so many times.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;One thing - I actually like the battery testing on my cheapy centech from harbor freight better. It uses a small load to actually measure current produced by the AA battery I'm testing.  It is much more accurate than looking at voltage on it...  Plus I worry much less about my kids testing batteries with a $6 multimeter - so that is what lives in the junk drawer in the kitchen. &lt;/p&gt;
&lt;p&gt;All that aside, I could probably get most of that list from a $100 multimeter...   For me, if you use a tool a lot, you should buy a great one and enjoy it.  Did I just talk myself into buying a new macbook?&lt;/p&gt;</summary><category term="gadgets"></category></entry><entry><title>Not Sharing python command history between python2 and python3</title><link href="http://www.clift.org/fred/not-sharing-python-command-history-between-python2-and-python3.html" rel="alternate"></link><updated>2014-09-16T09:00:00-06:00</updated><author><name>Fred Clift</name></author><id>tag:www.clift.org,2014-09-16:fred/not-sharing-python-command-history-between-python2-and-python3.html</id><summary type="html">&lt;p&gt;I'm finally getting around to switching most-of-the-time to python3.  &lt;/p&gt;
&lt;p&gt;One minor annoyance was that I had to fix my simple .pythonrc.py file that
I use to turn on command history for python2.&lt;/p&gt;
&lt;p&gt;In python 3, it appears that readline, tab-completion, and command history 
between sessions are automatically enabled.&lt;/p&gt;
&lt;p&gt;I used to have to do this for python2&lt;/p&gt;
&lt;p&gt;in my shell (bash in this case...)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;export PYTHONSTARTUP=~/.pypythonrc.py
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;and then make a file &lt;code&gt;.pythonrc.py&lt;/code&gt; in my home directory. The contents
of that file, were:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;# Add auto-completion and a stored history file of commands to your Python&lt;/span&gt;
&lt;span class="c"&gt;# interactive interpreter. Requires Python 2.0+, readline. Autocomplete is&lt;/span&gt;
&lt;span class="c"&gt;# bound to the Esc key by default (you can change it - see readline docs).&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Store the file in ~/.pythonrc.py, and set an environment variable to point&lt;/span&gt;
&lt;span class="c"&gt;# to it:  &amp;quot;export PYTHONSTARTUP=~/.pypythonrc.py&amp;quot; in bash.&lt;/span&gt;


&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;atexit&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;readline&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;rlcompleter&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;

&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/usr/local/lib/python2.7/site-packages&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;historyPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;HOME&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;.pyhistory&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;readline&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_and_bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;tab: complete&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_history&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;historyPath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;historyPath&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;readline&lt;/span&gt;
    &lt;span class="n"&gt;readline&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_history_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;historyPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;historyPath&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;readline&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_history_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;historyPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;atexit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;save_history&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;atexit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;readline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rlcompleter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;save_history&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;historyPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This would turn on readline, tab completion, and load any existing command history
so that I could repeat commands with either the arrow keys or good-ol' emacs-bindings
of cntrl-p/cntrl-n.  It also arranges to save my current history buffer to a file at 
exit so that it can be reloaded the next time I invoke a shell. It also twiddles my 
sys.path to include stuff I want, useful mostly for back when I didn't use virtualenv
so much.&lt;/p&gt;
&lt;p&gt;The mac Python3 build from python.org aparently already does all of this history and tab-completion stuff. 
but the format used to save history has changed sometime between the version of readline 
included with 2.7 and 3.4.  So the history files are not compatible. Python3 by default
uses the name .python_history.  I'm renaming my old history file to be &lt;code&gt;.pyhistory2&lt;/code&gt; so
there is no confusion later.&lt;/p&gt;
&lt;p&gt;I now do this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;# Add auto-completion and a stored history file of commands to your Python&lt;/span&gt;
&lt;span class="c"&gt;# interactive interpreter. Requires Python 2.0+, readline. Autocomplete is&lt;/span&gt;
&lt;span class="c"&gt;# bound to the Esc key by default (you can change it - see readline docs).&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Store the file in ~/.pythonrc.py, and set an environment variable to point&lt;/span&gt;
&lt;span class="c"&gt;# to it:  &amp;quot;export PYTHONSTARTUP=~/.pypythonrc.py&amp;quot; in bash.&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version_info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;major&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version_info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minor&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;atexit&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;readline&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;/usr/local/lib/python2.7/site-packages&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;historyPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;HOME&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;.pyhistory2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;readline&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_and_bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;tab: complete&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_history&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;historyPath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;historyPath&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;readline&lt;/span&gt;
        &lt;span class="n"&gt;readline&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_history_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;historyPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;historyPath&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;readline&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_history_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;historyPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;atexit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;save_history&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;save_history&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;historyPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;atexit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;readline&lt;/span&gt;

&lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="c"&gt;#other stuff?&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;While python 3 and 2 don't share history, at least it works in both.&lt;/p&gt;</summary><category term="python"></category></entry><entry><title>Chromecast - This is why we can't have nice things</title><link href="http://www.clift.org/fred/chromecast-this-is-why-we-cant-have-nice-things.html" rel="alternate"></link><updated>2014-09-11T10:00:00-06:00</updated><author><name>Fred Clift</name></author><id>tag:www.clift.org,2014-09-11:fred/chromecast-this-is-why-we-cant-have-nice-things.html</id><summary type="html">&lt;p&gt;There was a recent chromecast firmware update from google.  This wasn't the long awaited
new functionality promised at IO.  It ostensibly has better tab-casting performance
and some bug fixes (e.g. change in some apis related to subtitles, among others).&lt;/p&gt;
&lt;p&gt;There has been some speculation that the reason for the update, without the promised
"Summer 2014" new functionality was to address a recent announcement by some smart guys
on XDA about a new method of rooting the device - the so-called &lt;a href="http://forum.xda-developers.com/hardware-hacking/chromecast/root-hubcap-chromecast-root-release-t2855893"&gt;HubCap Root Release&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One other unmentioned change, which affects me, was the change to how Chrome Remote Debugging
works on the device. &lt;/p&gt;
&lt;p&gt;It used to be that the remote developer port was open 24x7 on devices in development mode.  Now, it 
appears that the ONLY time a device has the developer port available and open is when running a
dev app that you have registered in the developer console.  When the app ends, the connection
is closed.  I presume that there is some firewall-rule magic going on - that the port is only
opened after your dev app has successfully launched.&lt;/p&gt;
&lt;p&gt;This makes my &lt;a href="http://www.clift.org/fred/chromecast-displaying-arbitrary-urls-using-pychromecast.html"&gt;Display Whatever You Want&lt;/a&gt; trick useless.  Yes, it still works, but ONLY when you have already
launched some app you have control of.  I feel like going off on a long rant about the folks
at Google breaking my toys and restating the title of this post.  But...  I want to try to
give them the benefit of the doubt.  &lt;/p&gt;
&lt;p&gt;I'm pretty sure that they didn't rush out a new firmware
update just to spite me. They must have had this change in the works for quite a while now.  &lt;/p&gt;
&lt;p&gt;My current operating theory is that having the remote-debugging 
stuff available all the time was a security issue for them.  Specifically, they have content
providers (think "big media streaming companies"), that use DRM - I would guess that having
debug access to the receiver app would make it much easier to reverse-engineer and circumvent
content protections. DRM is probably a necessary evil, placating content providers, while
only keeping honest people honest.  &lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;I had a simple automatically starting slideshow program that no longer works.  But, I can work around it.&lt;/p&gt;
&lt;p&gt;Using my native-api POC from last post, I have been able to launch my app, which 
only needed slight tweaks to be considered a "real" receiver app.&lt;/p&gt;
&lt;p&gt;Here is simplified html similar to what I did before the remote-debugging port was restricted.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;castphotosaver&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
            function new_img() {
                var i = document.getElementById(&amp;#39;slide&amp;#39;);
                i.src=&amp;#39;/img?&amp;#39;+Math.random();
            }
        &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;style&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/css&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
img { display: block; margin-left: auto; margin-right: auto; max-width: 100%; max-height: 100%; }
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;slide&amp;#39;&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/img&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I feed it images from a little webservice - each hit on /img gives the next photo.  I add the
query parameter as a (lame) way of keeping the browser from caching and not reloading the image.&lt;/p&gt;
&lt;p&gt;The bare minimum to turn this into a receiver that the cast device will like is this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;castphotosaver&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;script&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;
                &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;//www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
            function new_img() {
                var i = document.getElementById(&amp;#39;slide&amp;#39;);
                i.src=&amp;#39;/img?&amp;#39;+Math.random();
            }
        &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;style&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/css&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
img { display: block; margin-left: auto; margin-right: auto; max-width: 100%; max-height: 100%; }
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;slide&amp;#39;&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/img&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  window.onload = function() {
    window.castReceiverManager = cast.receiver.CastReceiverManager.getInstance();
    //castReceiverManager.start({maxInactivity: 60});
    castReceiverManager.start();
    setInterval(&amp;#39;new_img();&amp;#39;,3000);
  };
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;All you have to add is code to load the receiver library and instantiate and start a 'CastReceiverManager'.  As a side note you can control the PING/PONG frequency for connected senders with that commented
out maxInactivity parameter...  &lt;/p&gt;
&lt;p&gt;The sender code to launch this is left as an exercise to the reader.  In reality I never wrote a sender.  I used my python-native-api proof-of-concept to load the app.&lt;/p&gt;
&lt;p&gt;I fully expect a new update to break this in the future.  &lt;/p&gt;
&lt;p&gt;In retrospect, I think that perhaps it would have been better if Google had NOT restricted when 
the debugger port was available on debug devices, but rather restricted the debug device to only
run your own registered apps.  At least it would make my life easier.  &lt;/p&gt;
&lt;p&gt;This is why we can't have nice things.&lt;/p&gt;</summary><category term="web"></category><category term="chrome"></category><category term="chromecast"></category><category term="gadgets"></category></entry><entry><title>Chromecast - steps closer to a python native api</title><link href="http://www.clift.org/fred/chromecast-steps-closer-to-a-python-native-api.html" rel="alternate"></link><updated>2014-09-05T15:00:00-06:00</updated><author><name>Fred Clift</name></author><id>tag:www.clift.org,2014-09-05:fred/chromecast-steps-closer-to-a-python-native-api.html</id><summary type="html">&lt;p&gt;So, after seeing this:  &lt;a href="https://gist.github.com/TheCrazyT/11263599"&gt;https://gist.github.com/TheCrazyT/11263599&lt;/a&gt; I got
more interested in being able to speak the native chromecast api from 
python.  &lt;/p&gt;
&lt;p&gt;That lead me to this presentation by Sebastian Mauer: &lt;a href="http://www.slideshare.net/mauimauer/chrome-cast-and-android-tv-add14"&gt;http://www.slideshare.net/mauimauer/chrome-cast-and-android-tv-add14&lt;/a&gt; especially slide 20, which lead to a bit more google searching.&lt;/p&gt;
&lt;p&gt;This lead to a couple of related projects by Vincent Bernat and Thibaut Séguy, 
nodecastor and node-castv2/node-castv2-client - both 
&lt;a href="http://nodejs.org"&gt;node.js&lt;/a&gt; apps that speak approximations of the same 
protocol that the chrome-browser plugin uses to talk to the chromecast device.  &lt;/p&gt;
&lt;p&gt;They are both based on the &lt;a href="http://code.google.com/p/protobuf"&gt;protobuf&lt;/a&gt; data format 
used internally and some places externally at Google. TL;DR: make a .proto 
file that defines a bunch of data fields and Googles protobuf code will 
automatically format data into and parse data out of very compact, efficient 
binary blobs that you can use for data serialization between various 
platforms and languages.&lt;/p&gt;
&lt;p&gt;The .proto file used by chromium builds to enable the browser plugin to 
communicate with devices is used by nodecastor and node-castv2 to allow them 
to speak with chromecast devices.  &lt;/p&gt;
&lt;p&gt;To give you a flavor, here are partial contents of the cast_channel.proto file...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
syntax = &amp;quot;proto2&amp;quot;;
option optimize_for = LITE_RUNTIME;
package extensions.api.cast_channel;
message CastMessage {
  enum ProtocolVersion {
    CASTV2_1_0 = 0;
  }
  required ProtocolVersion protocol_version = 1;
  required string source_id = 2;
  required string destination_id = 3;
  required string namespace = 4;
  enum PayloadType {
    STRING = 0;
    BINARY = 1;
  }
  required PayloadType payload_type = 5;
  optional string payload_utf8 = 6;
  optional bytes payload_binary = 7;
}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Once you have run the google 'proto' compiler, you get a file: cast_channel_pb2.py 
that you can import into your programs and now have an easy way to encode your 
data across platforms/languages for rpc/data interchange.&lt;/p&gt;
&lt;p&gt;In python it might look at simple as this:&lt;/p&gt;
&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="c"&gt;#properly formatted PONG response for a receiver PING request&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;cast_channel_pb2&lt;/span&gt;
&lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cast_channel_pb2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CastMessage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;protocol_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CASTV2_1_0&lt;/span&gt;
&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;source_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;sender-0&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;destination_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;receiver-0&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;payload_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cast_channel_pb2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CastMessage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;STRING&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;heartbeat&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;payload_utf8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&amp;quot;{ &amp;quot;type&amp;quot;: &amp;quot;PONG&amp;quot; }&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;A bunch of reverse-engineering by those 
guys yields a pretty good over view of the wire-level chromecast protocol.&lt;br /&gt;
See &lt;a href="https://github.com/thibauts/node-castv2/blob/master/README.md"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So, I implemented a proof of concept in python using Googles protobuf python 
bindings, using TheCrazyTs gist as an outline.  Eventually, I got the data
encoded right, the lengths calculated right and presto!  I can start the
default media receiver and ask it to stream some media, all from native 
python code with no browser in the middle.&lt;/p&gt;
&lt;p&gt;If you have an existing ssld connection to your chromecast, you could do this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;be_size = pack(&amp;quot;&amp;gt;I&amp;quot;,msg.ByteSize())
payload = be_size + msg.SerializeToString()
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;If you had already connected to a chromecast with a 'CONNECT' request, you might
get a PING request from the receiver, which you could send this payload back to.
If you want to see some actual code that can connect, get status, start the default media receiver and ask it to stream a video, see the actual code    &lt;/p&gt;
&lt;p&gt;See &lt;a href="https://github.com/minektur/chromecast-python-poc"&gt;https://github.com/minektur/chromecast-python-poc&lt;/a&gt; for code and 
instructions to try it out yourself.&lt;/p&gt;
&lt;p&gt;Next up:  Write some 'real' networking code and integrate into pychromecast&lt;/p&gt;</summary><category term="web"></category><category term="chrome"></category><category term="chromecast"></category><category term="gadgets"></category></entry><entry><title>Chromecast - Displaying arbitrary URLs using pychromecast</title><link href="http://www.clift.org/fred/chromecast-displaying-arbitrary-urls-using-pychromecast.html" rel="alternate"></link><updated>2014-08-25T15:00:00-06:00</updated><author><name>Fred Clift</name></author><id>tag:www.clift.org,2014-08-25:fred/chromecast-displaying-arbitrary-urls-using-pychromecast.html</id><summary type="html">&lt;p&gt;Continuing on with my Chromecast experiments...  I have been playing with a
python library on github by Paulus Schoutsen called
&lt;a href="https://github.com/balloob/pychromecast"&gt;pychromecast&lt;/a&gt;. At various points in
it's life it has been able to interact with Chromecast devices to do a variety
of things.  With the official SDK release and firmware update from google
earlier this year, much of that broke.  It can however find devices on my local
network, and get device status, and a few other things.&lt;/p&gt;
&lt;p&gt;Referring back to my last post, my ideal app for the chromecast would be a
daemon running on my Mac or FreeBSD box that will watch the device for
inactivity and, if it wasn't doing anything else, would cause photos or home
videos to be streamed to the device.  &lt;/p&gt;
&lt;p&gt;You may recall that I recently was learning about remote chrome browser
debugging. If you register for a Developer Account with google, you can
whitelist your Chromecast and get it put in development mode.  Once in
development mode, the device turns on remote chrome debugging on the standard
port 9222.&lt;/p&gt;
&lt;p&gt;I dug around to find out about the protocol the debugger speaks over that debug
port.  You can read all about the &lt;a href="https://developer.chrome.com/devtools/docs/debugger-protocol"&gt;Chrome Remote Debugging protocol&lt;/a&gt;
but here is a brief over view.  There is a very simple web server running on
that port that gives you a link for each tab showing in Chrome, or in this case
each tab showing on your Chromecast.  If you click that link, it will actually
open the Chrome debugger, connected to a websocket on the device.  You can
enumerate what the websocket endpoints are - &lt;a href="http://cast-IP:9222/json"&gt;http://cast-IP:9222/json&lt;/a&gt; - you
get a small bit of json back listing all the details of your open tabs.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&amp;gt;&amp;gt; curl http://192.168.0.138/json
[ {
  &amp;quot;devtoolsFrontendUrl&amp;quot;: &amp;quot;/devtools/devtools.html?host=192.168.0.138:9222&amp;amp;page=1&amp;quot;,
  &amp;quot;faviconUrl&amp;quot;: &amp;quot;&amp;quot;,
  &amp;quot;thumbnailUrl&amp;quot;: &amp;quot;/thumb/chrome://newtab/&amp;quot;,
  &amp;quot;title&amp;quot;: &amp;quot;New Tab&amp;quot;,
  &amp;quot;url&amp;quot;: &amp;quot;chrome://newtab/&amp;quot;,
  &amp;quot;webSocketDebuggerUrl&amp;quot;: &amp;quot;ws://192.168.0.138:9222/devtools/page/1&amp;quot;
} ]
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;If you make a websocket connection to webSocketDebuggerUrl you can speak the
remote debugging api to your browser, or remote Chromecast-in-developer-mode
device.&lt;/p&gt;
&lt;p&gt;A "quick" persual of the api docs shows a function that I need.  Page.navigate&lt;/p&gt;
&lt;p&gt;If you send&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;{&amp;quot;id&amp;quot;:0, &amp;quot;method&amp;quot;:&amp;quot;Page.navigate&amp;quot;, &amp;quot;params&amp;quot;:{&amp;quot;url&amp;quot;:&amp;quot;http://www.clift.org/fred&amp;quot;}}
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;over the wire, then presto, what you see on your TV is my website.&lt;/p&gt;
&lt;p&gt;You dont have to do all the work yourself.  Paulus has been kind enough to merge
my code into pychromecast.&lt;/p&gt;
&lt;p&gt;See: &lt;a href="https://github.com/balloob/pychromecast/blob/master/pychromecast/remotedebug.py"&gt;https://github.com/balloob/pychromecast/blob/master/pychromecast/remotedebug.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you join the Google developer program for $5, and install pychromecast, you can
do this very simply:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&amp;gt;&amp;gt; devices = pychromecast.get_chromecasts_as_dict()
&amp;gt;&amp;gt; devices[&amp;#39;castdevicename&amp;#39;].crd_open_url(&amp;quot;https://github.com/balloob/pychromecast&amp;quot;)
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This wont work for any old device, and you can't ship apps that work this way,
but for my "ideal" app, It should be pretty simple, with pychromecast.  I've
got a rudimentary proof of concept working now.  &lt;/p&gt;
&lt;p&gt;Note that shipping apps that do this would be against Googles TOS, and they
wouldn't work anyway...&lt;/p&gt;
&lt;p&gt;Have fun with your personal projects.&lt;/p&gt;
&lt;p&gt;P.S. Someone else &lt;a href="https://www.igvita.com/2012/04/09/driving-google-chrome-via-websocket-api/"&gt;did this in ruby&lt;/a&gt; too!&lt;/p&gt;</summary><category term="web"></category><category term="chrome"></category><category term="chromecast"></category><category term="gadgets"></category></entry><entry><title>Chromecast - both cool and frustrating</title><link href="http://www.clift.org/fred/chromecast-both-cool-and-frustrating.html" rel="alternate"></link><updated>2014-08-21T08:00:00-06:00</updated><author><name>Fred Clift</name></author><id>tag:www.clift.org,2014-08-21:fred/chromecast-both-cool-and-frustrating.html</id><summary type="html">&lt;p&gt;I recently purchased a &lt;a href="http://www.google.com/chromecast"&gt;Chromecast&lt;/a&gt; device. 
For the price it's a great media streamer.  You can control it from your Android
or IOS smartphone from many different apps.  For chrome browser, you need to
install a browser plugin.  There are many websites that, when viewed in Chrome 
browser give you the option of &lt;em&gt;casting&lt;/em&gt; the content.  &lt;/p&gt;
&lt;p&gt;By &lt;em&gt;casting&lt;/em&gt; I mean, you tell the Chromecast device to load it's own special
app and have that app directly stream the content.  In Chromecast dev terms
the app you tell the device to load is a 'receiver' app.&lt;/p&gt;
&lt;p&gt;Youtube is a prime example.  On your computer when watching a 
Youtube video, you find an extra button that allows you to send the 
video directly to your Chromecast device.  This allows your Chromecast
to directly stream the content, rather than needing your browser or smartphone
in the loop.&lt;/p&gt;
&lt;p&gt;You can also mirror the contents of any chrome tab and some android-devices to the 
screen.  It takes a lot of bandwidth, but if you have a good network, the quality
is good. (built on WebRTC)&lt;/p&gt;
&lt;p&gt;There is a lot of clever magic going on behind the scenes to make all of this 
work on your home network.  Initial setup for instance, is done by having the
device create it's own wireless network, and having a setup program on your PC 
connect to the network and then to the device to talk to it directly.&lt;/p&gt;
&lt;p&gt;Once it's set up, your devices find Chromecasts in the local network via 
&lt;a href="http://wikipedia.org/wiki/MDNS"&gt;Multicast DNS&lt;/a&gt; (... used to use DIAL/SSDP/UPnP ...)
Some low-level code in the Chromecast API impelemntation, supported, at least in
the Chrome browser takes care of sending IP multicast dns queries asking for 
any devices that will respond to the name _googlecast._tcp.local. All the responses 
are collected and cached.&lt;/p&gt;
&lt;p&gt;Your Chromecast-api using app can then, at the user's
request, contact any of the devices, ask the device to load a receiver app, and 
then establish a control channel to the app to tell it what media to load, to
control playback, etc.&lt;/p&gt;
&lt;p&gt;All the api communication with the Chromecast device is over ssl'd websockets.&lt;/p&gt;
&lt;p&gt;Much more technical detail about how Chromecast devices work can be seen at
&lt;a href="https://github.com/jloutsenhizer/CR-Cast/wiki/Chromecast-Implementation-Documentation-WIP"&gt;Justin Loutsenhizer's excellent Github page&lt;/a&gt;, and at &lt;a href="http://www.slideshare.net/mauimauer/chrome-cast-and-android-tv-add14"&gt;Sebastian Mauer's presentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Google is pretty uptight about the user-experience they will allow with published 
apps.  See their &lt;a href="https://developers.google.com/cast/docs/terms"&gt;Terms of Service&lt;/a&gt;. 
This agreement is what people who want to develop and distribute a Chromecast app
must agree to to get their app usable by the general public. There is also the 
&lt;a href="https://developers.google.com/cast/docs/design_checklist"&gt;Google Cast Design Checklist&lt;/a&gt;
and some &lt;a href="https://developers.google.com/cast/docs/ux_guidelines"&gt;User Interface guidelines&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I have some real problems with the terms of service.  Some of it is understanable.
Section 3.4.7 for instance is about user-data and privacy - common sense. Or, section
3.4.3 - don't encourage users to engage in high-risk activities...  What I don't like
are the terms that forbid exactly what I actually want to do with the device.  3.4.8 - 
must not automatically cast content, 3.4.1 - must not load arbitrary apps - only Google-Approved
ones, 3.4.2 - don't allow usage of the device without the official API...&lt;/p&gt;
&lt;p&gt;I wouldn't care (much) about being required to use only the official API if they would actually
support a platform I cared about developing for.  The three supported APIs are for Android, IOS, 
and chrome-browser apps.  There is no native SDK/API for anything else.   &lt;/p&gt;
&lt;p&gt;What I really want?  An daemon to run on my FreeBSD server in the basement, or on the family
mac-mini that has access to the  60 Gig of family photos and home-videos, and to play 
random ones, if, and only if, the Chromecast device is on the home-screen.  It would poll
periodically to see if the device is busy, and restart showing photos if the device wasn't 
in use.  I want it to start automatically, as a system service, find the device by name, and 
turn my tv into a giant digital photo frame.&lt;/p&gt;
&lt;p&gt;Next post I'll talk about pychromecast, and the code I contributed that will let me make my
dream a reality, while technically not violating Google's TOS.&lt;/p&gt;
&lt;p&gt;refs, for me mostly:  &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&amp;lt;https://github.com/vincentbernat/nodecastor/blob/master/lib/cast_channel.proto&amp;gt;
&amp;lt;http://www.slideshare.net/mauimauer/chrome-cast-and-android-tv-add14&amp;gt;
&amp;lt;https://github.com/wearefractal/nodecast&amp;gt;
&amp;lt;http://dashkiosk.readthedocs.org/en/latest/chromecast.html&amp;gt;
&amp;lt;https://www.npmjs.org/package/nodecastor&amp;gt;
&amp;lt;https://github.com/thibauts/node-castv2&amp;gt;
&lt;/pre&gt;&lt;/div&gt;</summary><category term="web"></category><category term="chrome"></category><category term="chromecast"></category><category term="gadgets"></category></entry><entry><title>Notes on chrome remote debugging</title><link href="http://www.clift.org/fred/notes-on-chrome-remote-debugging.html" rel="alternate"></link><updated>2014-08-07T12:00:00-06:00</updated><author><name>Fred Clift</name></author><id>tag:www.clift.org,2014-08-07:fred/notes-on-chrome-remote-debugging.html</id><summary type="html">&lt;p&gt;These are mostly notes for me, but you might find them useful also.&lt;/p&gt;
&lt;p&gt;On my laptop, I run chrome and usually have many, many tabs open across
a few windows.  Google searching for me usually ends up with me open-in-new-tabbing
the first 5 or 10 links concurrently... It bugs me when I have to close my
browser and loose my set of open tabs, though, in the case of crashes, modern
browsers do a decent job of remembering what I had up.&lt;/p&gt;
&lt;p&gt;Chrome at least does not remember my open tabs if I quit chrome cleanly. Now and
then, I need to use Chrome's Developer Tools.  Sometimes
 I need to run the developer tools remotely. &lt;a href="https://developer.chrome.com/devtools/index"&gt;Chrome Developer Tools&lt;/a&gt;
are pretty sweet - lots of debugging and performance tools for web development.
Generally you run the developer tools in the same instance of chrome as whatever you're
debugging, but you can actually run the UI on another instance of Chrome, either on 
the same machine or remotely.&lt;/p&gt;
&lt;p&gt;Running them remotely is almost indistinguishable from running them within the same
browser instance - cool technology the Chrome guys have written.&lt;/p&gt;
&lt;p&gt;All you need to do is to use the command line option &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;--remote-debugging-port=9222
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;or whatever port you want and chrome will start up listening on that port for connections
from a remote debugger.  To debug, just point your OTHER chrome browser, on the same or other
box at &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;http://&amp;lt;YOUR IP&amp;gt;:9222/
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;and you'll get a list of the open tabs/frames.  Click on one and off you go.&lt;/p&gt;
&lt;p&gt;So, my problem.  I have chrome open with 60 or 70 tabs, across 3 or 4 windows.  I want to 
run chrome with that command line option so I can play around with some command-line tools to 
to remote debugging/control.  I don't want to shut down chrome... &lt;/p&gt;
&lt;p&gt;So, I use another command line option&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;--user-data-dir=/tmp/foo
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;and I can invoke a second instance of chrome, that is completely separate from what I already have running.&lt;/p&gt;
&lt;p&gt;On my macbook:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;/Applications/Google Chrome.app/Contents/MacOS/Google\ Chrome --user-data-dir=/tmp/foo --remote-debugging-port=9222
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Presto - a new chrome instance, using a separate profile, with remote debugging enabled, without having to shut down
my precious tabs.&lt;/p&gt;</summary><category term="web"></category><category term="chrome"></category></entry><entry><title>Why the Nook won my dollars over Kindle</title><link href="http://www.clift.org/fred/why-the-nook-won-my-dollars-over-kindle.html" rel="alternate"></link><updated>2014-07-18T11:10:00-06:00</updated><author><name>Fred Clift</name></author><id>tag:www.clift.org,2014-07-18:fred/why-the-nook-won-my-dollars-over-kindle.html</id><summary type="html">&lt;h2&gt;Summer Vacation Time.&lt;/h2&gt;
&lt;p&gt;So I vacation on the beach every summer.  I read a lot.  This year I decided I would try out an ebook reader.  Because the plan is to be outdoors on the beach, E-Ink readers seem to be the way to go.  There are two big names for new readers Barnes &amp;amp; Nobles Nook (Simple Touch Glowlight) and Amazons Kindle (Paperwhite - with light...).&lt;/p&gt;
&lt;p&gt;This is not going to be one of those long drawn out technical comparisons.  There are plenty of those out there.  For me, it came down to two things - supported formats, and manufacturer business model.&lt;/p&gt;
&lt;p&gt;The kindle supports amazon's proprietary formats AZWx, txt pdf, mobi, prc.  The Nook supports epub and pdf.  Note that both claim to support more formats either through some form of file conversion, which I'm fully capable of doing myself, or only "support" them with air quotes.  For instance the Nook supports several popular image formats, but only for it's screensaver. I own a large number of ebooks in epub format, and the reader software I use on my android phone (FBReader) uses epub as it's primary format also.  I occasionally use the great open-source software &lt;a href="http://calibre-ebook.com/"&gt;Calibre&lt;/a&gt; to convert ebooks from one format to another, even though the the process of doing so is clunky and complicated.&lt;/p&gt;
&lt;p&gt;As far as business models go, with the kindle, unless you pay a non-trivial amount more, you get (not very obtrusive) advertisements.   If I pay for something, I'd rather not become a revenue source and ad target because of it.  Also, the Nook has a much simpler register-without-a-credit-card process, though you can successfully register a kindle without a credit card, irritating but possible.&lt;/p&gt;
&lt;p&gt;Technically, I think I might like the kindle more...  but... Nook is what I spent my money on.&lt;/p&gt;</summary><category term="Nook"></category><category term="gadgets"></category></entry><entry><title>Managing Lots Of Pregenerated HTML And Other Files With Pelican</title><link href="http://www.clift.org/fred/managing-lots-of-pregenerated-html-and-other-files-with-pelican.html" rel="alternate"></link><updated>2014-06-14T23:08:00-06:00</updated><author><name>Fred Clift</name></author><id>tag:www.clift.org,2014-06-14:fred/managing-lots-of-pregenerated-html-and-other-files-with-pelican.html</id><summary type="html">&lt;p&gt;For one of the Pelican-managed websites I maintain, I have a lot of files that I don't really want to manage with Pelican, and in some cases, I can't easily, without lots of gymnastics.&lt;/p&gt;
&lt;p&gt;In this case, I have about 30k html files that are a dump of an old phpNuke web forum.  And, one directory that has a php app installed (Simple Machines Forum).
I also have a a lot of other miscellaneous files that I'd like to go into the DocumentRoot of my web server&lt;/p&gt;
&lt;p&gt;For the HTML, I could write a script to modify each file to include the meta tags that pelican needs, then put all the images into my images directory, and then put all the CSS into my template.  For my archival content, that would be acceptable, but for the SimpleMachines forum, which is maintained by 3rd parties and updated regularly, it isn't practical.  I would have to redo my changes every time they released a new version...&lt;/p&gt;
&lt;p&gt;I could use STATIC_PATHS, with ARTICLE_EXCLUDES and PAGE_EXCLUDES to do what I want, but for some reason pelican still wants to look at each file. Doing this increased my build times 10x or so with my data.  In short, I could use the static_paths to add all my files, and then the excludes settings to tell pelican to not process them.&lt;/p&gt;
&lt;p&gt;Instead I tried the following, and use this now.  In my top-level directory, I made a directory 'output-skel' and then I edited my Makefile.  In the html target, I added this line as one of the commands that is run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;rsync -SHqav output-skel/ &lt;span class="k"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;so my html make block looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;html:
    &lt;span class="k"&gt;$(&lt;/span&gt;PELICAN&lt;span class="k"&gt;)&lt;/span&gt; &lt;span class="k"&gt;$(&lt;/span&gt;INPUTDIR&lt;span class="k"&gt;)&lt;/span&gt; -o &lt;span class="k"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="k"&gt;)&lt;/span&gt; -s &lt;span class="k"&gt;$(&lt;/span&gt;CONFFILE&lt;span class="k"&gt;)&lt;/span&gt; &lt;span class="k"&gt;$(&lt;/span&gt;PELICANOPTS&lt;span class="k"&gt;)&lt;/span&gt;
    rsync -SHqav output-skel/ &lt;span class="k"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This way, I can put ANY file I want to include in my output in it's relative path in output-skel and have it show up where I want it.&lt;/p&gt;
&lt;p&gt;This sure beats having 30k entries in my STATIC_PATHS.  I put .htaccess files, static-html, some 'downloads' files there.&lt;/p&gt;
&lt;p&gt;As an aside, I also add the following lines to my html make target:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;    chown -R root:www &lt;span class="k"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="k"&gt;)&lt;/span&gt;
    find &lt;span class="k"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="k"&gt;)&lt;/span&gt; -type d &lt;span class="p"&gt;|&lt;/span&gt; xargs chmod 750
    find &lt;span class="k"&gt;$(&lt;/span&gt;OUTPUTDIR&lt;span class="k"&gt;)&lt;/span&gt; -type f &lt;span class="p"&gt;|&lt;/span&gt; xargs chmod 640
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Just to keep things neat and tidy - both secure the files and allow the webserver to see them.&lt;/p&gt;</summary><category term="pelican"></category><category term="web"></category></entry><entry><title>Why I wont fix your computer, part 3</title><link href="http://www.clift.org/fred/why-i-wont-fix-your-computer-part-3.html" rel="alternate"></link><updated>2014-06-12T12:00:00-06:00</updated><author><name>Fred Clift</name></author><id>tag:www.clift.org,2014-06-12:fred/why-i-wont-fix-your-computer-part-3.html</id><summary type="html">&lt;h2&gt;Not quite a computer, but...&lt;/h2&gt;
&lt;p&gt;I have an Embryon Pinball machine made by Bally in the early 80s - one of their early solid-state machines.&lt;/p&gt;
&lt;p&gt;See either the &lt;a href="http://www.ipdb.org/machine.cgi?gid=783"&gt;Internet Pinball Database&lt;/a&gt; or &lt;a href="http://pinside.com/pinball/archive/embryon/gallery"&gt;Pinside&lt;/a&gt; for pictures and more info.&lt;/p&gt;
&lt;p&gt;When I bought this machine, I got a great deal on it because the sound was not working and it was missing almost all of the playfield plastics (still looking for an original set if you have a lead...)  AND the playfield is trashed.&lt;/p&gt;
&lt;p&gt;I've repaired the power supply, replaced a bunch of caps on the soundboard, and done a bunch of little repairs and cleaning on switches, pop-bumpers etc.  I dont want to put to much into the machine because I think the quality of the playfiled is so low that it's not worth the effort.&lt;/p&gt;
&lt;p&gt;Now, on to why I wont fix your computer - I mean, pinball machine.  One of the things that WAS in good condition when I bought the machine was the apron.  It has some little flecks of paint or something on it and in my stupidity, I thought that I should try a little water and magic-eraser.    &lt;/p&gt;
&lt;p&gt;&lt;img alt="The result" src="/fred/images/embryon_apron.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;The excellent results speak for themselves.  Oh wait, I mean, what an idiot I am.  At least the kids still have fun playing it.&lt;/p&gt;
&lt;p&gt;This is why I wont fix your pinball machine.&lt;/p&gt;
&lt;p&gt;PS - I have a bunch of pictues, a few of my machine and many others I've collected.  Including 3 sets of plastics scans I've bummed off people (thanks all) and even one relatively low res playfield scan. (maybe a photo?).  See my &lt;a href="/fred/emb/"&gt;embryon archive&lt;/a&gt;.  My current playfield is shown in painful detail in the playfield-crappy directory, and most of the good scans are in source-materials/donors.  Hopefully this helps someone out there.&lt;/p&gt;</summary><category term="wontfix"></category><category term="oldblog"></category><category term="pinball"></category></entry><entry><title>Why I wont fix your computer, part 2</title><link href="http://www.clift.org/fred/why-i-wont-fix-your-computer-part-2.html" rel="alternate"></link><updated>2014-06-11T12:00:00-06:00</updated><author><name>Fred Clift</name></author><id>tag:www.clift.org,2014-06-11:fred/why-i-wont-fix-your-computer-part-2.html</id><summary type="html">&lt;h2&gt;On being stupid with my own hardware&lt;/h2&gt;
&lt;p&gt;I built a &lt;a href="http://mame.net/"&gt;mame&lt;/a&gt; machine into a dead arcade cabinet for the breakroom of a former employer.  Well, it was for me, and for a few others that had regularly been playing an original upright Vs Super Mario Brothers, and then later an original Marble Madness (gimme that double WAND).  &lt;/p&gt;
&lt;p&gt;I was in the middle of a system upgrade (old motherboard died...) and I had a pretty good chance that my very-small-linux-install on the drive would 'just work'&lt;/p&gt;
&lt;p&gt;I had the whole thing apart and was about to connect power to the IDE drive the system ran on...  and while distractedly talking to a co-worker, I attached the new drive, while the other system was still on.  It fried the drive...&lt;/p&gt;
&lt;p&gt;What would probably have been a 5 minute hardware swap turned into a several-hour reinstall/re-setup of my mame front end (&lt;a href="http://advancemame.sourceforge.net/menu-readme.html"&gt;AdvanceMENU&lt;/a&gt;), on a new drive, which I fortunately had laying around.&lt;/p&gt;
&lt;p&gt;What did I learn?  Turn off the power.  Sigh.  this is why I dont want to fix your computer.&lt;/p&gt;</summary><category term="wontfix"></category><category term="oldblog"></category><category term="mame"></category></entry><entry><title>Embedding PHP in Pelican-generated Static pages</title><link href="http://www.clift.org/fred/embedding-php-in-pelican-generated-static-pages.html" rel="alternate"></link><updated>2014-06-10T09:37:00-06:00</updated><author><name>Fred Clift</name></author><id>tag:www.clift.org,2014-06-10:fred/embedding-php-in-pelican-generated-static-pages.html</id><summary type="html">&lt;p&gt;So, I wanted to make a simple website with pelican.  But I 
had a little legacy php code that I still wanted to function.&lt;/p&gt;
&lt;p&gt;It sure would be nice, I thought, if I could take my php
app and with only small tweaks make it work as PART of
pelican staticly generated page.  But pelican doesn't
directly parse php.  I could just staticaly copy my php
pages into my docroot and have some links to them but
then I loose the menu, the CSS etc.&lt;/p&gt;
&lt;p&gt;Long story short, I made a reStructuredText page in content/pages 
that has the following:&lt;/p&gt;
&lt;pre&gt;
Sample Php App
##############

:date: 2014-05-03 10:20
:url: pages/sample.php
:save_as: pages/sample.php


.. raw:: html
    :file: php/sample.php
&lt;/pre&gt;

&lt;p&gt;php/sample.php is relative to the directory that this RST file is in.&lt;br /&gt;
The php page can run standalone and produces the output I want, minus 
the site integration.  PHP is not one of the formats that pelican
knows how to process so it ignores the php code.&lt;/p&gt;
&lt;p&gt;When I generate my html output (e.g. 'make html') pelican generates a 
page http://mysite/pages/sample-php-app.php which has a big html wrapper
around some php code.  Works great and it lets me integrate some
legacy php tools into an othewise static site.&lt;/p&gt;</summary><category term="pelican"></category><category term="web"></category></entry><entry><title>Trying out Pelican static site generator.</title><link href="http://www.clift.org/fred/trying-out-pelican-static-site-generator.html" rel="alternate"></link><updated>2014-06-10T09:36:00-06:00</updated><author><name>Fred Clift</name></author><id>tag:www.clift.org,2014-06-10:fred/trying-out-pelican-static-site-generator.html</id><summary type="html">&lt;h3&gt;TLDR&lt;/h3&gt;
&lt;hr /&gt;
&lt;p&gt;I'm running a couple of websites with pelican static website generator
because it's easy to maintain, and lightweight, and kind of futurer-proof.&lt;/p&gt;
&lt;h3&gt;History&lt;/h3&gt;
&lt;hr /&gt;
&lt;p&gt;So I've tried a bunch of tools over the years to make personal
web pages.  I hand-rolled html (for a class) in the 90s, I used
a variety of templates that were table based, and then later
CSS based, and then a bunch of different blog and micro-blog software.
Lighter weight ones like &lt;a href="http://blosxom.sourceforge.net/"&gt;Blosxom&lt;/a&gt; and 
&lt;a href="http://www.s9y.org/"&gt;Serendipity&lt;/a&gt;, and several heavy-weight contenders like
&lt;a href="http://movabletype.org/"&gt;MovableType&lt;/a&gt; and &lt;a href="http://wordpress.org/"&gt;Wordpress&lt;/a&gt;.  &lt;/p&gt;
&lt;p&gt;I have worked in hosting related fields off and on for 15 years and because
of the nature of the business, my perspectives are a bit skewed.  Density, 
multi-tennancy, and automatic upgrades are important, Working at 
&lt;a href="http://viaverio.com"&gt;NTT/Verio&lt;/a&gt; also kept me focused on localization. All
that is important when you run software for other people on a large scale.  But, I 
don't do that any more.  Setting software up for myself makes me want to find 
lightweight and very easy to maintain systems. Blosxom is great in that it is 
mostly run from the cli - mostly flat files.  It also has a lot of cool plugins.
I used Serendipity with the sqlite backend, which again removed one service 
dependency and upgrade-pain-point. &lt;/p&gt;
&lt;h3&gt;My problem&lt;/h3&gt;
&lt;hr /&gt;
&lt;p&gt;I also recently got the opportunity to migrate an old old phpNuke based 
website (with phpBB forums) which were installed around 2002 and barely upgraded
since.  We had been limping along with all the admin functionality disabled
because of the many many security vulnerabilities.  We had upgraded from 
php3 to php4 on FreeBSD 8 with modest tweaks. The 5 year old hardware 
started to fail, and my 'free' colo with a friend was straining relationships.&lt;/p&gt;
&lt;h3&gt;My Solution&lt;/h3&gt;
&lt;hr /&gt;
&lt;p&gt;So, I set up a kvm instance at &lt;a href="http://www.betterservers.com"&gt;BetterServers&lt;/a&gt; which
is a sister company to my employer &lt;a href="http://www.betterlinux.com"&gt;BetterLinux&lt;/a&gt;, installed 
FreeBSD 10, and started to migrate the Website, the game, and shell accounts.  PHP4 
could be made to work, theoretically, but I didn't really want to have to 
build by hand (no longer supported by the ports system - the last release was
6ish years ago).  Then I started trying to migrate the apps, mostly unchanged 
to work with PHP5.  Ugh.  Somwhere in there I decided that it would be better
to throw out the old app, make an easier to maintain website and migrate a most
of the content.  I looked at several different dynamic and static simple cms
systems that I could use.  I knew I needed forum software and so I tried a 
few of those hoping that they might have themes or plugin systems that would
allow a simple static site along with the forum.  I tried some CMS systems like
 &lt;a href="http://get-simple.info/"&gt;GetSimple&lt;/a&gt;
that I could embed some forum software into...  Eventually... I stumbled 
across &lt;a href="http://docs.getpelican.com/en/3.3.0/"&gt;Pelican&lt;/a&gt; which generates a website
from a template and a variety of different markup-formatted files. (I mostly
use markdown, but several others are supported)  Pelican is in python, (works
in 2.x and 3.x) and is aimed at someone who wants to, and is not afraid
of tinkering.   &lt;/p&gt;
&lt;p&gt;So I set up a new website &lt;a href="http://www.blackmud.com/"&gt;blackmud&lt;/a&gt; and embedded 
&lt;a href="http://www.simplemachines.org/"&gt;SimpleMachines forum&lt;/a&gt; into it and then 
migrated a lot of content in various forms from the old site.  &lt;/p&gt;
&lt;p&gt;Verdict?  I like pelican.  The templates are mostly aimed at people who want
to write a blog.  I was brushing up on my html, php, and markdown and did not
want to have to learn Jinja2 templates also, though they look pretty simple.&lt;/p&gt;
&lt;p&gt;I'd love a multi-level static website menu template.  I kind of faked it
with a heavily modified 'syte' template.  I found a way to embed some 
legacy php utilities into the static page system (inline raw 'html'
file includes in a reStructuredText doc) so that I get my old
php utilties but get to keep the static menu and css around it. 
I'll write up the details sometime. The benefit of this is that I 
end up with mostly static html that should be very portable and
not take much babysitting for years.  Yes, I'll have to stay on 
top of SimpleMachines updates... I swear I'll be vigilant, no really I will! I hope.&lt;/p&gt;
&lt;p&gt;I liked pelican enough that I thought I would replace my 5 year old
one page site, and broken dead blog site with pelican generated stuff.
This is the result&lt;/p&gt;</summary><category term="pelican"></category><category term="web"></category></entry><entry><title>Why I wont fix your computer, part 1</title><link href="http://www.clift.org/fred/why-i-wont-fix-your-computer-part-1.html" rel="alternate"></link><updated>2014-06-10T06:00:00-06:00</updated><author><name>Fred Clift</name></author><id>tag:www.clift.org,2014-06-10:fred/why-i-wont-fix-your-computer-part-1.html</id><summary type="html">&lt;h2&gt;How not to upgrade a server&lt;/h2&gt;
&lt;p&gt;I was working at a unix admin at a private university.  A research lab wanted an OS upgrade on their lab NFS and web server, which was indirectly my responsibility. User data, webserver, web content, all on an external (scsi) drive. I showed up with install media and popped in the disk, booted, installed to scsi ID 6 - the local default for OS on this class of server.  Shortly after first boot I go to mount the filesystems on the external drive, twiddle rc scripts to spin up the web server, mount user directories etc, and there doesn't seem to be anything but the old OS...  &lt;/p&gt;
&lt;p&gt;Immediate panic - apparently both internal OS drive and external data drive are SCSI Id 6, on controllers 0 and 1.  I had just successfully installed hpux 10.10  over the lab's important data.  The backup that the grad students had assured me as safely locked away?  1 month old.  &lt;/p&gt;
&lt;p&gt;So what did I learn?  how to apologize, to double check-backups before upgrades, to double-check target drive for installs, to not offer to help!  &lt;/p&gt;</summary><category term="wontfix"></category><category term="oldblog"></category></entry></feed>