Exploration

One year with a Sony Alpha SLT-A55

I’ve now spent just over one year with my EVIL (electronic viewfinder, interchangeable lens) DSLR-like camera, the Sony Alpha SLT-A55V. It has been a rewarding year for me photographically.

I can’t express how much more fun it is to use this camera compared to anything I’ve had earlier. I was in the land of point-and-shoots (including a “super zoom” of its time, the Olympus C-750UZ) before the SLT-A55.

There isn’t much I don’t like, and most of that is simply comes with the territory of a DSLR-like camera. The extra expense and bulk and even uncertainty (which lens should I take?) of having interchangeable lenses could be a drawback. All things considered, I am a happy and satisfied SLT-A55 owner, nothing more.

But beyond that, I have found this camera very freeing. I worry less and less about the pictures I take. I love the camera’s:

  • fast startup time
  • focus and depth-of-field
  • fast shot-to-shot time and rapid-fire shooting
  • automatic GPS tagging (although for best performance, you should keep the GPS Assist data up-to-date; I Download Sony GPS Assist data automatically when a memory card mounts with a LaunchAgent)
  • 1080i movies that I can play on my flat panel TV
  • availability of Minolta lenses on eBay (I wish I’d known more about this up-front), as well as new Sony lenses (online and in a more limited fashion at retail)

There’s certainly more, but this camera has removed so many barriers for me. I’ve taken at least 17,375 photos with it in a year, and I haven’t counted how many video clips. The best part is that I think a higher percentage of my images have been good than ever before.

The camera has gotten some press but I’m continually surprised that I don’t see it advertised more or presented in the weekly sales circulars (where it’s all Canon and Nikon, predictably).

Reinstall Xcode if easy_install fails with missing files like python.h

I was trying to install Dulwich on a Mac OS X Lion system this week and ran into difficulty. I kept getting installation failures that included a missing “python.h” and, eventually, llvm-gcc-4.2 failed to compile the module.

I found the situation frustrating, partly because I pretty much own the top search hits about how to install Dulwich and Hg-Git on Mac OS X Lion, thanks to some earlier article.

It turns out that I had reinstalled Lion about two weeks ago, and had not reinstalled Xcode 4. So, I updated to Xcode 4.2 and this completely eliminated my problem. Presumably, it would also work for you — and for future me, since I’m likely to repeat this — even if Lion hadn’t been reinstalled in between.

Things that didn’t work included but were not limited to:

  • cursing under my breath
  • stomping
  • hand-waving
  • complaining on Twitter
  • the silent treatment
  • waiting for 4, 12, and then 24 hours to see if it would fix itself
  • installing the latest version of setuptools, v0.6c11, from an egg
  • installing the current version of Distribute, new hotness or not
  • any steps involving Linux distribution package managers like apt-get.

Install Hgsubversion with Mercurial 1.9 on Mac OS X

In previous articles, I described how to Install Mercurial 1.9, Dulwich, and Hg-Git on Mac OS X Snow Leopard and Install Mercurial 1.9, Dulwich, and Hg-Git on Mac OS X Lion. For either of those operating systems, we can further extend Mercurial with support for connecting to Subversion repositories. While we could enable the bundled Convert extension, the Hgsubversion extension adds live access to remote networked Subversion repos, so it sounds more interesting.

My personal goal in doing this is to be able to work with the InstaDMG repository. It is hosted on Google Code, uses a Subversion repo, and depends upon Subversion keyword substitution. It presents an interesting challenge.

So, let’s install Hgsubversion to add Subversion support to the Git support we’ve previously set up. We need to take the following additional steps, going beyond what is done in the previous articles. To continue, you must already have installed:

  1. the Xcode tools (including Subversion), as appropriate for your operating system (probably either Xcode 3.2.6 or 4.1 at this point)
  2. Mercurial 1.9.2 or later.

Hgsubversion also needs Subversion to be installed. The Mercurial extension then has two different ways of working with Subversion. The Hgsubversion docs I read indicated that Subvertpy is preferred over the other method — using Subversion’s SWIG bindings — so we’ll install this additional Python module first. The docs also recommend running tests on Hgsubversion before using it on projects, so we will try that, as well.

Start with installing Subvertpy:

  1. Run the easy_install command in Terminal.
    $ sudo easy_install ‘subvertpy>=0.8.7’
    Searching for subvertpy>=0.8.7
    Reading <a href="http://pypi.python.org/simple/subvertpy/
    Reading"
    title="http://pypi.python.org/simple/subvertpy/
    Reading"
    >http://pypi.python.org/simple/subvertpy/
    Reading</a> <a href="http://samba.org/~jelmer/subvertpy
    Reading"
    title="http://samba.org/~jelmer/subvertpy
    Reading"
    >http://samba.org/~jelmer/subvertpy
    Reading</a> <a href="http://launchpad.net/subvertpy
    Best"
    title="http://launchpad.net/subvertpy
    Best"
    >http://launchpad.net/subvertpy
    Best</a> match: subvertpy 0.8.7
    Downloading <a href="http://samba.org/~jelmer/subvertpy/subvertpy-0.8.7.tar.gz
    Processing"
    title="http://samba.org/~jelmer/subvertpy/subvertpy-0.8.7.tar.gz
    Processing"
    >http://samba.org/~jelmer/subvertpy/subvertpy-0.8.7.tar.gz
    Processing</a> subvertpy-0.8.7.tar.gz
    Running subvertpy-0.8.7/setup.py -q bdist_egg —dist-dir /tmp/easy_install-hU1kY_/subvertpy-0.8.7/egg-dist-tmp-Qgbloj
    subvertpy/editor.c: In function ‘txdelta_call’:
    subvertpy/editor.c:133: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘string_list_to_apr_array’:
    subvertpy/util.c:235: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘path_list_to_apr_array’:
    subvertpy/util.c:258: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘revnum_list_to_apr_array’:
    subvertpy/util.c:497: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/_ra.c: In function ‘auth_init’:
    subvertpy/_ra.c:2180: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/_ra.c: In function ‘auth_set_parameter’:
    subvertpy/_ra.c:2215: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/_ra.c: In function ‘py_ssl_server_trust_prompt’:
    subvertpy/_ra.c:2681: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/wc.c: In function ‘adm_process_committed’:
    subvertpy/wc.c:1205: warning: ‘svn_wc_process_committed4’ is deprecated (declared at /usr/include/subversion-1/svn_wc.h:3765)
    subvertpy/wc.c: In function ‘get_pristine_copy_path’:
    subvertpy/wc.c:2451: warning: ‘svn_wc_get_pristine_copy_path’ is deprecated (declared at /usr/include/subversion-1/svn_wc.h:4994)
    subvertpy/wc.c: In function ‘py_dict_to_wcprop_changes’:
    subvertpy/wc.c:1127: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/wc.c: In function ‘adm_process_committed’:
    subvertpy/wc.c:1205: warning: ‘svn_wc_process_committed4’ is deprecated (declared at /usr/include/subversion-1/svn_wc.h:3765)
    subvertpy/wc.c: In function ‘get_pristine_copy_path’:
    subvertpy/wc.c:2451: warning: ‘svn_wc_get_pristine_copy_path’ is deprecated (declared at /usr/include/subversion-1/svn_wc.h:4994)
    subvertpy/_ra.c: In function ‘auth_init’:
    subvertpy/_ra.c:2180: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/_ra.c: In function ‘auth_set_parameter’:
    subvertpy/_ra.c:2215: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/_ra.c: In function ‘py_ssl_server_trust_prompt’:
    subvertpy/_ra.c:2681: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘string_list_to_apr_array’:
    subvertpy/util.c:235: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘path_list_to_apr_array’:
    subvertpy/util.c:258: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘revnum_list_to_apr_array’:
    subvertpy/util.c:497: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/editor.c: In function ‘txdelta_call’:
    subvertpy/editor.c:133: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/repos.c: In function ‘fs_root_file_length’:
    subvertpy/repos.c:747: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘string_list_to_apr_array’:
    subvertpy/util.c:235: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘path_list_to_apr_array’:
    subvertpy/util.c:258: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘revnum_list_to_apr_array’:
    subvertpy/util.c:497: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/wc.c: In function ‘adm_process_committed’:
    subvertpy/wc.c:1205: warning: ‘svn_wc_process_committed4’ is deprecated (declared at /usr/include/subversion-1/svn_wc.h:3765)
    subvertpy/wc.c: In function ‘get_pristine_copy_path’:
    subvertpy/wc.c:2451: warning: ‘svn_wc_get_pristine_copy_path’ is deprecated (declared at /usr/include/subversion-1/svn_wc.h:4994)
    subvertpy/wc.c: In function ‘py_dict_to_wcprop_changes’:
    subvertpy/wc.c:1127: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/wc.c: In function ‘adm_process_committed’:
    subvertpy/wc.c:1205: warning: ‘svn_wc_process_committed4’ is deprecated (declared at /usr/include/subversion-1/svn_wc.h:3765)
    subvertpy/wc.c: In function ‘get_pristine_copy_path’:
    subvertpy/wc.c:2451: warning: ‘svn_wc_get_pristine_copy_path’ is deprecated (declared at /usr/include/subversion-1/svn_wc.h:4994)
    subvertpy/util.c: In function ‘string_list_to_apr_array’:
    subvertpy/util.c:235: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘path_list_to_apr_array’:
    subvertpy/util.c:258: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘revnum_list_to_apr_array’:
    subvertpy/util.c:497: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/editor.c: In function ‘txdelta_call’:
    subvertpy/editor.c:133: warning: implicit conversion shortens 64-bit value into a 32-bit value
    zip_safe flag not set; analyzing archive contents…
    subvertpy.__init__: module references __file__
    subvertpy.ra_svn: module references __file__
    Adding subvertpy 0.8.7 to easy-install.pth file
    Installing subvertpy-fast-export script to /usr/local/bin

    Installed /Library/Python/2.7/site-packages/subvertpy-0.8.7-py2.7-macosx-10.7-intel.egg
    Processing dependencies for subvertpy>=0.8.7
    Finished processing dependencies for subvertpy>=0.8.7
  2. Clone the Hgsubversion repository from Bitbucket to a temporary location on your system. Under normal circumstances, I would use easy_install again to get hgsubversion. However, version 1.2.1 in PyPi didn’t work for me. So, to get a more current working version, I went to the source repository to get the latest software.
    $ cd /tmp
    $ hg clone <a href="https://bitbucket.org/durin42/hgsubversion" title="https://bitbucket.org/durin42/hgsubversion">https://bitbucket.org/durin42/hgsubversion</a> hgsubversion-work
  3. Run the setup tool from the repository.
    $ sudo python setup.py install
    running install
    running bdist_egg
    running egg_info
    creating hgsubversion.egg-info
    writing hgsubversion.egg-info/PKG-INFO
    writing top-level names to hgsubversion.egg-info/top_level.txt
    writing dependency_links to hgsubversion.egg-info/dependency_links.txt
    writing manifest file ‘hgsubversion.egg-info/SOURCES.txt’
    reading manifest file ‘hgsubversion.egg-info/SOURCES.txt’
    reading manifest template MANIFEST.in’
    warning: no files found matching ’*.rst’
    writing manifest file ‘hgsubversion.egg-info/SOURCES.txt’
    installing library code to build/bdist.macosx-10.7-intel/egg
    running install_lib
    running build_py
    creating build
    creating build/lib
    creating build/lib/hgsubversion
    copying hgsubversion/__init__.py -> build/lib/hgsubversion
    copying hgsubversion/__version__.py -> build/lib/hgsubversion
    copying hgsubversion/editor.py -> build/lib/hgsubversion
    copying hgsubversion/maps.py -> build/lib/hgsubversion
    copying hgsubversion/pushmod.py -> build/lib/hgsubversion
    copying hgsubversion/replay.py -> build/lib/hgsubversion
    copying hgsubversion/stupid.py -> build/lib/hgsubversion
    copying hgsubversion/svncommands.py -> build/lib/hgsubversion
    copying hgsubversion/svnexternals.py -> build/lib/hgsubversion
    copying hgsubversion/svnmeta.py -> build/lib/hgsubversion
    copying hgsubversion/svnrepo.py -> build/lib/hgsubversion
    copying hgsubversion/util.py -> build/lib/hgsubversion
    copying hgsubversion/wrappers.py -> build/lib/hgsubversion
    creating build/lib/hgsubversion/svnwrap
    copying hgsubversion/svnwrap/__init__.py -> build/lib/hgsubversion/svnwrap
    copying hgsubversion/svnwrap/common.py -> build/lib/hgsubversion/svnwrap
    copying hgsubversion/svnwrap/subvertpy_wrapper.py -> build/lib/hgsubversion/svnwrap
    copying hgsubversion/svnwrap/svn_swig_wrapper.py -> build/lib/hgsubversion/svnwrap
    creating build/lib/hgsubversion/help
    copying hgsubversion/help/subversion.rst -> build/lib/hgsubversion/help
    creating build/bdist.macosx-10.7-intel
    creating build/bdist.macosx-10.7-intel/egg
    creating build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/__init__.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/__version__.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/editor.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    creating build/bdist.macosx-10.7-intel/egg/hgsubversion/help
    copying build/lib/hgsubversion/help/subversion.rst -> build/bdist.macosx-10.7-intel/egg/hgsubversion/help
    copying build/lib/hgsubversion/maps.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/pushmod.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/replay.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/stupid.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/svncommands.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/svnexternals.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/svnmeta.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/svnrepo.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    creating build/bdist.macosx-10.7-intel/egg/hgsubversion/svnwrap
    copying build/lib/hgsubversion/svnwrap/__init__.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion/svnwrap
    copying build/lib/hgsubversion/svnwrap/common.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion/svnwrap
    copying build/lib/hgsubversion/svnwrap/subvertpy_wrapper.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion/svnwrap
    copying build/lib/hgsubversion/svnwrap/svn_swig_wrapper.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion/svnwrap
    copying build/lib/hgsubversion/util.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/wrappers.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/__init__.py to __init__.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/__version__.py to __version__.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/editor.py to editor.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/maps.py to maps.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/pushmod.py to pushmod.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/replay.py to replay.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/stupid.py to stupid.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/svncommands.py to svncommands.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/svnexternals.py to svnexternals.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/svnmeta.py to svnmeta.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/svnrepo.py to svnrepo.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/svnwrap/__init__.py to __init__.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/svnwrap/common.py to common.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/svnwrap/subvertpy_wrapper.py to subvertpy_wrapper.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/svnwrap/svn_swig_wrapper.py to svn_swig_wrapper.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/util.py to util.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/wrappers.py to wrappers.pyc
    creating build/bdist.macosx-10.7-intel/egg/EGG-INFO
    copying hgsubversion.egg-info/PKG-INFO -> build/bdist.macosx-10.7-intel/egg/EGG-INFO
    copying hgsubversion.egg-info/SOURCES.txt -> build/bdist.macosx-10.7-intel/egg/EGG-INFO
    copying hgsubversion.egg-info/dependency_links.txt -> build/bdist.macosx-10.7-intel/egg/EGG-INFO
    copying hgsubversion.egg-info/top_level.txt -> build/bdist.macosx-10.7-intel/egg/EGG-INFO
    zip_safe flag not set; analyzing archive contents…
    hgsubversion.__init__: module references __file__
    hgsubversion.util: module references __file__
    creating dist
    creating ‘dist/hgsubversion-1.2.1_34_f28e0f54a6ef-py2.7.egg’ and adding ‘build/bdist.macosx-10.7-intel/egg’ to it
    removing ‘build/bdist.macosx-10.7-intel/egg’ (and everything under it)
    Processing hgsubversion-1.2.1_34_f28e0f54a6ef-py2.7.egg
    creating /Library/Python/2.7/site-packages/hgsubversion-1.2.1_34_f28e0f54a6ef-py2.7.egg
    Extracting hgsubversion-1.2.1_34_f28e0f54a6ef-py2.7.egg to /Library/Python/2.7/site-packages
    Adding hgsubversion 1.2.1-34-f28e0f54a6ef to easy-install.pth file

    Installed /Library/Python/2.7/site-packages/hgsubversion-1.2.1_34_f28e0f54a6ef-py2.7.egg
    Processing dependencies for hgsubversion==1.2.1-34-f28e0f54a6ef
    Finished processing dependencies for hgsubversion==1.2.1-34-f28e0f54a6ef
  4. Check the version of Hgsubversion. Since we haven’t enabled it yet, it won’t report anything terribly useful.
    $ hg version —svn
    hg version: option —svn not recognized
    hg version

    output version and copyright information

    use "hg help version" to show the full help text
  5. Add Hgsubversion to your ~/.hgrc to enable the extension. We previously enabled the “bookmarks” and “hggit” extensions.
    [extensions]
    hgext.bookmarks =
    hggit =
    hgsubversion =
  6. Check Hgsubversion’s version again. Now that it is enabled, it should report information similar to what you see below, which is very different than what we saw above.
    $ hg version —svn
    Mercurial Distributed SCM (version 1.9.2+20110831)
    (see <a href="http://mercurial.selenic.com" title="http://mercurial.selenic.com">http://mercurial.selenic.com</a> for more information)

    Copyright (C) 2005-2011 Matt Mackall and others
    This is free software; see the source for copying conditions. There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

    hgsubversion: 1.2.1+34-f28e0f54a6ef
    Subversion: 1.6.16
    bindings: Subvertpy 0.8.7
  7. Run the Hgsubversion test suite. In my case, I had failures, a situation which appears to already have been reported in the Hgsubversions issue tracker.
    $ python tests/run.py
    ………………………………………….F………FF…………………………..FEF………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………
    ======================================================================
    ERROR: test_file_map_exclude_stupid (test_fetch_mappings.MapTests)
    –––––––––––––––––––––––-
    Traceback (most recent call last):
      File "/private/tmp/hgsubversion_work/tests/test_fetch_mappings.py", line 130, in test_file_map_exclude_stupid
        self.test_file_map_exclude(True)
      File "/private/tmp/hgsubversion_work/tests/test_fetch_mappings.py", line 125, in test_file_map_exclude
        self.wc_path, filemap=self.filemap)
      File "/Library/Python/2.7/site-packages/mercurial/commands.py", line 1041, in clone
        branch=opts.get(‘branch’))
      File "/Library/Python/2.7/site-packages/mercurial/hg.py", line 334, in clone
        destrepo.clone(srcrepo, heads=revs, stream=stream)
      File "/Library/Python/2.7/site-packages/mercurial/localrepo.py", line 1946, in clone
        return self.pull(remote, heads)
      File "/private/tmp/hgsubversion_work/hgsubversion/svnrepo.py", line 48, in wrapper
        return fn(self, *args, **opts)
      File "/private/tmp/hgsubversion_work/hgsubversion/svnrepo.py", line 63, in pull
        return wrappers.pull(self, remote, heads, force)
      File "/private/tmp/hgsubversion_work/hgsubversion/wrappers.py", line 346, in pull
        firstrun)
      File "/private/tmp/hgsubversion_work/hgsubversion/stupid.py", line 642, in convert_rev
        ui, svn, meta, b, branches[b], r, parentctx)
      File "/private/tmp/hgsubversion_work/hgsubversion/stupid.py", line 228, in diff_branchrev
        files_data = patchrepo(ui, meta, parentctx, cStringIO.StringIO(d2))
      File "/private/tmp/hgsubversion_work/hgsubversion/stupid.py", line 168, in patchrepo
        ret = patch.patchbackend(ui, backend, patchfp, 0, touched)
      File "/Library/Python/2.7/site-packages/mercurial/patch.py", line 1412, in patchbackend
        raise PatchError(_(‘patch failed to apply’))
    PatchError: patch failed to apply

    ======================================================================
    FAIL: test_many_special_cases_diff (test_fetch_command.TestBasicRepoLayout)
    –––––––––––––––––––––––-
    Traceback (most recent call last):
      File "/private/tmp/hgsubversion_work/tests/test_fetch_command.py", line 72, in test_many_special_cases_diff
        self._many_special_cases_checks(repo)
      File "/private/tmp/hgsubversion_work/tests/test_fetch_command.py", line 83, in _many_special_cases_checks
        ‘4e256962fc5df545e2e0a51d0d1dc61c469127e6’)
    AssertionError: ‘13c5dc1514ad8619c589a8929bfe0ece5c00f18e’ != ‘4e256962fc5df545e2e0a51d0d1dc61c469127e6’

    ======================================================================
    FAIL: test_oldest_not_trunk_and_tag_vendor_branch (test_fetch_command.TestStupidPull)
    –––––––––––––––––––––––-
    Traceback (most recent call last):
      File "/private/tmp/hgsubversion_work/tests/test_fetch_command.py", line 224, in test_oldest_not_trunk_and_tag_vendor_branch
        ‘1a6c3f30911d57abb67c257ec0df3e7bc44786f7’)
    AssertionError: ‘fa799f2781255dba874645e849d75af837472518’ != ‘1a6c3f30911d57abb67c257ec0df3e7bc44786f7’

    ======================================================================
    FAIL: test_stupid (test_fetch_command.TestStupidPull)
    –––––––––––––––––––––––-
    Traceback (most recent call last):
      File "/private/tmp/hgsubversion_work/tests/test_fetch_command.py", line 201, in test_stupid
        ‘4e256962fc5df545e2e0a51d0d1dc61c469127e6’)
    AssertionError: ‘13c5dc1514ad8619c589a8929bfe0ece5c00f18e’ != ‘4e256962fc5df545e2e0a51d0d1dc61c469127e6’

    ======================================================================
    FAIL: test_file_map_exclude (test_fetch_mappings.MapTests)
    –––––––––––––––––––––––-
    Traceback (most recent call last):
      File "/private/tmp/hgsubversion_work/tests/test_fetch_mappings.py", line 127, in test_file_map_exclude
        self.assertEqual(node.hex(self.repo[‘default’].node()), ‘b37a3c0297b71f989064d9b545b5a478bbed7cc1’)
    AssertionError: ‘81ae7af456c0e414ddc380ff641b37da84a9df8f’ != ‘b37a3c0297b71f989064d9b545b5a478bbed7cc1’

    ======================================================================
    FAIL: test_file_map_stupid (test_fetch_mappings.MapTests)
    –––––––––––––––––––––––-
    Traceback (most recent call last):
      File "/private/tmp/hgsubversion_work/tests/test_fetch_mappings.py", line 115, in test_file_map_stupid
        self.test_file_map(True)
      File "/private/tmp/hgsubversion_work/tests/test_fetch_mappings.py", line 112, in test_file_map
        self.assertEqual(node.hex(self.repo[‘default’].node()), ‘e524296152246b3837fe9503c83b727075835155’)
    AssertionError: ‘ecf9b521a1799ebb0e01c1d1e86305ea8b542d2e’ != ‘e524296152246b3837fe9503c83b727075835155’

    –––––––––––––––––––––––-
    Ran 492 tests in 1212.923s

    FAILED (failures=5, errors=1)

That’s it! Hgsubversion has been installed and enabled. Even though we got errors from the tests, we can proceed.

  1. Clone a Subversion repository, like the InstaDMG one.
    • This gets you just the trunk and current state:
      $ hg clone <a href="http://instadmg.googlecode.com/svn/trunk" title="http://instadmg.googlecode.com/svn/trunk">http://instadmg.googlecode.com/svn/trunk</a> /tmp/instadmg_trunk_work
    • This gets you the full history.
      $ hg clone <a href="http://instadmg.googlecode.com/svn/" title="http://instadmg.googlecode.com/svn/">http://instadmg.googlecode.com/svn/</a> /tmp/instadmg_full_work
  2. Enable the Mercurial Keywords extension in the cloned repo’s .hg/hgrc. (According to the documentation, you should not enable the Keywords extension on a global basis in your ~/.hgrc, but instead do so on a repo-by-repo basis in .hg/hgrc.) We’ll also set it so it only affects Bash scripts with the right filename extension, because we want keyword expansion on just the “instadmg.bash” file.
    [paths]
    default = http://instadmg.googlecode.com/svn/trunk/

    [extensions]
    keyword =

    [keyword]
    # expand keywords in all bash and python files in working dir
    **.bash =
  3. Set your HGUSER environment variable. The next step will need this. The following steps will do it temporarily for your current terminal session, so when you get a chance, you’ll want to read about how to do this more permanently.
    $ HGUSER="anonymous"
    $ export HGUSER
  4. Try kwdemo. You must be in the directory for the cloned repository and have enabled the Keyword extension.
    $ hg kwdemo —default

            configuration using default svn keywordset
    [extensions]
    keyword =
    [keyword]
    **.bash =
    demo.txt =
    [keywordset]
    svn = True
    [keywordmaps]
    Author = {author|user}
    Date = {date|svnisodate}
    Id = {file|basename},v {node|short} {date|svnutcdate} {author|user}
    LastChangedBy = {author|user}
    LastChangedDate = {date|svnisodate}
    LastChangedRevision = {node|short}
    Revision = {node|short}

            keywords expanded
    $Author: anonymous $
    $Date: 2011-09-26 14:32:06 -0400 (Mon, 26 Sep 2011) $
    $Id: demo.txt,v 5e86fb5c9fae 2011-09-26 18:32:06Z anonymous $
    $LastChangedBy: anonymous $
    $LastChangedDate: 2011-09-26 14:32:06 -0400 (Mon, 26 Sep 2011) $
    $LastChangedRevision: 5e86fb5c9fae $
    $Revision: 5e86fb5c9fae $
  5. Edit the .hg/hgrc file for the cloned repository so that you can set up keyword substitution for the “Revision” keyword. In my experience, you’ll need to set up a map for this, unless you want to get the Hg revision number that you see in the default output of kwdemo above.
    [paths]
    default = http://instadmg.googlecode.com/svn/

    [extensions]
    keyword =

    [keyword]
    # expand keywords in all bash and python files in working dir
    **.bash =

    [keywordmaps]
    Revision = {svnrev}
  6. Try the demo again. I never get this part to work, but you may have more luck. Actual shrinking/expansion, which we’ll see next, does work for me.
    $ hg kwdemo —rcfile .hg/hgrc

            configuration using custom keyword template maps
            extending current template maps
    [extensions]
    keyword =
    [keyword]
    **.bash =
    demo.txt =
    [keywordset]
    svn = False
    [keywordmaps]
    Revision = {svnrev}

            keywords expanded
    $Revision:  $
  7. Expand the keywords in your local InstaDMG repository.
    $ hg kwshrink # Run after each keyword configuration change
    $ grep Revision instadmg.bash
    SVN_REVISION=`/bin/echo ‘$Revision$’ | /usr/bin/awk ’{ print $2 }’`
    $ hg kwexpand # Run to expand the keywords in the repository. Be sure to kwshrink before making and committing changes.
    $ grep Revision instadmg.bash
    SVN_REVISION=`/bin/echo ‘$Revision: 425 $’ | /usr/bin/awk ’{ print $2 }’`
    $ hg kwshrink

Notice that “Revision: 425” is returned when keywords are expanded, replacing the “$Revision” keyword. This is precisely what we need with InstaDMG, where the revision information is reported when the instadmg.bash script is run.

Install Mercurial 1.9, Dulwich, and Hg-Git on Mac OS X Lion

Hg-Git is the Mercurial extension to use if you want to connect to local or remote Git repositories. I exclusively use Mercurial and Hg-Git for all of my Github transactions, so I can personally vouch that it works.

Now that Hg-Git has been updated to better support Mercurial 1.9, let’s see if we can get an Hg toolchain working on Lion. Since I did that on Snow Leopard a few days ago, Hg-Git has made it into PyPI. The installation instructions this time are a bit more streamlined, because we can now use easy_install to get Hg-Git and its dependencies.

To get the toolchain set up, we’ll need Xcode. The Xcode suite includes tools we’ll need to make Python easy_install work, along with Subversion (a prerequisite for Hgsubversion, which I’ll talk about in a later article) and other useful tools.

The Xcode installation is a multi-step install process. Both current download methods — the developer download through connect.apple.com (if you have a paid Mac Developer Account) and the Mac App Store — give you an “Install Xcode” application. That application runs a second, real installer that you have to finish before you actually have the Xcode tools available in a ready-to-use state. This is very similar to the situation for Mac OS X Lion, so you may be developing a sense of familiarity with the situation.

To install Mercurial:

  1. Download Mercurial 1.9.2 or later. The binary packages are standard Mac OS X packages; get the one for Lion.
  2. Install Mercurial.

To add Hg-Git to Mercurial on Lion:

  1. Download Xcode 4.1 or later if you don’t already have it. You can do this through connect.apple.com or via the Mac App Store.
  2. Install Xcode if it is not already installed.
    • Open the developer disk image, run the installer inside it, and then run the “Install Xcode” application that was placed in /Applications.
    • Run the “Install Xcode” application that was placed in /Applications by the Mac App Store.
  3. Open Terminal. Run the following command, which will install hg-git and its dependencies (including dulwich, of which you’ll want version 0.8.0 or later):
    $ sudo easy_install ‘hg-git>=0.3.1’
    Password:
    Searching for hg-git>=0.3.1
    Reading <a href="http://pypi.python.org/simple/hg-git/
    Reading"
    title="http://pypi.python.org/simple/hg-git/
    Reading"
    >http://pypi.python.org/simple/hg-git/
    Reading</a> <a href="http://hg-git.github.com/
    Best"
    title="http://hg-git.github.com/
    Best"
    >http://hg-git.github.com/
    Best</a> match: hg-git 0.3.1
    Downloading <a href="http://pypi.python.org/packages/source/h/hg-git/hg-git-0.3.1.tar.gz#md5=4b15867a07abb0be985177581ce64cee
    Processing"
    title="http://pypi.python.org/packages/source/h/hg-git/hg-git-0.3.1.tar.gz#md5=4b15867a07abb0be985177581ce64cee
    Processing"
    >http://pypi.python.org/packages/source/h/hg-git/hg-git-0.3.1.tar.gz#md5=…</a> hg-git-0.3.1.tar.gz
    Running hg-git-0.3.1/setup.py -q bdist_egg —dist-dir /tmp/easy_install-_Uauza/hg-git-0.3.1/egg-dist-tmp-rERQMH
    zip_safe flag not set; analyzing archive contents…
    Adding hg-git 0.3.1 to easy-install.pth file

    Installed /Library/Python/2.7/site-packages/hg_git-0.3.1-py2.7.egg
    Processing dependencies for hg-git>=0.3.1
    Searching for dulwich>=0.8.0
    Reading <a href="http://pypi.python.org/simple/dulwich/
    Reading"
    title="http://pypi.python.org/simple/dulwich/
    Reading"
    >http://pypi.python.org/simple/dulwich/
    Reading</a> <a href="http://samba.org/~jelmer/dulwich
    Reading"
    title="http://samba.org/~jelmer/dulwich
    Reading"
    >http://samba.org/~jelmer/dulwich
    Reading</a> <a href="http://launchpad.net/dulwich
    Best"
    title="http://launchpad.net/dulwich
    Best"
    >http://launchpad.net/dulwich
    Best</a> match: dulwich 0.8.0
    Downloading <a href="http://samba.org/~jelmer/dulwich/dulwich-0.8.0.tar.gz
    Processing"
    title="http://samba.org/~jelmer/dulwich/dulwich-0.8.0.tar.gz
    Processing"
    >http://samba.org/~jelmer/dulwich/dulwich-0.8.0.tar.gz
    Processing</a> dulwich-0.8.0.tar.gz
    Running dulwich-0.8.0/setup.py -q bdist_egg —dist-dir /tmp/easy_install-bHRaTM/dulwich-0.8.0/egg-dist-tmp-MNy6RK
    dulwich/_objects.c: In function ‘py_parse_tree’:
    dulwich/_objects.c:101: warning: implicit conversion shortens 64-bit value into a 32-bit value
    dulwich/_objects.c: In function ‘cmp_tree_item’:
    dulwich/_objects.c:148: warning: implicit conversion shortens 64-bit value into a 32-bit value
    dulwich/_objects.c:152: warning: implicit conversion shortens 64-bit value into a 32-bit value
    dulwich/_objects.c: In function ‘py_sorted_tree_items’:
    dulwich/_objects.c:192: warning: implicit conversion shortens 64-bit value into a 32-bit value
    dulwich/_objects.c:224: warning: implicit conversion shortens 64-bit value into a 32-bit value
    dulwich/_pack.c: In function ‘py_apply_delta’:
    dulwich/_pack.c:98: warning: implicit conversion shortens 64-bit value into a 32-bit value
    dulwich/_pack.c:101: warning: implicit conversion shortens 64-bit value into a 32-bit value
    zip_safe flag not set; analyzing archive contents…
    dulwich.tests.__init__: module references __file__
    dulwich.tests.test_index: module references __file__
    dulwich.tests.test_objects: module references __file__
    dulwich.tests.test_pack: module references __file__
    dulwich.tests.utils: module references __file__
    Adding dulwich 0.8.0 to easy-install.pth file
    Installing dul-daemon script to /usr/local/bin
    Installing dul-web script to /usr/local/bin
    Installing dulwich script to /usr/local/bin

    Installed /Library/Python/2.7/site-packages/dulwich-0.8.0-py2.7-macosx-10.7-intel.egg
    Finished processing dependencies for hg-git>=0.3.1
  4. Edit your ~/.hgrc to enable the Hg-Git Mercurial extension, as noted in the Hg-Git documentation.
    [extensions]
    hgext.bookmarks =
    hggit =

That’s it! Once Mercurial 1.9 plus Hg-Git 0.3.1 or later are installed and you’ve enabled Hg-Git in your ~/.hgrc, you are ready to use Mercurial with local and remote Git repositories.

Switch between multiple Kerberos identities by changing the default identity

I’ve been using a few Kerberos-enabled Web applications lately. I tend to need to use a different Kerberos identity to log into these applications than I use for my Mac. This has tended to result in frustration, because my browsers have not prompted which identity for which application.

I found that I can work around this by changing which identity is the default one:

  1. Launch the Ticket Viewer application. The easiest way is by opening Keychain Access and selecting it from the bold Keychain Access application menu.
  2. Add two or more Identities and get tickets for each.
  3. Highlight the identity that you will use in the first Web application.
  4. Choose Ticket > Set as Default.
  5. Switch back to your browser, and load the first Web application. It should allow you to log in using the current default Kerberos identity.
  6. Switch back to Ticket Viewer.
  7. Select the identity you will use for the second Web application.
  8. Choose Ticket > Set as Default.
  9. Go to the browser and load the second Web application.
  10. Switch back to Ticket Viewer.
  11. Highlight the identity you will use for regular tasks in Mac OS X. This was probably your original default identity.
  12. Choose Ticket > Set as Default.

Once you have logged in to each Kerberized Web application, you shouldn’t need to reauthenticate during the same login session. Switching back to your original default identity ensures that the regular uses of your Mac OS X login work with single sign-on.

Parse a vendor RSS feed to get the latest available product version

There may be times when you want to obtain the number of the latest available version — not just the latest installed version — of a software package through automated means. If the vendor or project provides a syndication feed (either RSS or Atom) that describes new releases, then you may be able to parse that data and get the newest release from it.

As an example, let’s examine the RSS feed for Group Logic’s ExtremeZ-IP. Other developers provide RSS/Atom feeds for their releases, but the EZIP feed is a good one to start a demonstration with because it is generally structured well.

We can break apart the EZIP feed with the Universal Feed Parser module for Python, which you must obtain separately.

Update: Because of Mark Pilgrim situation (also described here), the Universal Feed Parser Web site is no longer available. There is an alternative source for the Universal Feed Parser at Google Code, and I have cloned the Universal Feed Parser repository to Bitbucket from there.

import feedparser
ezip_feed = feedparser.parse(‘https://www.grouplogic.com/ezipreleases.xml’)
ezip_feed[‘feed’][‘title’]
u‘ExtremeZ-IP Latest Releases’
ezip_feed.version
‘rss20’

As you can see, the “ExtremeZ-IP Latest Releases” feed is automatically recognized as RSS 2.0. I prefer to use HTTPS for fetching these feeds whenever possible, so if the developer has an HTTP feed, I try to see if it also works with HTTPS.

Next, let’s find out where the version numbers are kept in the feed. It looks like they are in the entry title, based on reading the feed in Safari RSS. I can confirm that with the Universal Feed Parser. We’ll want to examine the title of every feed item so we can better handle both current and future entries from the feed. There are more entries to the EZIP than I will print out.

for entry in ezip_feed.entries:
    entry[‘title’]
u‘ExtremeZ-IP File and Print Server - Version 7.1.1x94’
u‘ExtremeZ-IP File and Print Server - Version 7.1x14’
u‘ExtremeZ-IP File and Print Server - Version 7.0x41’

We get Unicode strings as output from the Universal Feed Parser. That’s why the quoted strings are preceded with a “u” character.

I’d like to strip out the version string from the title element in each entry. I’m going to do so by splitting on whitespace and getting the last group of characters from the string. (This doesn’t account for text like “Hot Fix,” as seen in the EZIP feed, but it is still a good enough starting point for my purposes.)

for entry in ezip_feed.entries:
    entry[‘title’].split()[-1]
u‘7.1.1x94’
u‘7.1x14’
u‘7.0x41’

By stripping out the build number after “x” in the version string, you potentially lose some data. In the EZIP feed, there are entries where two consecutive version numbers are the same except for the build number after the “x.” However, depending on your needs, it may still be useful to eliminate that part of the version string, so we’ll do that next.

for entry in ezip_feed.entries:
    entry[‘title’].split()[-1].partition(‘x’)[0]
u‘7.1.1’
u‘7.1’
u‘7.0’

We really only need the most current or “top” item in the feed, since that should give us the newest release number. The newest version in this particular feed should be in the first entry. That’s “entries[0]” below, because we’re using Python and it zero references the first item in lists.

ezip_current_release = ezip_feed.entries[0].title
ezip_current_release_version = ezip_current_release.split()[-1].split()[-1]
ezip_current_release_version_stripped = ezip_current_release_version.partition(‘x’)[0]
u‘7.1.1’

There, we now have the version number of the most current release, 7.1.1, for the product. We have drawn it straight from the developer’s syndicated feed, so it is as current as the developer makes it.

How could that be useful? The output can be compared against other data, like the currently-installed version. The comparison, in turn, could be made part of a monitoring workflow, so you could get alerts if you fall behind.

If we didn’t strip the build number after the “x,” we would be left with a complex version number. Some Python tools, like distutils, will not currently handle the trailing characters in the version number well.

I have found that you can improve upon distutils’ StrictVersion/LooseVersion version number handling by switching to parse_version in pkg_rsources (“from pkg_resources import parse_version as V”). More coverage of that topic appears in PEP 386. If you are comparing the original version strings from the EZIP feed with similarly complex output from elsewhere, then I would probably use the pkg_resources module.

Planning out some Mac Pro surgery

I’m thinking about performing surgery on my Mac Pro. It’s been a while since my computer has felt fast, or like I had enough storage to do what I want to do with it.

I want a lot more storage in the case. I want to see the performance difference that upgrading to an SSD boot disk might make. I want to continue to support Boot Camp, since there is the off chance I may boot into Windows. Whatever happens, I want the new storage to be faster than what it’s replacing.

I already have a CalDigit RAID card, obtained late in 2010. I haven’t populated it yet, so I still have flexibility. The RAID card will allow me to add multiple drives into one larger volume, which should produce a performance benefit over having a single larger drive. (The latest generation of drives just hit 3 TB in capacity.)

Here’s the plan I am roughing out:

  • Reroute the motherboard SFF-8087 (iPass) connection to the optical drive bay, breaking it out into SATA connections. From one forum post I found, I think that it would be good to have an iPass cable longer than the common 0.5 meter variety. The Adaptec 1 meter SFF-8087 to SFF-8482 cable seems to be a good choice; it has removable power cables that appear to be Molex-to-SATA. It’s also one of the least expensive options I uncovered, especially when considering cables that have SFF-8482 connectors that combine SATA data and power.
  • Set up space for a 2.5 inch and 3.5 inch drive in the empty lower optical bay with the OWC Multi-mount 2.5“ to 3.5” and 3.5“ to 5.25” bracket and cable set for 2006-2008 MacPro. I don’t really need the entire kit, but I’m hoping the bundled Molex-to-SATA power splitter will work with the Adaptec iPass cable to support both drives from the single Molex cable in the bottom half of the bay.
  • Get a relatively small OWC Mercury Extreme Pro SSD to set up as the primary boot disk, physically locating it in the OWC bracket in the optical bay. Connect it to the new iPass cable.
  • Use one of my existing 3.5 inch drives in the optical bay as a Boot Camp disk. Connect it to the new iPass cable.
  • Reroute the iPass connector from the motherboard to the CalDigit RAID card, so that the four 3.5 inch SATA drive bays are associated with that controller.
  • Populate the four drive bays with matched drives for a RAID. These will hold user data that doesn’t fit on the SSD.
  • Add a UPS that supports USB shutdown on Mac OS X. I’m looking at the CyberPower CP1500PFCLCD. Graceful shutdown during power outages will be critical for the RAID array.
  • Optionally route the two remaining motherboard SATA connectors, which I’ve heard do not support booting or Boot Camp, to a back panel eSATA PCI Express insert. This would give me more options for external storage beyond FireWire 800. This will be important for faster local backup, if I can get to that point. (While the CalDigit RAID card does support external storage, it reportedly can only use CalDigit’s own enclosures.)
  • Optionally add more RAM. Memory for my early 2008 Mac Pro is on the down side of the commodity curve, so it’s not getting any cheaper.

Of course, all of this is all more expensive than I would like — especially since right now it’s something of a gamble as to whether it would work or not — but the more I think about it, the more fun it seems. I haven’t taken on a project like this in a long time.

Of Flash Player versions and codesigning and signatures

It’s certainly an understatement to say that there’s been a lot of talk about the Adobe Flash Player on Apple platforms in the last year. On Mac OS X, Apple bundles the Flash Player and tends to distribute some — but not all — updates to it.

I wanted compare the bundled Flash Player version against the latest version from Adobe, which is currently v10.1.82.76. So, let’s look at what comes with Snow Leopard from the perspective of a codesigned executable.

# Flash Player version 10.0.45.2
# Installed with Mac OS X Snow Leopard v10.6.4
$ codesign -vvv /Library/Internet\ Plug-Ins/Flash\ Player.plugin
/Library/Internet Plug-Ins/Flash Player.plugin: valid on disk
/Library/Internet Plug-Ins/Flash Player.plugin: satisfies its Designated Requirement

A quick look at the bundled plugin shows that it is codesigned. This means that it has a known signature. If the executable is modified, the signature will no longer be valid. The signature is tied to the identity of a signing authority, which is generally the source of the software.

It may be helpful to think of codesigning as a tamper-resistant seal from the manufacturer. It’s not going to protect you from lots of different kinds of vulnerabilities, but if its cryptographic signature is intact and valid, you have a good idea that the software hasn’t been modified by a third party.

Mac OS X Leopard and Snow Leopard have shipped with applications signed by Apple. The Flash Player plugin comes from Adobe. So, who signs the bundled Flash Player?

$ codesign -dvvv /Library/Internet\ Plug-Ins/Flash\ Player.plugin
Executable=/Library/Internet Plug-Ins/Flash Player.plugin/Contents/MacOS/Flash Player
Identifier=com.macromedia.Flash Player.plugin
Format=bundle with Mach-O universal (i386 ppc)
CodeDirectory v=20100 size=34023 flags=0x0(none) hashes=1694+3 location=embedded
CDHash=f81bb75e4ec6f085f59e3c21021136c0f974fa7a
Signature size=4064
Authority=Software Signing
Authority=Apple Code Signing Certification Authority
Authority=Apple Root CA
Info.plist entries=12
Sealed Resources rules=9 files=2
Internal requirements count=1 size=188

You’d be forgiven for not having your eye drawn to the answer immediately, but it’s right there on the “Authority” lines. Just as with the rest of Mac OS X, Apple signed the Flash Player plugin they bundled with the OS.

Now, let’s upgrade the plugin to the latest version available from Adobe and see what happens to the signature. Courtesy of Preston’s WatchedInstall tool, we can see that the plugin’s CodeResources file is removed during this upgrade. Interestingly, the “Adobe Flash Player Install Manager” application installed with the update is codesigned.

- /Library/Internet Plug-Ins/Flash Player.plugin/Contents/CodeResources
- /Library/Internet Plug-Ins/Flash Player.plugin/Contents/_CodeSignature/CodeResources
+ /Applications/Utilities/Adobe Flash Player Install Manager.app/Contents/CodeResources
+ /Applications/Utilities/Adobe Flash Player Install Manager.app/Contents/_CodeSignature/CodeResources

The newer Flash Player version, however, seems to consist of two new plugins contained within the overall structure of a parent plugin. Neither the parent nor the new applications within the same bundle install a new code signature. This results in three unsigned executables:

# Flash Player version 10.1.82.76
# Installed on Mac OS X 10.6.4
$ codesign -vvv /Library/Internet\ Plug-Ins/Flash\ Player.plugin
/Library/Internet Plug-Ins/Flash Player.plugin: code object is not signed
$ codesign -vvv /Library/Internet\ Plug-Ins/Flash\ Player.plugin/Contents/PlugIns/FlashPlayer-10.6.plugin
/Library/Internet Plug-Ins/Flash Player.plugin/Contents/PlugIns/FlashPlayer-10.6.plugin: code object is not signed
$ codesign -vvv /Library/Internet\ Plug-Ins/Flash\ Player.plugin/Contents/PlugIns/FlashPlayer-10.4-10.5.plugin
/Library/Internet Plug-Ins/Flash Player.plugin/Contents/PlugIns/FlashPlayer-10.4-10.5.plugin: code object is not signed

Therefore, you trade the known security vulnerabilities of the older version of Flash Player bundled with the operating system with a different kind of security problem with the new version. It would be silly to not make that trade if you are browsing the Web at all on a Snow Leopard-based computer.

However, it’s also difficult to understand why a large corporation with the resources of Adobe cannot codesign a piece of software as critical to the Mac OS X browsing experience as the Adobe Flash plugin is — especially when its “Install Manager” application is signed.

It’s also puzzling why Apple continues to trail well behind the latest releases of Flash Player. Add to that mystery the question of why Apple never updates the absolutely antique bundled version of the Shockwave Player plugin.

Run Drupal cron with Drush at Site5

After starting to use Drush, I wanted to switch my Drupal cron jobs over to it. I’d previously been running these jobs the standard way, loading a URL for each of my Drupal sites with curl. That curl command was run by the cron; Site5 allows editing of your crontab directly or through its Backstage Web interface.

I started with the basics. Drush was installed in my home directory, with an alias under ~/bin in my Site5 account. I replaced my curl command one-for-one with the following:

$ /home/your_site5_account/bin/drush \
—quiet \
—root=/home/your_site5_account/public_html/your_drupal_site/ \
cron

This didn’t work out well. I was getting a huge number of mail messages, indicating problems with the cron jobs. The messages typically contained “tput: No value for $TERM and no -T specified.” Needless to say, this was rather frustrating, so after some trial and error, I modified the command as follows, and that has been working better for me.

$ /usr/local/bin/php \
-d memory_limit=64M \
/home/your_site5_account/drush/drush.php \
—quiet \
—root=/home/your_site5_account/public_html/your_drupal_site/ \
cron

The biggest change is that I specify PHP, rather than Drush, directly. This was done so that I could increase the PHP memory limit for the cron job significantly, to 64M, while running Drush. I’m sure that this increase was needed partly due to the number of modules I have installed on my main site (which has an even larger default PHP memory limit in its php.ini). My research indicated that I needed to do this for Drush since it doesn’t access the Drupal sites in the same way loading a page does.

The other noticeable change is that I provide the path to the drush.php file rather than pointing to the Drush alias.

Local logins succeed but network logins fail on an Active Directory bound Mac OS X Leopard system

I came across an interesting “problem” with Active Directory binding on Mac OS X Leopard. The symptoms were:

  • No Active Directory user accounts could log into the computer from the loginwindow.
  • Some of the attempted logins involved cached mobile accounts from the Active Directory.
  • The account login failures happened even though loginwindow’s “network accounts are available” indicator was green.
  • The login problem persisted it the computer been unbound and rebound to the domain.
  • The same Active Directory users could log in on other Macs.
  • Local users could log in to the affected computer.
  • Using “su” to switch users from a local user to an Active Directory user worked in Terminal.
  • Lookups using “dscl” and other DirectoryService tools worked.

Since I’ve written (what seems like a) a book about Active Directory troubleshooting, I threw the book at this problem. It ended up taking quite some time to troubleshoot, and the answer ended up being very simple. However, it wasn’t on my normal list of culprits.

The biggest clue I found, besides the symptoms above, was that the DirectoryService debug logs yielded this during Active Directory logins from loginwindow:

2010-02-24 21:33:37 EST - T[0xB0103000] - mbrmig - Dispatch - Membership -
is user jaharmi member of group GUID 3BBC71F5-3497-4494-904B-8AC3E25CCA52 =
false

It didn’t seem like a smoking gun, but I’d never come across this “false” response on a bound system before. So, what group was so important to the login process that the DirectoryService debug logs cared enough to note the failure? I was darned if I knew, and I had no other promising clues at that point.

So, I investigated that group further, and found it by its UUID using dsmemberutil:

$ dsmemberutil getid -X 3BBC71F5-3497-4494-904B-8AC3E25CCA52
gid: 200

Well, that helped a little, but the name would have helped a lot more. I had to find which group corresponded to the GID of 200. That GID was not at all familiar to me, but it was under 500, so there was a pretty good chance it came from Mac OS X.

$ dscl /Local/Default -list /Groups PrimaryGroupID | awk ‘$2 == 200 { print
$1; }’

com.apple.access_loginwindow

This was my eureka! moment. I wasn’t entirely sure, but I was pretty confident that the “com.apple.access_loginwindow” group was the access control list group for the loginwindow process. Loginwindow controls all graphical logins to Mac OS X, and is the parent process of each GUI login session.

Looking up the group’s description confirmed that it was the ACL group. I did the lookup in Workgroup Manager, which was set to view the DSLocal directory service. While I was there, I also checked the membership: it listed only the computational group “localaccounts.” The “localaccounts” group is essentially a query that returns all accounts in the local directory service.

Well, that would certainly prevent Active Directory users from logging in with loginwindow. The ACL consulted the membership of the “com.apple.access_loginwindow” group to determine who was allowed to log in via the GUI. Because it contained only the “localaccounts” group, the ACL was preventing all non-local users from logging in.

Not knowing how this group was handled or even what had last edited it, I compared the affected system to a different AD-bound Leopard computer, which also had Workgroup Manager. (It’s handy to have the Mac OS X Server Admin Tools deployed out to your computers even if you don’t have a server to maintain.) The second computer didn’t have the group at all, which perplexed me a bit.

However, that made me reasonably sure I could simply delete that group. I backed it up from the filesystem at the command line, just to make sure, and then deleted it with Workgroup Manager on the affected computer.

After that, logins for all Active Directory accounts I tried proceeded normally at the loginwindow on that system.

With the problem solved, I sought more information about the workings of the “com.apple.access_loginwindow” group. I confirmed that it is created when the “Allow network users to login in at login window”
option is turned on in System Preferences > Accounts > Login Options. This should be turned on by default, and that initial state results in no “com.apple.access_loginwindow” group at all.

Since the option is on by default, the really simple solutions to this kind of problem are:

  1. Don’t turn off the “Allow network users to login in at login window” option in System Preferences > Accounts > Login Options.

     

  2. If “Allow network users to login in at login window” has been turned off, either:
    1. delete the group named above, or
    2. toggle the option back on.

Deleting the “com.apple.access_loginwindow” group removes it completely and reinstates login capability for both local and network user accounts.

Toggling the System Preferences option back on, adds the “netaccounts” group to the “com.apple.access_loginwindow” group, reenabling login for both local and network users. It does not, however, remove the group “com.apple.access_loginwindow,” which remains on the system afterwards.

Here’s what that looks like in Workgroup Manager:

To prevent this on managed clients, I could see a system administrator proactively creating and managing the membership of the “com.apple.access_loginwindow” group. To ensure that managed clients bound to an Active Directory allow both local and network users to log in, make sure the group is populated with the appropriate nested groups: “localaccounts” and “netaccounts.”

Syndicate content