How to really change your default web browser on OSX

Warning – this solution requires you understand how to build software from source code

When you install an alternate web browser that you wish to use as your default, they’ll generally ask if you want to make the browser your ‘default’ web browser. This is fine and dandy, but Apple doesn’t exactly hand over the reigns at that point. Case in point, if you’ve set your default browser to ‘Chrome’, as I have – if you use the /usr/bin/open command to open an HTML file, it’ll still load up in Safari. I wondered how I could figure out where that was getting hooked, and ultimately found the answer in the open source package DUTI.

DUTI provides a CLI interface for querying and modifying the default application registry using Apple’s Uniform Type Identifiers (UTI). As an example, Safari’s UTI is com.apple.Safari whereas my build of Chromium reports itself as org.chromium.Chromium.

DUTI shows in it’s examples how to set the default handler for HTML documents:

duti -s com.apple.Safari public.html all

So, to make sure that /usr/bin/open directs things correctly – I simply had to run:

duti -s org.chromium.Chromium public.html all

And open started working correctly.

You can also get information about registered file extensions using:

duti -x

Example:


% duti -x jpg
Preview
/Applications/Preview.app
com.apple.Preview

Cute path.py trick

I recently started using path.py in lieu of os.walk() and came up with a quick little trick for sorting directories by time that takes advantage of the fact that you can directly stat returned path objects.


p = path('/some/path')
newest_directory = sorted(p.dirs(), key=lambda x: x.stat().st_ctime)[::-1]][0]

A tribute to SSH host key artwork

If you use SSH, you’ve probably seen some SSH host key artwork – but usually only at key generation time. I don’t know why, but most distros (including OpenBSD) disable the display of the artwork by default.

The idea is described in the OpenSSH 5.1 release notes as follows:

New features:
 
 * Introduce experimental SSH Fingerprint ASCII Visualisation to ssh(1)
   and ssh-keygen(1). Visual fingerprinnt display is controlled by a new
   ssh_config(5) option "VisualHostKey". The intent is to render
   SSH host keys in a visual form that is amenable to easy recall and
   rejection of changed host keys. This technique inspired by the
   graphical hash visualisation schemes known as "random art[*]", and
   by Dan Kaminsky's musings at 23C3 in Berlin.
 
   Fingerprint visualisation in is currently disabled by default, as the
   algorithm used to generate the random art is still subject to change.
 
   [*] "Hash Visualization: a New Technique to improve Real-World
       Security", Perrig A. and Song D., 1999, International Workshop on
       Cryptographic Techniques and E-Commerce (CrypTEC '99)
   http://sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf

You can manually peek at the artwork by using ssh-keygen

nar@bloop ~]:  <strong>ssh-keygen -lv -f ~/.ssh/id_github.pub</strong>
2048 76:b1:95:80:f3:79:1a:9d:c2:6b:5b:69:dc:6a:0e:dd /Users/nar/.ssh/id_github.pub (RSA)
+--[ RSA 2048]----+
|         ..      |
|        o  . .   |
|         +.oo.   |
|          *++    |
|        S oB o   |
|       . .+.=..  |
|         ..+..E  |
|          ..o    |
|           o.    |
+-----------------+

I think the idea is great, even if it’s not utilized as much as it should be.

To take the idea one step further, I put together a Python implementation of
the fingerprint art algorithm and modified it generate 240 color fingerprints
using ANSI.

Here’s an example of the output using random 4k blobs of data:

randart.py output

And finally the source which I shamelessly “ported” from OpenSSH’s C code, most likely
introducing bugs that render it useless. Don’t use this in anything you care about unless
you can prove to yourself it works right.

#!/usr/bin/python
 
import hashlib
import random
 
class AnsiUtil(object):
 
    NUM_BASIC_ANSI_COLORS = 16
 
    @staticmethod
    def calc_rgb(val):
        r = ( 0xf00 & val ) >> 8
        g = ( 0x0f0 & val ) >> 4
        b = ( 0x00f & val )
        return AnsiUtil.NUM_BASIC_ANSI_COLORS+(r*36)+(g*6)+b
 
    @staticmethod
    def bg(value):
        return ( "\x1b[48;5;%dm" % AnsiUtil.calc_rgb(value))
 
    @staticmethod
    def rst():
        return ( "\x1b[0m" )
 
    @staticmethod
    def write(value):
        return (value)
 
def randart(data=''):
 
	m = hashlib.md5()
	m.update(data)
	hex_digest=m.digest()
 
	BASEFLD = 8
	mtx_x = BASEFLD + 1
	mtx_y = BASEFLD + 2 + 1
	augstr = "123456789abcdef"
	alen = len(augstr)-1
	field = [[0 for x in range( mtx_x+3 )] for y in range( mtx_y+2)]
	x = mtx_x / 2
	y = mtx_y / 2
 
	for i in range(16):
		input = ord(hex_digest[i])
		for b in range(0,4):
			x += ( input & 0x1 ) or -1
			y += ( input & 0x2 ) or -1
			x = max( x, 0)
			y = max( y, 0)
			x = min( x, mtx_x - 1)
			y = min( y, mtx_y - 1)
			if field[x][y] < = 16:
				field[x][y] += 1
			input >>= 2
	field[x][y] = len(augstr)
 
	for y in range(mtx_y):
		print '  ', # Just add a little padding on the left..
		for x in range(mtx_x):
			c = min(field[x][y],alen)
			bg=''
			if c == 0:
				bg = AnsiUtil.rst()
			else:
				bg = AnsiUtil.bg(ord(hex_digest[c]))
			print bg,' ',
		print AnsiUtil.rst()
 
randart(''.join([chr(random.randint(1,16)) for x in range(0,4096)]))

A quick and dirty dynamic lsof wrapper for Python

I was thinking today that lsof could use a wrapper library. I started looking into it and while there is actually a static liblsof.a that is produced – the more I thought about it, depending on features available and record columns – it would probably be the path of least resistance to just write something that wraps calling the binary.

lsof provides a couple useful flags for this type of operation:

lsof -F?

Will list the available columns that you can see when you run lsof on your system.

lsof -F0

Will then output your lsof commands with null separated records, one per line.

To really be pro, I like to include:

  • -P, disables /etc/services lookups so you just see the numeric ports
  • -n, disables hostname resolution (ala tcpdump), which makes the resource crawls much faster.

The end result is an untested, possibly buggy, but fairly robust and compact Python class:

#!/usr/bin/python
 
from subprocess import Popen, PIPE
import re
 
class lsof():
	"""
	lsof wrapper class
 
	An automated wrapper around lsof output that yields
	an array of processes with their file handle information
	in the form of:
 
	results = [
        {
            info: {
                'none': '',
		'user_id': '501',
		'process_id': '22665',
		'process_group_id': '20',
		'parent_pid': '232',
		'login_name': 'nar',
                'command_name': 'mdworker'
	    },
        files: [ 
			...  
		]    
	},
 
	...
 
	"""
 
    LSOF_BIN='/usr/sbin/lsof'
    flagRegex = re.compile("\s+(?P&lt;flag&gt;[a-zA-Z])\s+(?P&lt;desc&gt;.*)$")
    flag_cache = {'': 'none'}
 
    def __init__(self):
	"""
	For initialization, we query lsof using the '-Fh?' flag
	to automatically generate a list of possible key names
	so that later on when we're parsing records we can substitute
	them in the final dict. 
 
	When using the -F option, the key names are only provided as
	single characters, which aren't terribly useful if you don't
	know them by heart.
	"""
 
        cmd = "%s -F?" % (self.LSOF_BIN)
        p = Popen( [ self.LSOF_BIN, "-F?" ] , stdout=PIPE, stderr=PIPE, close_fds=True )
        for line in p.stderr.readlines()[1:]:
            m = self.flagRegex.match(line)
            if m is not None:
                flag = m.group('flag')
                desc = m.group('desc').lower()
                for s in (':', ' as', ' ('):
                    if desc.find(s) > -1:
                        desc = desc[0:desc.find(s)]
                        break
                self.flag_cache[m.group('flag')] = re.sub('[/ ,\t_]+', '_', desc)
 
    def run(self):
	"""
	run lsof and parse the results into a tmp array that gets passed back to the
	caller. 
	"""
 
        tmp = []
        cmd = "%s -PnF0" % (self.LSOF_BIN)
        p = Popen(cmd.split(' '), stdout=PIPE, close_fds=True)
        for line in p.stdout.xreadlines():
            r = dict( [ ( self.flag_cache[x[0:1]], x[1:] ) for x in line.rstrip('\n').split('\0') ] )
            if r.has_key('command_name'):
                tmp.append( { 'info': r, 'files': [] } )
            else:
                tmp[-1]['files'].append(r)
        return tmp
 
if __name__ == '__main__':
    import pprint
    l = lsof()
    for r in l.run():
        info = r['info']
        files = r['files'] 
 
        print "PID(%s) %s" % (info['process_id'], info['command_name'])
        for f in files:
            for k in f.keys():
                print "\t%-20s -> %s" % ( k, f[k] )

Dynamically modifying Terminal.app preferences (font, window size, colors)

Cobbled together this snippet after getting the idea for dynamically changing the color-scheme of a Terminal window based on the destination of an SSH connection.

#!/usr/bin/env osascript
 
on run argv
    set NewSetting to "" & item 1 of argv
    tell application "Terminal"
        set current settings of tab of first window to settings set NewSetting
    end tell
end run

You can accomplish this similarly with the following open command, but it will open a new window rather than just modifying the active frame in the active window.

open someSettings.settings -a /Applications/Utilities/Terminal.app

OSX Lion, Python 2.7, Growl 1.3 and getting click notification callbacks working with pyobjc

pyGrr!!

No no, I’m not angry. It’s the name of this script. It’ll help you use growl to send Grrs.

I ran across this stackoverflow post querying about how you could get python to work with Growl so that you’d get callbacks whenever a notification is clicked. I dug around a bit and found a post on the cocoaforge forums where user “tooru” had an example that at one time worked, had suffered a little bit rot.

I gave it a once over, got it working under Lion, and it’s hot off the presses for all your grr! needs.

Incidentally, even though Growl costs $3 in the AppStore (boo!), you can download the source (yay!) and build yourself if you’re feeling adventurous.

-nar

#!/usr/bin/env python 
#
# pyGrr!
#
# This code was originally found @
# http://www.cocoaforge.com/viewtopic.php?f=6&amp;t=13359&amp;p=91992&amp;hilit=pyobjc+growl
# It is (to the best of our knowledge) the work of user 'tooru' on the same
# website.
#
# I make no claim to this code, all I did was get it working with PyObjC on Lion
# reformatted it a bit and added some more verbose explanations of what the script
# does. To be honest, I haven't touched pyobjc in a couple years and I was amazed
# that it still works! Even more amazed that I was able to get this example working
# in about 20 minutes.
#
# Great job tooru! 
# 
#   I have verified this code works with the following combination of 
#   packages / versions
#
#   * OSX Lion 10.7.3
#   * Python 2.7
#   * Growl 1.3
#   * Growl SDK 1.3.1
#
# 
# - Nathan Ramella nar@hush.com (http://www.remix.net)
##################################################################################
 
import objc
from Foundation import *
from AppKit import *
from PyObjCTools import AppHelper
import time
import sys
import os
 
myGrowlBundle = objc.loadBundle(
  "GrowlApplicationBridge",
  globals(),
  bundle_path = objc.pathForFramework(
    '/Library/Frameworks/Growl.framework'
  )
)
 
class MenuMakerDelegate(NSObject):
 
  """
  This is a delegate for Growl, a required element of using the Growl
  service.
 
  There isn't a requirement that delegates actually 'do' anything, but
  in this case, it does. We'll make a little menu up on the status bar
  which will be named 'pyGrr!'
 
  Inside the menu will be two options, 'Send a Grr!', and 'Quit'. 
 
  Send a Grr! will emit a growl notification that when clicked calls back
  to the Python code so you can take some sort of action - if you're that
  type of person.
  """
 
  statusbar = None
  state = 'idle'
 
  def applicationDidFinishLaunching_(self, notification):
 
    """
    Setup the menu and our menu items. Getting excited yet?
    """
 
    statusbar = NSStatusBar.systemStatusBar()
    # Create the statusbar item
    self.statusitem = statusbar.statusItemWithLength_(NSVariableStatusItemLength)
 
    self.statusitem.setHighlightMode_(1)  # Let it highlight upon clicking
    self.statusitem.setToolTip_('pyGrr!')   # Set a tooltip
    self.statusitem.setTitle_('pyGrr!')   # Set an initial title
 
    # Build a very simple menu
    self.menu = NSMenu.alloc().init()
    menuitem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
      'Send a Grr!',
      'rcNotification:',
      ''
    )
    self.menu.addItem_(menuitem)
 
    # Default event
    menuitem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
      'Quit',
      'terminate:',
      ''
    )
    self.menu.addItem_(menuitem)
 
    # Bind it to the status item
    self.statusitem.setMenu_(self.menu)
 
  def rcNotification_(self,notification):
 
    """
    This is run when you select the 'Send a Grr!' menu item. It 
    will lovingly bundle up a Grr and send it Growl's way.
    """
 
    print "Sending a growl notification at", time.time()
 
    GrowlApplicationBridge.notifyWithTitle_description_notificationName_iconData_priority_isSticky_clickContext_(
      "Grr! - I'm a title!",
      "This is where you put notification information.",
      "test1",
      None,
      0,
      False,
      "this ends up being the argument to your context callback"
    )
 
class rcGrowl(NSObject):
 
  """
  rcGrowl registers us with Growl to send out Grrs on behalf
  of the user and do 'something' with the results when a 
  Grr has been clicked.
 
  For additional information on what the what is going on
  please refer to the growl dox @ 
 
  http://growl.info/documentation/developer/implementing-growl.php
  """
 
  def rcSetDelegate(self):
    GrowlApplicationBridge.setGrowlDelegate_(self)
 
  def registrationDictionaryForGrowl(self):
 
    """
    http://growl.info/documentation/developer/implementing-growl.php#registration
    """
 
    return {
      u'ApplicationName'    :   'rcGrowlMacTidy',
      u'AllNotifications'   :   ['test1'],
      u'DefaultNotifications' :   ['test1'],
      u'NotificationIcon'   :   None,
    } 
 
  # don't know if it is working or not
  def applicationNameForGrowl(self):
    """ 
    Identifies the application.
    """
    return 'rcGrowlMacTidy'
 
  #def applicationIconDataForGrowl(self):
    """
    If you wish to include a custom icon with the Grr,
    you can do so here. Disabled by default since I didn't
    want to bloat up this up
    """
    #icon = NSImage.alloc().init()
    #icon = icon.initWithContentsOfFile_(u'remix_icon.tiff')
    #return icon
 
  def growlNotificationWasClicked_(self, ctx):
 
    """
    callback for onClick event
    """
    print "we got a click! " + str(time.time()) + " &gt;&gt;&gt; " + str(ctx) + " &lt;&lt;

lockscreen announcement

lockscreen 1.0

Source Code: http://www.github.com/synthesizerpatel/lockscreen

OSX Lion 10.7+ Installer: http://www.remix.net/lockscreen.zip

Abstract

lockscreen.app provides a global-hotkey listener for the Control-Alt-Delete key-chord historically known as ‘The three finger salute’. Upon detecting the key-chord, will start the screen saver regardless of what application
you’re in.

The reason for this app is simple – there’s no ‘quick’ way to lock your screen if you’re in a hurry – the available solutions are lacking. With OSX’s built-in Hot Corners, about 50% of the time the act of getting up from my desk will jostle the mouse enough to bounce it back out of the hot corner resulting in the screen not locking.

Historical

Control-Alt-Delete was used to send a soft-reboot command to the operating system, with Windows NT they instead used this signal to bring up the Shutdown menu, which if you hit Enter would automatically lock your screen, I got used to this mechanism as it was instant and never prone to any problems.

Caveats

  • “OSX doesn’t have an alt button!”

    Yes it does, they call it Option, so if you want to be pedantic you could say this is really about utilizing Control-Option-Delete. If you hook up a PC keyboard, it’s really the same key code as ALT.

  • This is Lion Only

    This is built using the 10.7 SDK, I have no idea if this program will work on 10.6 or earlier, I don’t have the ability to test on any other operating systems, if someone else wishes to build/test this and contribute a modified Xcode build scheme I’ll be happy to include it.

Happy hacking!

Nathan Ramella (aka synthesizer patel)

webpage: remix.net

email: nar@hush.com

A clever bit of Bash

I can’t claim to be the originator of this technique, I recently saw something similar in the Android build tree, but I felt particularly clever when I came up with it not knowing the method.

#!/bin/bash 
export WORK_DIR=$(dirname $(readlink -e "${BASH_SOURCE}"))

The beauty of this is that it allows the script to know the directory it originated from, it’s ‘home’ for lack of a better description.

This type of mechanism is quite useful when dealing with cross-compile toolchains when you want to anchor all of your activity to one specific root directory that can propagate down into subordinate scripts through your environment variables.

The other bonus is that the script’s home directory is properly identified in the case where it’s sourced, i.e. source some_script.sh, or when it’s run /path/to/some_script.sh.

Rename your Python process automatically

Every Unixbeard has run into this situation – you need to kill one or more running Python scripts on a system and you run ‘killall myscript.py’, which sadly fails.

Why does it fail? Because the process doesn’t start with ‘myscript.py’ – it starts with the Python interprete and your script is shown as an argument.

Example:

root 1500 1228 0 Nov30 ? 00:00:08 python /usr/bin/printer-applet
root 2927 2918 1 Nov30 pts/2 02:22:42 /usr/bin/python -OO ./MyOpus.py

Fortunately setproctitle provides a method of arbitrarily renaming your process ID if you’re on a POSIX-ish operating system. The caveat I had was that I just wanted it to be automatic. To automate the process I drummed up this snippet which on being imported will automatically determine the script name that executed the Python interpreter and rename the process accordingly.

from setproctitle import setproctitle
import inspect
import os.path
parent = os.path.basename ( 
    os.path.abspath( 
        inspect.getfile( inspect.stack()[1][0] )
    ) 
)
setproctitle(parent)

All you need to do is import procshifter.py and it’ll handle the rest.

The cherished first post..

I think “Hello World” has served its purpose and should be retired.

I would like to offer up the alternative – I will crush you!

I think it’s much catchier.