ptone http://ptone.com/dablog Hodgepodge of thoughts, technical notes, and random observations Mon, 22 Jun 2009 20:49:41 +0000 http://wordpress.org/?v=2.5 en Saving time (and wrists) scripting Illustrator http://ptone.com/dablog/2009/06/saving-time-and-wrists-scripting-illustrator/ http://ptone.com/dablog/2009/06/saving-time-and-wrists-scripting-illustrator/#comments Thu, 04 Jun 2009 19:04:11 +0000 ptone http://ptone.com/dablog/2009/06/saving-time-and-wrists-scripting-illustrator/ Sometimes doing tedious layout work in Illustrator is both time consuming and hard on the wrists. In this post I show a quick example of how one can script Illustrator very effectively

I work at a school where I’m involved in putting together our yearbook. For several years now we have done a collage of self portraits.

These can be a chore to layout in Illustrator. You want to randomize the grades, scale everything to be in nice rows and line everything up.

Here is what I started with:

Picture 46

Each grade is a different layer (and highlight color)

Its hard to know what size to standardize on to flow these out best onto the cover layout (its a front and back deal) Also I don’t want all of one grade next to another, so they need some shuffling.

So I whipped up this python script to do the grunt work for me.

quick digression on scripting language Note I could have done this in Applescript - and for most of you that would be easier to use. I think that for quick scripts where the bulk of the script is generating apple events - Applescript is generally easier. But as soon as you involve much general programming, Applescript is sorely lacking. It has an awkward and verbose syntax, and has a very limited standard library - so you often have to write all your own utilities. appscript is a Python module that provides access to Apples OSA system and allows you to do anything you can do in Applescript. The syntax takes some getting used to, but if you need to do other things in your script - its a win. For example the “Shuffle” bit in the code below was a single line of python, but would have required a PITA sub-routine in Applescript.

#!/usr/bin/env python
# encoding: utf-8
"""
Created by Preston Holmes on 2009-06-04.
"""

import sys
import os
import appscript
import random
import time
def main():
    il = appscript.app("Adobe Illustrator")
    page_items = il.current_document.page_items()
    random_index = range(len(page_items))
    random.shuffle(random_index)
    left_edge = 15
    curr_x = left_edge
    curr_y = 775
    x_max = 600
    row_height = 80
    print len(random_index)
    for i in random_index:
        item = page_items[i]
        if item.locked() or item.layer.locked():
            continue
        h = item.height()
        w = item.width()
        if h == 0 or w ==0:
            continue
        s = int ((row_height/h) * 100)
        # s2 = row_height/h
        if (curr_x + w) > x_max:
            curr_x = left_edge
            curr_y = curr_y - row_height
        sm = il.get_scale_matrix(horizontal_scale=s,vertical_scale=s)
        # Having some real problems with scale transforms screwing up stroke width - but I'm just working with placed items
        il.transform(item,using=sm,line_scale=100,transforming_stroke_patterns=False)
        # item.stroked_width.set(1)
        # alternate scale approach (also seems to affect stroke width…)
        # item.width.set(w*s2)
        # item.height.set(h*s2)
        left,top,right,bottom = item.geometric_bounds()
        dx = curr_x - left
        dy = curr_y - top
        m = il.get_translation_matrix(delta_x=dx,delta_y=dy)
        # can't use concatenated transform as scale will change the amount to move…
        # full_m = il.concatenate_scale_matrix(m,horizontal_scale=s,vertical_scale=s)
        il.transform(item,using=m)
        curr_x = curr_x + (right - left)

    

if __name__ == '__main__':
    main()

So what does this get me?

Picture 47

I made this to generate one page width - and its easy to split up across pages: Picture 48

And as you can see, it also shuffled the source layers: Picture 49

Now if I want to change how it flows - or add or subtract images I can just change the single row_height parameter of the script and get variations like this: Picture 50

Picture 51

If you realize an image needs 90 degree rotation, it no longer requires having to entirely re-layout the whole project

Of course there is still some massaging needed by hand - but it is a fraction of the effort.

]]>
http://ptone.com/dablog/2009/06/saving-time-and-wrists-scripting-illustrator/feed/
Two quick collage assistants http://ptone.com/dablog/2009/06/two-quick-collage-assistants/ http://ptone.com/dablog/2009/06/two-quick-collage-assistants/#comments Tue, 02 Jun 2009 22:39:22 +0000 ptone http://ptone.com/dablog/2009/06/two-quick-montage-assistants/ Despite vowing to stay away from Applescript as far as I can - sometimes its the fastest way to save a little time

At work I’m involved in laying out our yearbook. Honestly a lot of these pages are just quick photo montages. I’ve made these in both Illustrator and Pages and each have some advantages - I use mostly Illustrator but its harder to have others help in that app.

I wanted to whip up some scripts that could take some of the scale and random rotation repetition out of the process, which these do - the pages version also adds drop shadow - both could be improved to do some repositioning…

The Pages version:

tell application "Pages"

set docName to name of document 1
tell document 1
--mark images:

set imagelist to get every image -- of body text

set an_image to item 1 of imagelist
repeat with an_image in imagelist
set w to width of an_image
set h to height of an_image
if w > h then
set width of an_image to 3
else
set height of an_image to 3
end if
set x to random number from -15 to 15
set rotation of an_image to x
set shadow of an_image to true
set shadow angle of an_image to 58.0
set shadow color of an_image to {0, 0, 0}
set shadow offset of an_image to 7
set shadow opacity of an_image to 75
set shadow blur of an_image to 7

--get properties of an_image
end repeat

end tell
end tell

The Illustrator version:

tell application "Adobe Illustrator"
set s to 2
set maxdim to 250

set page_items to page items of current document

repeat with an_item in page_items
set w to width of an_item
set h to height of an_item
if w > h then
set s to maxdim / w
else
set s to maxdim / h
end if
set width of an_item to w * s
set height of an_item to h * s
set x to random number from -15 to 15
set baseMatrix to get rotation matrix angle x
transform an_item using baseMatrix
end repeat

end tell
]]>
http://ptone.com/dablog/2009/06/two-quick-collage-assistants/feed/
Will iTunes 8.2 lock out the Pre before it arrives? http://ptone.com/dablog/2009/06/will-itunes-82-lock-out-the-pre-before-it-arrives/ http://ptone.com/dablog/2009/06/will-itunes-82-lock-out-the-pre-before-it-arrives/#comments Tue, 02 Jun 2009 04:01:46 +0000 ptone http://ptone.com/dablog/2009/06/will-itunes-82-lock-out-the-pre-before-it-arrives/ The iTunes 8.2 update came out today with some curious details that, when you squint hard enough, suggest that Apple is trying to lock out the Palm Pre even before it launches.

John Gruber recently discussed the likely coming battle around Palm’s bold claims that it syncs with iTunes just like an iPod or iPhone.

Some details were turned up by the Mac IT community (users of a tool called Radmind, that have to consider how each new file effects plans to manage hundreds to thousands of Macs across an enterprise)

It was observed that the installer creates a new user and group named _usbmuxd

and a new, for now empty, folder at /private/var/db/lockdown

these alone might suggest something about locking down iTunes USB use - but another clue that this might have been added rather late is that the post install script that creates this user sloppily creates the user with a line of script:

$CMD -create $CMDGROUPS/_usbmuxd passwd '*'

This sets the password not to a literal asterisk, but to a shell glob/wildcard listing of /

root# dscl . -read /Users/_usbmuxd
AppleMetaNodeLocation: /Local/Default
GeneratedUID: FFFFEEEE-DDDD-CCCC-BBBB-AAAA000000D5
NFSHomeDirectory: /var/db/lockdown
Password: Applications Library Network System Users Volumes afs automount bin cores dev etc home mach_kernel mach_kernel.ctfsys net private sbin tmp usr var

Not that this password glitch is not likely a hole in however Apple plans to use this.

I haven’t yet tried watching these areas of the filesystem when I connect an Apple device to see if there is any interpretable activity in these areas - but that could certainly yield further clues.

UPDATE:

usbmux does in fact relate to iTunes communicating to devices over USB - these changes could also be about throwing more walls up in front of the jailbreakers - or of course these could be completely inconsequential.

]]>
http://ptone.com/dablog/2009/06/will-itunes-82-lock-out-the-pre-before-it-arrives/feed/
10.5.7 fails to update PPC machines properly http://ptone.com/dablog/2009/05/1057-fails-to-update-ppc-machines-properly/ http://ptone.com/dablog/2009/05/1057-fails-to-update-ppc-machines-properly/#comments Thu, 21 May 2009 23:37:07 +0000 ptone http://ptone.com/dablog/2009/05/1057-fails-to-update-ppc-machines-properly/ One of the things Leopard changed from Tiger was who could add printers. In Leopard only admins could manage printers - a change made apparently because schools were complaining that non-admins were adding printers they weren’t supposed to. But then it turned out that there were just as many cases where this caused a problem. Apple tried to fix this in 10.5.7 - but forgot to apply the fix completely for PPC machines.

First - more background on the change made from Tiger to Leopard:

From John DeTroye’s Tips and Tricks document

–Begin Quote from page 37

Printing

Note that the selection to allow users to modify the printer list applies to only 10.4 and below. This is due to a change in the Leopard printer system preferences to require local admin access to add/remove printers. Funny thing is, this came about because of all the schools screaming at us because the students and teachers kept adding printers all the time. Now those of you who didn’t care about that then are now screaming. So… if you would like your users to be able to add their own printers, you can make a change to a file on the client system. To fix this for now, until we get it fixed in a future update, you need to locate the “/etc/cups/cupsd.conf” file on your admin system and open it with TextWrangler (or use terminal and your favorite editor). Locate the line:

All administration operations require an administrator to authenticate…

Change the following lines to:

<Limit CUPS-Add-Modify-Printer CUPS-Delete-Printer CUPS-Add-Modify-Class
CUPS-Delete-Class CUPS-Set-Default>
   # AuthType Default
   # Require user @SYSTEM
   Require valid-user
   Order deny,allow
</Limit>

So the fix was to modify /private/etc/cups/cupsd.conf to allow anyone to add printers.

also documented here and here among other places

The 10.5.7 update adds a parental control for non-admin users that controls whether or not they can add printers Parental Controls

Apple also provides instructions to allow network users to manage printers

However these changes are enabled though modifications to /etc/authorization that are made not when the update is applied, but at the first boot.

the 10.5.7 installer installs:

/System/Library/LaunchDaemons/com.apple.su.startup.plist

which calls the installed:

/private/etc/com.apple.su.startup/su.startup shell script

which checks for securityd and then runs the tool

/private/etc/com.apple.su.startup/authsysprintadmin_

which, I’m assuming, is what makes the edits on first boot to /etc/authorization

however that tool seems to be intel only…

bash-3.2# /usr/bin/lipo -info /Volumes/OS-Build-09-05-15/etc/com.apple.su.startup/authsysprintadmin

Non-fat file: /Volumes/OS-Build-09-05-15/etc/com.apple.su.startup/authsysprintadmin is architecture: i386

I checked and it is intel only regardless of the architecture of the machine the updater runs on and changes to /etc/authorization are not made on PPC machines.

Not sure how this one get through QA?

The symptom on machines with managed printers is that users are presented with a “Type an administrator’s name and password to allow ManagedClient to make changes.” screen over the loginwindow. While you can move this window around you can’t click to type into it and you can’t click OK or Cancel. The login is stalled. Restarting does not fix.

Removing managed printers from the user or group will allow users to log in - but the only true fix is to copy a version of /etc/authorization from an updated intel machine to the affected PPC machines.

or make these changes:

From /etc/authorization in 10.5:

<key>system.print.admin</key>
<dict>
    <key>class</key>
    <string>rule</string>
    <key>k-of-n</key>
    <integer>1</integer>
    <key>rule</key>
    <array>
        <string>is-lpadmin</string>
        <string>is-admin</string>
        <string>default</string>
    </array>
</dict>

To /etc/authorization in 10.5.7:

<key>system.print.admin</key>
<dict>
    <key>allow-root</key>
    <true/>
    <key>class</key>
    <string>user</string>
    <key>group</key>
    <string>lpadmin</string>
    <key>shared</key>
    <true/>
</dict>
]]>
http://ptone.com/dablog/2009/05/1057-fails-to-update-ppc-machines-properly/feed/
Reducing size of iPhoto POI database http://ptone.com/dablog/2009/04/reducing-size-of-iphoto-poi-database/ http://ptone.com/dablog/2009/04/reducing-size-of-iphoto-poi-database/#comments Wed, 29 Apr 2009 21:02:45 +0000 ptone http://ptone.com/dablog/2009/04/reducing-size-of-iphoto-poi-database/ This is probably only of interest to those of you that have to deploy iPhoto over a slower (ie wireless) network.

updated with corrections from comments

The main application package for iPhoto 8 is over 300mb - of that 100+ is a Points of interest database, that contains information in many languages about various points on the globe.

You can remove the non-english language stuff - and so far I have found no ill effects on an english system.

Be warned that this could brake future iPhoto updates

cd /Applications/iPhoto.app/Contents/Resources/
cp PointOfInterest.db PointOfInterest_original.db 
sqlite3 PointOfInterest.db
sqlite> .read /path/to/droplanguages.sql

Where droplanguages.sql contains:

DROP TABLE GeoLookup_da;
DROP TABLE GeoLookup_de;
DROP TABLE GeoLookup_es;
DROP TABLE GeoLookup_fi;
DROP TABLE GeoLookup_fr;
DROP TABLE GeoLookup_it;
DROP TABLE GeoLookup_ja;
DROP TABLE GeoLookup_ko;
DROP TABLE GeoLookup_nl;
DROP TABLE GeoLookup_no;
DROP TABLE GeoLookup_pl;
DROP TABLE GeoLookup_pt;
DROP TABLE GeoLookup_ptbr;
DROP TABLE GeoLookup_ru;
DROP TABLE GeoLookup_sv;
DROP TABLE GeoLookup_zh;
DROP TABLE GeoLookup_zhtw;
DELETE FROM GeoPlaceNames WHERE language!="en";
VACUUM;

This reduces the POI database to a lean 12MB

To exit sqlite just type .exit on a line by itself at the sqlite3 prompt

]]>
http://ptone.com/dablog/2009/04/reducing-size-of-iphoto-poi-database/feed/
Screen Juggling http://ptone.com/dablog/2009/04/screen-juggling/ http://ptone.com/dablog/2009/04/screen-juggling/#comments Thu, 09 Apr 2009 17:44:00 +0000 ptone http://ptone.com/dablog/2009/04/screen-juggling/ Ever since I had a Powerbook Duo back in the day - I’ve made a laptop my primary machine. There has never been a sync solution that I have found adequate - and with my current AL macbook - I’m happy with the performance and memory. The only shortcomming is the screen as you can never have too much (spaces help - but its not a solution). At my desk at work I have an older eMac that serves as sort of a utility workhorse (encoding/archiving DVDs, backup ARD admin terminal, iTunes jukebox etc). I’ve always sought a way to make good use of the extra screen. In the beginning I was just using Apple Remote Desktop, but this was always a little klunky as it required switching away from whatever I was doing. I’ve not found a couple pieces that when put together result in close to a perfect setup.

First a while back I discovered teleport - which is a fantastic piece of donationware that allows you to have a network based software KVM switch. This let me arrange the emac screen on the edge of my laptop screen and switch to its screen just by moving my mouse to the edge - this works almost a little too well, luckily you can set a slight delay.

teleportconfig

With teleport I was able to quickly pull up website based docs, and generally continue to use the eMac as the utility computer. But I wasn’t able to use the screen as extra real estate directly for tasks I was doing on my laptop.

Enter ScreenRecycler - which is basically a combination of a video driver that turns a VNC session as a phantom external monitor, and a fast VNC server to run on the other machine. You run the included JollyFast VNC on the second computer, and the app/Driver on your primary computer and you can set up and use the second computer as an extra screen. The refresh rates are pretty good - and in every way it acts like a second monitor.

screenrecycler

Now when you move your mouse into the second virtual screen - you have no control over the machine that is hosting that extra video - remember your computer only thinks its an extra screen - however you can combine teleport and ScreenRecycler for the best of both worlds. In fact you can arrange the second computer (in my case the eMac) in teleport, to be located off the edge of the same computer’s virtual screen - so that while your mouse is on that screen, you can move between it being a second monitor, and a second computer pretty easily.

comboconfig

Now the only problem for me with this, is getting used to all that extra screen for when I do projects away from my desk.

Some additional observations:

  • teleport has nice builtin pasteboard transfer, and can do file drag and drop
  • screen some applications won’t deal gracefully when your second (virtual) monitor goes away and you could strand some things on the second monitor - but more often when you close screenrecycler, everything gets rudely shoved back onto your primary display, which happens to me at the end of the day.

desk

]]>
http://ptone.com/dablog/2009/04/screen-juggling/feed/
Managing Macs with centralized login scripts http://ptone.com/dablog/2009/03/managing-macs-with-centralized-login-scripts/ http://ptone.com/dablog/2009/03/managing-macs-with-centralized-login-scripts/#comments Thu, 19 Mar 2009 17:58:16 +0000 ptone http://ptone.com/dablog/2009/03/managing-macs-with-centralized-login-scripts/ Managing a large number of Macs at an institution often requires a hodgepodge of tricks and tools. While Apple packages some useful stuff os OS X server, many of the most useful things are hidden, created by the community, or rely on OS X’s UNIX underpinnings. One of the tricks that I came up with a couple years back that I have found invaluable, is a system where by I can create and manage a set of scripts on a server, that get run on each client at login. Read on for details and some snippets.

First some prerequisites and expectations. You will need a server on your network that supports sharing volumes to OS X, and a way to configure your clients to automount such a sharepoint. Both of these are easily achieved if you have an OS X server that your clients are binding to - but there are other ways. Also I assume that you have some familiarity with what shell scripts are, and file permissions, if not more of a command on how to write them.

At its most basic level, a system of login scripts relies on a single hook provided by the system. In the plist at:

/var/root/Library/Preferences/com.apple.loginwindow.plist

<key>LoginHook</key>
<string>/path/to/script</string>

This just is the path to an executable script that will be run with root privileges at user login. The script is passed a single argument, which is the name of the logging in user.

In this script you can do any number of things you can do in a script - it runs with root privileges (I’ll outline some of those things later on).

Now if you start doing a lot of things in this script, or you want to do some things on some machines but not others, managing the contents of this script can get tricky or tedious. The solution here is the solution for many similar situations. Modularize it into a set of separate scripts. This has the advantage of atomizing different purposes into their own files that can be moved and reused, or even downloaded from other sources and added into your process. It also allows you to use scripts written in multiple languages. You may have some things that are best done in Bash, others that are better done with Python or Perl.

The basic way to modularize this is to have a folder of scripts, and then your login script pointed to by the plist key acts as a wrapper that might look like this:

#! /bin/sh

export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin

DIR="/etc/loginscripts"

if [ -d ${DIR} ]; then
    for script in ${DIR}/*; do
    # if the file exists and is executable
    if [ -s ${script} -a -x ${script} ]; then
        # run the item
        ${script} $*
        exit_value=$?
        # bail if any sub script returns abnormally
        if [ ${exit_value} -ne 0 ]; then
        logger -s -t Loginscript -p user.info ${script} failed! 1>&2
        exit $exit_value
        fi
    fi
    done
fi

echo Loginscript complete.
exit 0

This basically loops over the directory specified (in this case /etc/loginscripts/) and runs each script in turn - passing along whatever original arguments were sent to the primary loginscript.

This system has been used by several management packages, including both NetRestore, and Radmind/iHook.

This is as good a time as any for a quick digression on iHook. iHook is a fantastic tool that allows you to provide graphical feedback of scripts that are running. If your login scripts are doing things that might take some time - there is no built in way to give the user feedback. iHook provides a UI window that scripts can write their output to (echo) or using iHook’s special commands, can update a progress bar or put up images.

I’m not going into the details here on iHook, but I use a modified version of the multiple scripts system that is distributed with the Radmind Assistant

The new (though I’m not claiming I’m the first/only one to think of or implement this) idea I want to introduce in this post is to take the modular approach another step and use a network folder to hold the scripts to be run at login.

That is, the login script first fetches all the scripts to run from a server, then runs them all locally.

The advantages of this are huge - mainly you no longer have to foresee at imaging time all the possible things you may want to do at login with a script, in order to have those actions written into scripts that are distributed as part of your image. The advantage over via ARD for taking such late breaking actions is that there is no issue if you have many laptops or machines that are often offline on your network, the commands are run at next login.

For me one of the best examples is printer creation. In the Tiger days I dutifully tried to deploy MCX printing - but had any number of problems with it, but primarily:

  • Printers that I removed from the list, were not removed from the clients
  • Many printer specific features could not be managed via MCX

Now I create printers with a script at login time - if we add or remove a printer from that list, I can make that change to a single copy of the script on the server, and all the machines who participate in this system will see the changes at login.

Another example is that midway through the school year, the administration asked that cameras on the Macs be disabled for students because they were posting to YouTube from campus. I was able to add in a script that did this to the folder on the server and poof, it was implemented instantly on all the machines at next login.

I’m using a combination of locally and remotely modular loginscripts that look something like this:

  • login.hook (starts iHook with the contents of the wrapper script as above )
    • locally defined login scripts are each run from /etc/hooks
    • one of these scipts then fetches additional scripts from the server.
      • each local copy of the remotely defined scripts is in turn run

The script that fetches and runs the remote scripts looks like this:

if [ -d /Network/Library/management08/ ]; then
        echo Running Centralized Management
        mkdir /tmp/manage
        cp /Network/Library/management08/scripts/* /tmp/manage
        chmod 755 /tmp/manage/*
        for x in /tmp/manage/*
        do
                $x $*
        done
        rm -f /tmp/manage/*
fi
exit 0

I’m using OS X’s server ability to define an automount at /Network/Library as this is in the search path for several other apps/technology that look in various Libraries. Obviously you want to make sure that such a location is read-only.

OK, so what are some of the useful things you can do with login scripts (local or remote)? Here is a collection of some snippets (see also Mike Bombich’s collection for more ideas). Remember these are snippets and should not just be pasted en-mass into one of your login scripts. Let me know of your own tricks in the comments! (and yes - I know many of these can also be done via MCX - I do use MCX, but sometimes this seems more straightforward to me, and generally works more predictably)

# set a user’s preference (in this case disabling perian update notices)
sudo -u $1 defaults write org.perian.Perian NextRunDate -date ‘4000-12-31 16:00:00 -0800′

# install that cool little tool you didn’t know about when making your image
# http://duti.sourceforge.net/
if [ ! -e /usr/local/bin/duti ]; then
    ditto /Network/Library/management08/files/duti/usr/local/ /usr/local/
fi

# then use that cool little tool to set quicktime player (not itunes) as the default player for .wav files
# that are generated by that new phone system ;-)
echo ‘com.apple.quicktimeplayer com.microsoft.waveform-audio all’ | sudo -u $1 /usr/local/bin/duti
# This grabs the user’s home directory path
user="$1"
input=`dscl localhost read Search/Users/$user NFSHomeDirectory`
nethomedir=${input:18}


# so you can then do things like disable the "Sharing" section of the sidebar

sudo -u $1 /usr/libexec/PlistBuddy -c "Set :networkbrowser:CustomListProperties:com.apple.NetworkBrowser.bonjourEnabled bool True" $nethomedir/Library/Preferences/com.apple.sidebarlists.plist
sudo -u $1 /usr/libexec/PlistBuddy -c "Set :networkbrowser:CustomListProperties:com.apple.NetworkBrowser.backToMyMacEnabled bool False" $nethomedir/Library/Preferences/com.apple.sidebarlists.plist
sudo -u $1 /usr/libexec/PlistBuddy -c "Set :networkbrowser:CustomListProperties:com.apple.NetworkBrowser.connectedEnabled bool False" $nethomedir/Library/Preferences/com.apple.sidebarlists.plist


# delete all printers and settings:
 # Clean up from previous script if needed
rm -f /tmp/print
    

# Set user variable
user="$1"
# This grabs the user’s home directory server
input=dscl localhost read Search/Users/$user NFSHomeDirectory | head -1
nethomedir=${input:18}

### Delete Current Printers
# From the 10.4 PrintingReset.sh script inside print utility
killall "PrinterProxy" "Printer Setup Utility" "cupsd" 2>/dev/null

# Give them all a chance to die
sleep 1

rm -rf $nethomedir/Library/Printers/
.app
rm -rf $nethomedir/Library/Preferences/com.apple.print.
rm -rf $nethomedir/Library/Preferences/ByHost/com.apple.print.

rm -rf /etc/cups/printers.conf*
rm -rf /etc/cups/classes.conf*
rm -rf /etc/cups/ppds.dat
rm -rf /etc/cups/ppds/* 2>/dev/null

launchctl start org.cups.cupsd

# set up printers using lpadmin:
#HP socket example
lpadmin -p "science_laser" -L science -D "Science B&W (D)" -E -v socket://10.5.5.36/?bidi -P "/Library/Printers/PPDs/Contents/Resources/HP LaserJet 2200.gz" -o Duplex=DuplexNoTumble

# Using airport/bonjour connected printer:
lpadmin -p "laser_90299" -L 102 -D "Preschool Laser" -E -v mdns://90299._pdl-datastream._tcp.local./?bidi -P "/Library/Printers/PPDs/Contents/Resources/HP LaserJet 1200.gz"

# Using LPD
lpadmin -p "tower_copier" -L Tower -D "Tower Copier" -E -v lpd://10.5.5.46/ -P "/Library/Printers/PPDs/Contents/Resources/en.lproj/Kyocera KM-C4035E.PPD"

# run some stuff only every 5 days - not every login:
tfile=/Library/Management/.timestamp
if [ ! -e $tfile ]; then
    touch -t 200811010000 $tfile
fi
r=`find $tfile -mtime +5`
if [ ! -z $r ]; then
    #do periodic stuff here
    touch $tfile
fi
# fix the poor network performance on the netgear WAPs on the third floor
MODEL=`system_profiler SPHardwareDataType | grep ‘Model Name’ | awk ‘{print $3}’`
echo $MODEL

if [ $MODEL == "MacBook" ]; then
    sysctl -w net.inet.tcp.delayed_ack=0
fi
#disable iSight if not staff member
ISSTAFF=`dseditgroup -n /Search -m $1 -o checkmember srstaff | awk ‘{print $1}’`

if [ "$ISSTAFF" == "yes" ]; then
    chmod 755 /System/Library/QuickTime/QuickTimeUSBVDCDigitizer.component/Contents/MacOS/QuickTimeUSBVDCDigitizer /System/Library/PrivateFrameworks/CoreMediaIOServicesPrivate.framework/Versions/A/Resources/VDC.plugin/Contents/MacOS/VDC
else
    echo ‘Camera Disabled’
    chmod 700 /System/Library/QuickTime/QuickTimeUSBVDCDigitizer.component/Contents/MacOS/QuickTimeUSBVDCDigitizer /System/Library/PrivateFrameworks/CoreMediaIOServicesPrivate.framework/Versions/A/Resources/VDC.plugin/Contents/MacOS/VDC
fi
# run a full os update:
osversionlong=`sw_vers -productVersion`
osvers=${osversionlong:5:1}
if [ $osvers -eq 4 ]
then
        MSG=‘Updating to 10.5.6 this takes about 5 minutes’
        echo $MSG
        #install updates
        hdiutil attach /Network/Library/management08/files/installers/MacOSXUpdCombo10.5.6.dmg
        echo $MSG
        installer -package ‘/Volumes/Mac OS X Update Combined/MacOSXUpdCombo10.5.6.pkg’ -target /Volumes/Internal
        shutdown -r now
fi
]]>
http://ptone.com/dablog/2009/03/managing-macs-with-centralized-login-scripts/feed/
setting up denyhosts to block ssh attacks on Leopard http://ptone.com/dablog/2009/03/setting-up-denyhosts-to-block-ssh-attacks-on-leopard/ http://ptone.com/dablog/2009/03/setting-up-denyhosts-to-block-ssh-attacks-on-leopard/#comments Wed, 11 Mar 2009 21:08:00 +0000 ptone http://ptone.com/dablog/2009/03/setting-up-denyhosts-to-block-ssh-attacks-on-leopard/ Deny hosts is a clever python script that will monitor your ssh log file for repeated failed login attempts, and then add the offending hosts to a system blacklist. While you can disable ssh entirely, or move it to a different port, there are reason you may want to keep it available and on a standard port and this tool will help keep the bad guys out.

  • download and unpack tarball
  • su as root
  • cd to the unpacked distribution folder and enter the following in terminal:

    python setup.py install
    touch /etc/hosts.deny
    cp /usr/share/denyhosts/denyhosts.cfg-dist /usr/share/denyhosts/denyhosts.cfg
    cp /usr/share/denyhosts/daemon-control-dist /usr/share/denyhosts/daemon-control
    chmod 700 /usr/share/denyhosts/daemon-control
    

read on for configuration

Configure Script

These are the MINIMAL settings for OS X. There are lots of other good options in there including some settings about thresholds and purging, and emailing an admin when a attacker is added to the list. There is also a feature that lets you share your list of attackers with the community, as well as download the community generated blacklist. Using your favorite text editor or the built in pico change the following settings:

pico /usr/share/denyhosts/denyhosts.cfg

look for and change the following:

    SECURE_LOG = /var/log/secure.log
    LOCK_FILE = /tmp/denyhosts.lock

(strictly speaking - you don’t need to do this next part as we will be starting the script with launchd)

pico /usr/share/denyhosts/daemon-control

look for and change the following:

    DENYHOSTS_BIN   = "/usr/local/bin/denyhosts.py"
    DENYHOSTS_LOCK  = "/tmp/denyhosts.lock"

to test out the daemon you can execute the following:

    /usr/bin/env python /usr/local/bin/denyhosts.py --daemon --config=/usr/share/denyhosts/denyhosts.cfg

To launch the monitoring script automatically

create and then edit a launchd plist

    pico /Library/LaunchDaemons/denyhosts.daemon.plist

and then paste the following:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/
    PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
            <key>Label</key>
            <string>denyhosts.daemon</string>
            <key>ProgramArguments</key>
            <array>
                    <string>/usr/bin/env</string>
                    <string>python</string>
                    <string>/usr/local/bin/denyhosts.py</string>
                    <string>--daemon</string>
                    <string>--config=/usr/share/denyhosts/denyhosts.cfg</string>
            </array>
            <key>RunAtLoad</key>
            <false/>
    </dict>
    </plist>

then to load the plist without logging out or restarting:

    launchctl load /Library/LaunchDaemons/denyhosts.daemon.plist

try it out - try logging in as a fake user/bad pw and you should see your IP listed in “/etc/hosts.deny”

simply remove a host from that file if you want to gain access again (or see the configuration notes about purging)

]]>
http://ptone.com/dablog/2009/03/setting-up-denyhosts-to-block-ssh-attacks-on-leopard/feed/
Voice to OmniFocus, revisited http://ptone.com/dablog/2009/03/voice-to-omnifocus-revisited/ http://ptone.com/dablog/2009/03/voice-to-omnifocus-revisited/#comments Mon, 09 Mar 2009 17:29:44 +0000 ptone http://ptone.com/dablog/2009/03/voice-to-omnifocus-revisited/ back when Jott was free I was one of several people who were using it to get quick voice notes into OmniFocus (OF). A couple of limitations of the Jott solution was that:

  • It sometimes had trouble understanding the recipient of “who I wanted to Jott”
  • You never got a chance to proofread or edit the transcription before it got sent

There are just times when I think of something on the go, and firing up the iPhone OF app and tapping it in won’t work, so when a friend told me about vlingo for iPhone and twitter, I thought - hmm I’ve been playing a bit with the twitter API and that might be a good route for me to get stuff from voice into OF.

So here are the steps (updated with more detail):

  • Set up a new twitter account - make it “private” (assuming you don’t want to world to see your tasks)
  • Set up vlingo to post status updates to this twitter account (obvious caveat, you won’t be able to use vlingo for your regular twitter account…)
  • download the python script (Click on the link, then do a save as, from here we will assume its on your Desktop, but it is quite likely you may want to keep it somewhere else)
  • Rename it oftwitter.py (mind os x’s habit of hiding extensions…)
  • open the script in any text editor and set the username and password settings to those of your new account
  • open terminal and type the following lines (you will need your admin password)

    chmod +x ~/Desktop/oftwitter.py
    sudo easy_install appscript
    sudo easy_install twitter
    

you will get a warning about a C extension not being compiled, but you can ignore that.

  • Now assuming you have already logged some tweets to the twitter account, try it out by typing the following into terminal:

    ~/Desktop/oftwitter.py
    

You should see your tweets as items in your OmniFocus inbox

The script creates a preference file stored at ~/Library/Preferences/com.ptone.oftwitter.plist that stores the most recent tweet and uses that to query twitter more effectively.

to set up automated retrieval of new tweets install Lingon and configure a new launch agent to look something like this:

Oftwitterlingon

Now when you are out and about you can fire up vlingo on the iPhone and say: “twitter, order new engine for rocket ship”, you get the chance to fix any quick errors, then update your status. I’m finding it even easier to just hit the “social” button in vlingo, then you don’t have to say “twitter” - just your task.

When you return to your Mac - it should shortly appear in your OF inbox. This script assumes you keep OmniFocus open

A couple other goodies:

  • If you say “important” or “flag”, that word will be stripped, and the entry will be flagged in OF
  • you can send a @reply message to the twitter account and replies are checked too (with the leading @user stripped from the start)
    • This is handy if you want to give someone a way to submit tasks to your inbox, they can just send a tweet to your new account

To remove all traces:

  • delete the oftwitter.py script file
  • delete ~/Library/Preferences/com.ptone.oftwitter.plist
  • delete /Library/Python/2.5/site-packages/appscript*
  • delete /Library/Python/2.5/site-packages/simplejson
  • delete /Library/Python/2.5/site-packages/twitter
]]>
http://ptone.com/dablog/2009/03/voice-to-omnifocus-revisited/feed/
Time Machine: poor man’s version control http://ptone.com/dablog/2009/02/time-machine-poor-mans-version-control/ http://ptone.com/dablog/2009/02/time-machine-poor-mans-version-control/#comments Tue, 24 Feb 2009 01:09:52 +0000 ptone http://ptone.com/dablog/2009/02/time-machine-poor-mans-version-control/ There are a has been a number of version control systems en vogue over time, CVS, SVN, Git etc.

I try to keep up with them, use them where possible, but don’t put EVERYTHING I do in version control. Since I am on a Mac running Leopard, I do have, and use Time Machine and so wanted to see if it would be pretty easy to use that to do some quick diffs between some source files.

The result is a quick and dirty python script. In the unlikely event that I ever have time, this would be a cool pyObjC project, a file browser panel, date versions picker, and a webkit view (with some better css).

The source to the script is below the fold - it will look for the first attached volume that has time machine backups for the current machine. It will not work with network based time machine backups that are on disk images. This script will run on a stock Leopard install without any extra python modules needed.

#!/usr/bin/env python
# encoding: utf-8
"""
Created by Preston Holmes on 2009-02-23.
Copyright (c) 2009 __MyCompanyName__. All rights reserved.
"""

import sys
import os
import getopt
import difflib
import time
import pdb
from subprocess import Popen, PIPE

# if you set time_machine_path, it should be to the full path of this machines backup drive:
# ie '/Volumes/TM_Drive/Backups.backupsdb/Joes-Mac/'
# if not set explicitly - the script will use the first TM drive it finds, 
# and the first host folder it finds - which should work for most cases
# Will Not work with network or disk image based time machine backups

time_machine_path = None

cmd = 'tell application "Finder" to name of (path to startup disk)'
boot_volume = Popen('osascript -e \'%s\'' % cmd,shell=True,stdout=PIPE,stderr=PIPE).communicate()[0][0:-1]



help_message = '''
Call this script with the path to one or more text based files as arguments
'''
header = '''

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html>

<head>
    <meta http-equiv="Content-Type"
          content="text/html; charset=ISO-8859-1" />
    <title></title>
    <style type="text/css">
        table.diff {font-family:Courier; border:medium;}
        .diff_header {background-color:#e0e0e0}
        td.diff_header {text-align:right}
        .diff_next {background-color:#c0c0c0}
        .diff_add {background-color:#aaffaa}
        .diff_chg {background-color:#ffff77}
        .diff_sub {background-color:#ffaaaa}
    </style>
</head>

<body>
'''
footer = '''
    <table class="diff" summary="Legends">
        <tr> <th colspan="2"> Legends </th> </tr>
        <tr> <td> <table border="" summary="Colors">
                      <tr><th> Colors </th> </tr>
                      <tr><td class="diff_add">&nbsp;Added&nbsp;</td></tr>
                      <tr><td class="diff_chg">Changed</td> </tr>
                      <tr><td class="diff_sub">Deleted</td> </tr>
                  </table></td>
             <td> <table border="" summary="Links">
                      <tr><th colspan="2"> Links </th> </tr>
                      <tr><td>(f)irst change</td> </tr>
                      <tr><td>(n)ext change</td> </tr>
                      <tr><td>(t)op</td> </tr>
                  </table></td> </tr>
    </table>
</body>

</html>
'''
class Usage(Exception):
    def __init__(self, msg):
        self.msg = msg

def find_versions(path):
    #pdb.set_trace()
    print 'looking for versions of %s' % path
    #print time.strftime("%m/%d/%Y %I:%M:%S %p",time.localtime(os.path.getmtime(fname)))
    backups = os.listdir(time_machine_path)
    backups.sort()
    pathlist = [os.path.join(time_machine_path,b,boot_volume,path[1:]) for b in backups]
    pathlist.append(path)
    versions = []
    mod_times = []
    for f in pathlist:
        if os.path.exists(f):
            mod_time = time.localtime(os.path.getmtime(f))
            if not mod_time in mod_times:
                mod_times.append(mod_time)
                versions.append({mod_time:f})
    return versions
    
def getTMLocation():
    global time_machine_path
    if time_machine_path and os.path.exists(time_machine_path):
        return True
    volumes = os.listdir('/Volumes')
    #hostname = os.uname()[1].split('.')[0]
    #cmd = 'scutil –get ComputerName'
    #machine_name = Popen('osascript -e \'%s\'' % cmd,shell=True,stdout=PIPE,stderr=PIPE).communicate()[0][0:-1]
    for v in volumes:
        if os.path.exists(os.path.join('/Volumes',v,'Backups.backupdb')):
            backupsdb = (os.path.join('/Volumes',v,'Backups.backupdb'))
            time_machine_path = os.path.join(backupsdb,os.listdir(backupsdb)[0])
            return True
            # candidate_path = os.path.join('/Volumes',v,'Backups.backupdb',machine_name)
            # if os.path.exists(candidate_path):
            #     time_machine_path = candidate_path
            #     return True
    return False
    
def main(argv=None):
    
    if not getTMLocation():
        print 'No Time Macine Backup Found'
        sys.exit()
    print 'time machine path: ' + time_machine_path
    if argv is None:
        argv = sys.argv
    try:
        try:
            opts, args = getopt.getopt(argv[1:], "ho:v", ["help", "output="])
        except getopt.error, msg:
            raise Usage(msg)
    
        # option processing
        for option, value in opts:
            if option == "-v":
                verbose = True
            if option in ("-h", "–help"):
                raise Usage(help_message)
            if option in ("-o", "–output"):
                output = value
        if not args:
            raise Usage(help_message)
        differ = difflib.HtmlDiff(tabsize=4)
        html = header
        for path in args:
            path = os.path.join(os.getcwd(),path)
            html += '<h1>Changes for %s</h1' % os.path.basename(path)
            versions = find_versions(path)
            if len(versions) < 2:
                html += '<h2>Less than 2 Versions found on Time Machine Backup</h2>'
            else:
                html += '<h2>%s versions found</h2>' % len(versions)
            for i in range(0,len(versions)):
                if i: #skip the first
                    d1 = time.strftime("%m/%d/%Y %I:%M:%S %p",versions[i-1].keys()[0])
                    d2 = time.strftime("%m/%d/%Y %I:%M:%S %p",versions[i].keys()[0])
                    html = html + '<h2>changes from %s to %s</h2>' % (d1,d2)
                    l1 = open(versions[i-1].values()[0]).readlines()
                    l2 = open(versions[i].values()[0]).readlines()
                    table = differ.make_table(l1,l2,context=True,numlines=3)
                    html += table
        html += footer
        o = open('/tmp/diff.html','w')
        o.write(html)
        o.close()
        import webbrowser
        webbrowser.open('/tmp/diff.html')
    except Usage, err:
        print >> sys.stderr, sys.argv[0].split("/")[-1] + ": " + str(err.msg)
        print >> sys.stderr, "\t for help use –help"
        return 2


if __name__ == "__main__":
    sys.exit(main())
]]>
http://ptone.com/dablog/2009/02/time-machine-poor-mans-version-control/feed/