How to Restore a Time Machine Backup From a Synology NAS

My Sunday morning started off like this:

“There’s an error with the EFI system partition’s file system.” Well today’s off to a great start // @siracusa — Tim Broder (@timothybroder) August 3, 2014

My laptop wouldn’t finish booting.

All I got was a backlit, black, screen.  I went through numerous combinations of booting with option, cmd+R, cmd+shift+R, un-mounting, ejecting. In all methods, trying in vain to get the Disk Utility to verify and repair the hard drive. I tried from Recovery Mode and a fresh OSX install on a USB hard drive. The things I saw on my screen included:

  • The aforementioned black screen
  • A backlit, white, screen
  • A gray screen with a flashing Folder with a question mark in it
  • “Live file system repair is not supported
  • There’s an error with the EFI system partition’s file system.”
  • Clean installs were failing: "An Error occurred while preparing the installation.  Try running this application again.”
  • “Verified failure: unrecognized file system”

After a few hours I moved from “I need to fix this” to “I need to get my laptop back up.”

Ok let’s do a clean install: "An Error occurred while preparing the installation.  Try running this application again.”

Crap.

At this point I booked a genius bar appointment for the next day just in case. 3 clean installs failed. Took a break to think about it…. I got back into Disk Utility and formatted the partition. Then deleted/recreated the partitions. Formatted again (why not?). At this point a clean install worked. Ok… Seems like the drive isn’t totally dead. But, I want to do a fresh restore from my Time Machine backup on my NAS. I normally also have a SuperDuper! backup. I didn’t. That’s a story for another time. Apple doesn’t quite support NAS Time Machine; there are a few hoops you have to jump through:

  1. Get into recovery mode (Boot while holding cmd+R)
  2. Open the terminal
  3. Go to Volumes cd /Volumes ls -la
  4. Make a place to mount the NAS mkdir synology
  5. Double check it’s there ls -la
  6. Mount your NAS. This is the IP of it in my house. This should be the IP you use to get to the admin console. My Admin is: http://192.168.1.205:5000 You’ll also need to know what share your Time Machine back is in. For me, it has it’s own drive, represented by “timstimemachine”. I’ll go into the details of my NAS setup in a future post mount -t afp afp://admin:[password]@192.168.1.205/timstimemachine synology
  7. Check you see files in the synology folder cd synology ls -la
  8. Even though we’re mounted, we won’t see this backup in Time Machine Restore just yet. Use hdid (hard drive image driver) to mount the sparse bundle image hdid [your back-up name].sparsebundle
  9. If your drive or the backup was encrypted, You’ll need to enter it’s password at this time
  10. Make sure your backup is still mounted ls -la
  11. Ok, quit the Terminal and load up Time Machine Restore
  12. You should see the backup we just mounted
  13. Start the restore. It can take a while. Mine is around 360 gigs and it estimated 16 hours. It took a lot less than that but it finished when I was sleeping so I don’t have an accurate time.

Sunday was a stressful day. Hopefully this can help you.

Using PonyDebugger on a device

PonyDebugger is awesome. I use it mostly for Core Data debugging. Most of the time, I find it easier then firing up SQLite Professional.

When using the simulator, hitting localhost:9000 is fine. On a device, not so much; you need to hit your machine. xip.ioto the rescue! What it is: xip.io is a magic domain name that provides wildcard DNS for any IP address. We use this heavily at work if the machine we’re on isn’t hooked up to a subdomain or [Vagrant Share](https://www.vagrantup.com/blog/feature- preview-vagrant-1-5-share.html).

We can use this wildcard to have our iPhone hit our laptop’s instance of PonyDebugger.

First, get your IP. I use this Alfred workflow. Take note of your local IP.

Start PonyDebugger listening on that IP:

bashponyd serve —listen-interface=192.168.1.10

For handy access, alias this command:

bashalias pony="ponyd serve --listen-interface=192.168.1.10"

Load Pony in your browser by appending your IP to the a xip.io URL: http://192.168.1.10.xip.io:9000/

To access via the simulator or a device:

swiftlet pony = PDDebugger.defaultInstance() pony.connectToURL(NSURL.URLWithString("ws://192.168.1.10.xip.io:9000/device")) pony.enableNetworkTrafficDebugging() pony.enableCoreDataDebugging()

Want to trace the call stack in Magento?

Update: This code is also available on Github as a Mageno module

This has helped me immensely in situations like "Where is this getting called from??!?"

Create a helper like so:

class Timbroder_Stack_Helper_Callstack extends Mage_Core_Helper_Abstract
{
    private function get_callstack($delim="\n") {
      $dt = debug_backtrace();
      $cs = '';
      foreach ($dt as $t) {
        $cs .= $t['file'] . ' line ' . $t['line'] . ' calls ' . $t['function'] . "()" . $delim;
      }

      return $cs;
    }

    public function toLog() {
        Mage::log($this->get_callstack());
    }

    public function toFirePhp() {
        $stack = $this->get_callstack();
        foreach (explode("\n", $stack) as $line) {
            Mage::helper('firephp')->send($line);
        }
    }
}

That can be called from anywhere:

``PHP Mage::helper('stack/callstack')->toFirePhp(); Mage::helper('stack/callstack')->toLog();


I've also wrapped this into a module that you can drop right into your project. Details here: [https://bitbucket.org/broderboy/magento_callstack/src](https://bitbucket.org/broderboy/magento_callstack/src "https://bitbucket.org/broderboy/magento_callstack/src") Example output:

.../app/code/community/Timbroder/Stack/Helper/Callstack.php line 16 calls get_callstack() .../app/design/frontend/mongoose/default/template/catalog/cms/bikes_bmx.phtml line 12 calls toLog() .../app/design/frontend/mongoose/default/template/catalog/cms/bikes.phtml line 21 calls require_once() .../app/code/core/Mage/Core/Block/Template.php line 212 calls include() .../app/code/core/Mage/Core/Block/Template.php line 239 calls fetchView() .../app/code/core/Mage/Core/Block/Template.php line 253 calls renderView() .../app/code/core/Mage/Core/Block/Abstract.php line 668 calls _toHtml() .../app/code/core/Mage/Core/Model/Email/Template/Filter.php line 190 calls toHtml() .../lib/Varien/Filter/Template.php line 134 calls call_user_func() .../app/code/core/Mage/Core/Model/Email/Template/Filter.php line 501 calls filter() .../app/code/core/Mage/Cms/Block/Page.php line 100 calls filter() .../app/code/core/Mage/Core/Block/Abstract.php line 668 calls _toHtml() .../app/code/core/Mage/Core/Block/Abstract.php line 513 calls toHtml() .../app/code/core/Mage/Core/Block/Abstract.php line 460 calls _getChildHtml() .../app/code/local/Mage/Page/Block/Html/Wrapper.php line 52 calls getChildHtml() .../app/code/core/Mage/Core/Block/Abstract.php line 668 calls _toHtml() .../app/code/core/Mage/Core/Block/Text/List.php line 43 calls toHtml() .../app/code/core/Mage/Core/Block/Abstract.php line 668 calls _toHtml() .../app/code/core/Mage/Core/Block/Abstract.php line 513 calls toHtml() .../app/code/core/Mage/Core/Block/Abstract.php line 464 calls _getChildHtml() .../app/design/frontend/mongoose/default/template/page/1column.phtml line 55 calls getChildHtml() .../app/code/core/Mage/Core/Block/Template.php line 212 calls include() .../app/code/core/Mage/Core/Block/Template.php line 239 calls fetchView() .../app/code/core/Mage/Core/Block/Template.php line 253 calls renderView() .../app/code/core/Mage/Core/Block/Abstract.php line 668 calls _toHtml() .../app/code/core/Mage/Core/Model/Layout.php line 529 calls toHtml() .../app/code/local/Mage/Core/Controller/Varien/Action.php line 389 calls getOutput() .../app/code/core/Mage/Cms/Helper/Page.php line 130 calls renderLayout() .../app/code/core/Mage/Cms/Helper/Page.php line 52 calls _renderPage() .../app/code/core/Mage/Cms/controllers/PageController.php line 45 calls renderPage() .../app/code/local/Mage/Core/Controller/Varien/Action.php line 418 calls viewAction() .../app/code/core/Mage/Core/Controller/Varien/Router/Standard.php line 254 calls dispatch() .../app/code/core/Mage/Core/Controller/Varien/Front.php line 177 calls match() .../app/code/core/Mage/Core/Model/App.php line 304 calls dispatch() .../app/Mage.php line 598 calls run() .../index.php line 155 calls run() ```

Thanks to nextide for some of the code

Displaying Custom Attributes on the Product Page in Magento

At some point you may not want to use the canned attributes.phtml groupings that magento provides, or you just want to cherry pick which attributes to show on your product listing page template/catalog/product/view.phtml

$_product = $this->getProduct();

//For the attribute bike_specs_rear_shock

$_product->getResource()->getAttribute('bike_specs_rear_shock')->getStoreLabel(); //label
$_product->getbike_specs_rear_shock(); //value

select foo, count(*) from bar group by foo in django

Every once in a while you need some old fashion SQL style queries in django. This is a common one for reporting and aggregation.  Its fairly easy to replicate in a queryset.  Say I wanted to get the authors and the number of articles they have written going back to the beginning of 2009 to the present:

from django.db.models import Count
Article.objects
    .filter(created_date__gte=datetime.datetime(2009,1,1))
    .values('author')
    .annotate(Count('author'))

The result:

[{'author__count': 1028, 'author': 17L}, {'author__count': 9, 'author': 9L}, {'author__count': 39, 'author': 12L}, {'author__count': 581, 'author': 10L}, {'author__count': 15, 'author': 7L}, {'author__count': 366, 'author': 13L}, {'author__count': 233, 'author': 5L}, {'author__count': 167, 'author': 15L}, {'author__count': 287, 'author': 14L}, {'author__count': 10, 'author': 6L}, {'author__count': 2101, 'author': 16L}]

Wordpress Plugin: Displaying your Google Reader RSS subscriptions

I've been meaning to write this code for a while, and I really wanted to take a stab at writing a wordpress plugin so here it goes.

The following takes in Google user credentials, and allows the user to display what RSS feeds they subscribe to on their wordpress blog

Example: The RSS that I read
Update: This plugin is now hosted by wordpress. click here

/*
Plugin Name: Google Reader Subscription List
Version: 1
Author: Timothy Broder
Description: Lists a users subscribed Google Reader feeds
*/

/*  Copyright 2009  Timothy Broder (email : timothy.broder@gmail.com)

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/


if (!class_exists('GoogleReaderSubList')) {
class GoogleReaderSubList {

  var $show_list       = 'show-google-reader-sub-list';  //the hook in a page
  var $login          = '';
  var $pass          = '';
  var $source         = 'wordpress-google-reader-sub-list-';  //the source the api sees when logging into Google
  var $service         = 'reader';  
  var $login_url        = 'https://www.google.com/accounts/ServiceLoginAuth?service=mail'; //URL to login to google
  var $subscription_list_url  = 'http://www.google.com/reader/api/0/subscription/list'; //URL that holds a users subscriptions

  function GoogleReaderSubList() {
   $options    = $this->get_admin_options();
   $this->login  = $options['google_login'];
   $this->pass  = $options['google_pass'];

   $this->source = $this->source . $this->login;
  }

  function show_sub_list() {
   $stop = false;
   if ($this->login == '' || $this->login == null) {
    echo 'Google login not set<br />';
    $stop = true;
   }
   if ($this->pass == '' || $this->pass == null) {
    echo 'Google password not set<br />';
    $stop = true;
   }

   //check to see if the zend plugin has been installed and activated
   //http://wordpress.org/extend/plugins/zend-framework/
   if (!(defined('WP_ZEND_FRAMEWORK') && WP_ZEND_FRAMEWORK)) {
    echo 'The <a href="http://wordpress.org/extend/plugins/zend-framework/" target="_blank">Zend Framework Plugin</a> is not active.  Please install and activate it.';
    $stop = true;
   }
   if ($stop) {
    return;
   }

   $client = new Zend_Http_Client($this->login_url);

   //connect, authenticate, and handshake with Google
   $client->setCookieJar()
    ->setMethod(Zend_Http_Client::POST)
    ->setParameterPost(array(
     'continue'             => $this->subscription_list_url,
     'service'              => 'reader',
     'niu'                  => 1,
     'hl'                   => 'en',
     'Email'              => $this->login,
     'Passwd'               => $this->pass,
     'PersistentCookie'     => 'yes',
     'asts'                 => ''
    ));


   //$error_level = error_reporting();
   //error_reporting(1);
   $response = $client->request('POST');
   $client->setUri($this->subscription_list_url)->setMethod(Zend_Http_Client::GET);
   $response = $client->request()->getBody();

   if ($client->request()->getStatus() == 400) {
    ?>Unable to login with supplied Google login/password< ?
    return;
   }

   //error_reporting($error_level);

   //got the feed, parse it
   $feed = simplexml_load_string($response);

   $hashmap = array();

   //organize the feeds by tag  
   foreach ($feed->list->object as $e) {
    $url = $e->string[0];
    $title = $e->string[1];
    $cat = $e->list->object->string[1];

    //make sure a feed is filed somewhere
    if ($cat == '') {
     $cat = 'unfiled';
    }
    $t = $hashmap["$cat"];

    //a category hasn't been used before
    if ($t == null) {
     $t = array($e);
     $hashmap["$cat"] = $t;
    }
    //category has been used before
    else {
     array_push($t, $e);
     $hashmap["$cat"] = $t;
    }
   }

   //sort the categories
   ksort($hashmap);

   //output
   ?>
   <p>Tags:
    < ?
    $endKey = end(array_keys($hashmap));
    foreach ($hashmap as $cat=>$t) {
     echo "<a href='#$cat'>$cat</a>";
     if ($cat != $endKey) {
      echo ', ';
     }
    }
    ?>
   </p>< ?

   foreach ($hashmap as $cat=>$t) {
    echo "<a name='$cat'></a>";
    echo "<b>$cat</b><br />";    
    foreach ($t as $e) {
     list($feed, $url) = split('feed/', $e->string[0]);
     $title = $e->string[1];

     echo "<a href='$url' target='_blank'>$title</a><br />";

    }
    echo '<br />';
   }
  }

  function addContent($content) {
   // Only do this if this is a page and it has the appropriate custom field
   if (is_page()) {
    $cust_field_values = get_post_custom_values($this->show_list);
    if ($cust_field_values != NULL) {
     if (defined('WP_ZEND_FRAMEWORK') && WP_ZEND_FRAMEWORK) {
      require_once 'Zend/Loader.php';
      Zend_Loader::loadClass('Zend_Http_Client');
     }
     $content = $this->show_sub_list();
    }
   }
   return $content;
  }

  function init() {
   $this->get_admin_options();
  }  

  function get_admin_options() {
   $admin_options = array('google_login' => '',
    'google_pass' => '',
    'use_accordion' => 'true');
   $options = get_option($this->adminOptionName);
   if (!empty($options)) {
    foreach ($options as $key => $option) {
     $admin_options[$key] = $option;
    }
   }
   update_option($this->admin_optionsName, $admin_options);
   return $admin_options;
  }

  function printAdminPage() {
   $options = $this->get_admin_options();

   if (isset($_POST['update_greader_sub_list_settings'])) {
    if (isset($_POST['greader_sub_list_login'])) {
     $options['google_login'] = $_POST['greader_sub_list_login'];
    }
    if (isset($_POST['greader_sub_list_pass'])) {
     $options['google_pass'] = $_POST['greader_sub_list_pass'];
    }

    update_option($this->admin_optionsName, $options);
    echo '<div class="updated"><p><strong>' .  _e('Settings Updated.', 'GoogleReaderSubList'). '</strong></p></div>';

   }
   //$submit = _e('Update Settings', 'GoogleReaderSubList');

   echo "<div class='wrap'>    <form method='post' action='" . $_SERVER['REQUEST_URI'] . "'>     <h2>Google Reader Subscription List</h2>     <h3>Google Login</h3>     <input type='text' name='greader_sub_list_login' value='";
   echo _e(apply_filters('format_to_edit', $options['google_login']), 'GoogleReaderSubList');
   echo "' />
     <h3>Google Password</h3>     <input type='password' name='greader_sub_list_pass' value='";
   echo _e(apply_filters('format_to_edit', $options['google_pass']), 'GoogleReaderSubList');
   echo "' />
     <div class='submit'>      <input type='submit' name='update_greader_sub_list_settings' value='";
   echo _e('Update Settings', 'GoogleReaderSubList');
   echo "'/>
     </div>    </form>   </div>";
  }
}
}

if (class_exists('GoogleReaderSubList')) {
$greader_sub_list = new GoogleReaderSubList();
}

if (!function_exists('greader_sub_list_ap')) {
function greader_sub_list_ap() {
  global $greader_sub_list;
  if (!isset($greader_sub_list)) {
   return;
  }
  if (function_exists('add_options_page')) {
   add_options_page('gReader Subscriptions', 'gReader Subscriptions', 9, basename(__FILE__), array(&$greader_sub_list, 'printAdminPage'));
  }
}
}

if (isset($greader_sub_list)) {
add_action('admin_menu', 'greader_sub_list_ap');
add_action('activate_google-raeder-list/google-reader-list.php', array(&$greader_sub_list, 'init'));
add_filter('the_content', array(&$greader_sub_list, 'addContent'), '7');
}

Quick Google Authentication in PHP

Here is a quick way to authenticate against Google and retrieve a protected feed. It does not use the supported ClientLogin method but it does allow you to get to some unsupported feeds (Reader, Bookmarks, etc) The Zend Gdata library is required

$show_list       = 'show-google-reader-sub-list';  //the hook in a page
$login          = '';
$pass          = '';
$source         = 'wordpress-google-reader-sub-list-';  //the source the api sees when logging into Google
$service         = 'reader';  
$login_url        = 'https://www.google.com/accounts/ServiceLoginAuth?service=mail'; //URL to login to google
$subscription_list_url  = 'http://www.google.com/reader/api/0/subscription/list'; //URL that holds a users subscriptions


$client = new Zend_Http_Client($login_url);

//connect, authenticate, and handshake with Google
$client->setCookieJar()
->setMethod(Zend_Http_Client::POST)
->setParameterPost(array(
 'continue'             => $subscription_list_url,
 'service'              => 'reader',
 'niu'                  => 1,
 'hl'                   => 'en',
 'Email'              => $login,
 'Passwd'               => $pass,
 'PersistentCookie'     => 'yes',
 'asts'                 => ''
));


$response = $client->request('POST');
$client->setUri($subscription_list_url)->setMethod(Zend_Http_Client::GET);
$response = $client->request()->getBody();

if ($client->request()->getStatus() == 400) {
?>Unable to login with supplied Google login/password```

How to manage podcasts in Winamp (screw itunes)

So I really have been digging the Windows 7 beta. However, Itunes does not sync podcasts correctly on the 64 bit version. IF syncing works at all, it takes a while. I had used winamp to listen to my music a while ago, but had switched to amarok when I started single booting linux (yes, I hated Vista that much). Now that I'm back to using windows a bit, I wanted my podcast experience to go flawlessly.

As much as I dislike iTunes, they have got podcast management down pat

  • search for podcast
  • subscribe to podcast
  • download episodes
  • sync with ipod
  • after an episode has been listened to, delete from computer hard drive That last point is the most important part, everything else can be done manually

All this can be done with winamp. You will need two things, winamp and the ml_iPod plugin. While winamp does come with ipod support built in, ml_iPod has many more features. The following steps should get you up to speed on podcasting with winamp

  • install winamp
  • install the ml_iPod plugin (it will tell you it has to remove the built in ipod plugin, this is ok)
  • set a directory to save the episodes in

  • plug in your ipod
  • enable podcast support for the ipod
  • point it to your episode folder
  • set the query for when to delete old episodes

  • Add your podcasts by searching for them in the directory, or adding them manually using their RSS feed

After this you should be good to go. podcasts!

Installing Ubuntu Netbook Remix (with Jaunty) on an MSI Wind

I recently picked up a MSI Wind Netbook and love the damn thing. Ubuntu Netbook Remix brings in a great UI which makes navigating on the small screen much easier.I used Ubuntu Jaunty Jackalope Alpha 4 as the base install, mainly because I didn't want to go through the trouble of converting my ext3 partitions to ext4 when it comes out on April 23rd, and the driver support is more complete. I haven't had many problems with it aside from a few random firefox crashes.

  1. We're going to make a bootable USB stick to install Ubuntu

  2. On a separate machine, Download the cd image. If you want to use 8.10, get the iso here

  3. if you are already using an Ubuntu install of 8.10 or higher, skip to step 11

  4. burn the image to a cd

  5. Boot to the cd, do not install, load the demo OS

  6. click System->Administration->Create a USB startup disk
  7. point it to either the cd in your drive, or the iso
  8. point to the correct USB stick
  9. the rest of the settings can stay default
  10. click Make Startup Disk
  11. Insert the usb drive into your wind, power it on, and hit delete to go into the bios, change the first boot device to USB Drive
  12. save and exit the bios
  13. If the wind boots off of the USB stick correctly, you should see the same screen as when you had booted off the cd
  14. Install Ubuntu
  15. I made my partitions as follows:
    |30 gig recovery partition|20 gig XP partition|15 Gig Ext4 Ubuntu Partition|4 gig swap partition|the rest of the drive as an ext4 partition
    ![](http://3.bp.blogspot.com/_Ng3QbVQfLZ8/SaYPBBQ2EmI/AAAAA AAAa74/DBWWqZQOEko/s1600-h/partitions.png)

  16. That last partition is where I will mount my home directory, as well as mount from windows xp using ext2fs (I havn't actually tried this yet)

  17. Add the netbook remix repositories to your system. This can be be done in synaptic or by typing the following into a terminal
    sudo gedit /etc/apt/sources.list

  18. add the following:
    deb http://ppa.launchpad.net/netbook-remix-team/ubuntu intrepid main
    deb-src http://ppa.launchpad.net/netbook-remix-team/ubuntu intrepid main

  19. sudo apt-get update

  20. sudo apt-get install go-home-applet
    sudo apt-get install window-picker-applet
    sudo apt-get install maximus
    sudo apt-get install human-netbook-theme

  21. Select the "Human Netbook Theme" in System Preferences>Apperance

  22. go into System Preferences-> sessions->startup programs and confirm that "Maximus" and "window-picker-applet" are thre

  23. Disable Compiz Effects System Preferences->Appearance->None

    This is what is required to get netbook remix running, I continued with the following to tweak it some more

  24. Delete the bottom panel by right clicking on it

  25. Delete all the applets on the top panel by right clicking on them

  26. Add applets to the top panel so it ends up like:
    Window Picker Applet | Trash Can |Notification Area | MixerApplet | Clock

  27. I also made alt+q the hotkey to show the desktop, makes navigating to it faster. Another option is the show desktop applet button that can be added to the top bar. Preferences->keyboard shortcuts->"Hide all normal windows....."

More info is available here.