Radmind transcripts with symlinks will be damaged when edited in the Radmind Transcript Editor. I have confirmed this with RTE version 0.7.7 used in conjunction with the version 1.13.0 Radmind command line tools.
The problem appears to be an interaction between RTE 0.7.7 (which is old) and newer Radmind tools, according to posts on the Radmind-Users mailing list. It apparently relates to any version of the Radmind tools greater than 1.12.0, which introduced symlink ownership, when used in combination with RTE 0.7.7. This issue is in the Radmind bug report tracker and has been fixed in the CVS version of RTE. To use that newer version of RTE, you have to build the GUI tools from CVS.
You only see the problem — assuming you are using the right combination of versions — if you edit and save a transcript or create a new transcript within RTE (either by drag and drop or the “Add Item to Transcript” command). So, using the RTE to simply view the transcript file — and then editing with a different editor (which is an inconvenience) — is a workaround.
To get a count of the affected transcripts (on your Radmind server), use the following command:
You can simplify the grep search to only return the path of each match, and then process that with Awk to get just the basename of the file. Here’s how to use that technique to get the list of affected transcript files on a Radmind client:
As for actually fixing the damaged transcripts, it appears that the best way to do so is to recreate them from scratch.
Nigel and Jeff present Mac OS X Laptop Deployments with Puppet in the MacIT track at Macworld Expo 2009. They are two of the first Mac system administrators I knew of using Puppet, and both had a background in Radmind.
I’ve been reading through James Turnbull’s Pulling Strings with Puppet, since our library had a copy. I had hoped to get through it during our winter break, but illness and other factors (no Puppet pun intended) conspired to get in the way. From what I’ve read about it already, Puppet is clearly interesting. Nigel was very enthusiastic about it when we talked at WWDC 2008.
To me, it seems that it would take some effort to model what you want in it and build up a repository of what you want managed. Perhaps I’m feeling like an old dog trying to learn new tricks. Grin.
One point that Nigel and Jeff made in their presentation slides that struck me is that they needed a solution that works when offline, which Puppet does. Radmind can work offline but I daresay that’s not the way that most people would think to use it (lapply with its “-n” flag would be the most basic change).
Kyle also mentioned to me that he’s been using Puppet in conjunction with Radmind. I believe he has Puppet managing configurations and Radmind managing the bulk of the filesystem.
You can find out if an SSL certificate has expired with the command below. I’ve found it useful to be able to check for expired certificates in my use of Radmind, where you can uniquely identify clients to the server with them.
$ openssl x509 -in /path/to/cert.pem -noout -checkend 0
I mention this command primarily because I reviewed the the OpenSSL x509 man page (“man x509”) that comes with Mac OS X Leopard, and it didn’t show the “checkend” option for the command. That was odd, because that option was just what I needed.
I did, however, find it documented in the usage statement-style help for the command:
$ openssl x509 --help
In that usage statement, the “checkend” option is described (with little punctuation) as a way to “check whether the cert expires in the next arg seconds [sic] exit 1 if so, 0 if not.” So, using zero seconds shows you if the certificate has already expired, while an integer greater than zero will show if it will expire in the future. No matter how many seconds you check against, you must examine the results from the exit code (the “$?” shell variable) to see if the certificate is or has expired.
I find this is tremendously useful knowledge when dealing with certificates in Radmind, where an expired certificate can mean the failure of a client to connect to the Radmind server. It could be beneficial in other circumstances, of course — but I don't have those circumstances.
Taking this further, you could check for certificate expiration on a Radmind server — if your certificates are stored in the Radmind special directory for each hostname of a managed client. (Substitute one of your own managed clients’ hostnames for “hostname” in the path below.)
$ openssl x509 -in /var/radmind/special/hostname/private/var/radmind/cert/cert.pem -noout -checkend 0
Since you can do it for one client certificate, you could also loop through all of the certificates on a Radmind server. In this example, I’ll continue to use the path of /var/radmind even though, on Mac OS X, I’d generally prefer to specify the full /private/var/radmind; your Radmind server may not be on Mac OS X even if your clients are. Also, you may need to modify the “depth” parameter on your search to accommodate the paths on your server. Finally, I’ll change the “checkend” parameter to 604800, for seven days (60*60*24*7=604800). That produces something along the lines of:
Change the last line to “done | grep expiring” if you only want to see the expiring certificates.
It’s great to get just the CN of the certificate in these circumstances, since it’s likely you’ll want to act on just those that need attention. One way to do this relatively cleanly is to use OpenSSL x509’s “subject” and “nameopt” options, and then parse the output. Below, I’ll use awk for that. (Again, substitute one of your own managed clients’ hostnames for “hostname” in the path below.)
$ openssl x509 -in /private/var/radmind/special/hostname/private/var/radmind/cert/cert.pem -noout -subject -nameopt sep_multiline | awk '/CN/ {split($1,elements,"=") ; print elements[2] ;}'
Beyond checking for expiration on the server, it may be valuable to do so in your Radmind client scripts, especially if you favor SSL connections. If you find an expired certificate, you can take some remedial action right away that might allow the client to communicate with the server.
I thought about this a while, and the easiest way I came up with — after having already developed more complex logic — was to simply rename or remove the expired certificate from its normal path. Then, allow the client to connect with another authorization level where the client certificate is unnecessary. (Use of a client certificate implies Ramind’s “-w2” authorization level, while a lesser level would mean you’re performing hostname/DNS rather than certificate verification.) This would probably mean you have multiple Radmind server processes running, each on its own port, to accept such incoming requests on the server.
Under normal circumstances, the latest Radmind tools that communicate with the server report client status updates in the Radmind server’s system log. These standard messages can include ones like:
May 8 03:14:56 RadmindServerHost radmind[7890]: report radmind-client.example.com 192.168.7.42 - - ktcheck No updates needed
May 15 03:15:25 RadmindServerHost radmind[24531]: report radmind-client.example.com 192.168.7.42 - - ktcheck Updates retrieved
May 15 03:21:48 RadmindServerHost radmind[24534]: report radmind-client.example.com 192.168.7.42 - - lapply Changes applied successfully
May 15 03:31:07 RadmindServerHost radmind[24356]: report radmind-client.example.com 192.168.7.42 CertificateCN - lapply Error, changes made
The Radmind repo, or “report,” tool provides the ability to send arbitrary messages to the Radmind server process. But how are these messages formatted and sent?
$ repo -e "Debug" -h radmindserverhost.example.com -w2 "Test message"
… results in the system log message:
May 15 03:31:56 RadmindServerHost radmind[25236]: report radmind-client.example.com 192.168.7.42 CertificateCN - Debug Test message
Here, we can see that an entry created with repo looks like the standard Radmind log messages above. The client hostname and IP address are reported after the “report” text. The CertificateCN for the client — if the highest authorization level is specified (with the -w2 flag) — is also listed; if not, a dash takes its place. I haven’t seen a case where the second dash is substituted, however.
Finally, where the Radmind command/tool used would normally be, the “event” specified by repo will printed. After that, the message text appears.
The value proposition is that if you’re using Radmind, the repo command can help you send arbitrary messages to the server for logging. As bonus, if you've taken the time and effort to build the certificate infrastructure for Radmind, you can send these messages securely between the clients and the server cloaked in SSL.
If you’re using multiple servers, you may want to combine their logs in one location so that you can get all of the clients’ reports in one location. You may also want or need to retain these reports for more time. In either case, determine what policies you should apply to the syslog or Apple System Logger (ASL, for Mac OS X) configuration for your server systems.
Whether or not you use repo, it’s good to know that the tools do some logging. The logging can be followed to try to determine the status of your clients, or whether they are failing their updates.
Unfortunately, the most common client failures I have seen tend to involve the lapply tool, and the default level of detail I’ve seen reported back to the server does not provide an indication of what problem has been encountered. You see only that there was an error. Still, even though you may not get enough detail to remotely resolve the problem, it’s something for you to go by find problems in the first place.
When I come across software I might need to add into Mac OS X that requires compilation, I typically want to produce one Universal Binary. Make it a four-way UB and you get both 32- and 64-bit support.
A single binary is ideal for a Radmind transcript (or other package, if you wanted to bundle it into an installer) that can be deployed on both PowerPC and Intel Macs on Leopard.
Since rsync 3.0.2 with some patches is apparently working quite well at preserving Mac OS X data and metadata — passing the Backup Bouncer tests — I thought I'd try my hand at a four-way Universal Binary.
What worked for me, using a Mac Pro 4x2.8 GHz with Mac OS X 10.5.2 and Xcode 3.0, was to start with Mike Bombich's instructions and modify them with some fairly standard Universal Binary build steps. The configure and compile were both less than a minute on this system.
$ ./prepare-source
$ env CFLAGS="-O -g -isysroot /Developer/SDKs/MacOSX10.5.sdk -arch i386 -arch ppc -arch ppc64 -arch x86_64" \
LDFLAGS="-arch i386 -arch ppc -arch ppc64 -arch x86_64" \
./configure --prefix=/usr/local --disable-dependency-tracking
$ make
$ sudo make install
I have seen the use of "-Wl,-syslibroot,/Developer/SDKs/MacOSX10.5.sdk," in the LDFLAGS environment variable when compiling some applications but this did not work for me with rsync; when I removed it, rsync 3.0.2 configured successfully for me.
The result of the above build process appears to be a full four-way UB:
$ lipo -info /usr/local/bin/rsync
Architectures in the fat file: /usr/local/bin/rsync are: i386 ppc7400 ppc64 x86_64
A local transfer on the build system appears to have worked correctly. I did not test with Backup Bouncer, sync with a non-Mac system, or when shuttling data between architectures. So, accept these results with a grain of salt; I’m just happy I got rsync to compile for now.
The “iLife Support 8.2” from Apple update seems to affect only /System/Library/PrivateFrameworks/iLifeMediaBrowser.framework. I ran lsbom -a on its package receipt, which is in /Library/Receipts/iLifeMediaBrowser.
I had the update appear on two different computers that didn’t have iLife 08 installed — one of each architecture (PowerPC and i386) — in Software Update. It appeared this week along with Mac OS X 10.5.2 and other updates, but did not require 10.5.2.
Therefore, it seems relatively safe to install (or Radmind to) to more computers than just those with iLife 08 installed. This is important with Radmind because you worry about the layering of updates and dependencies between them, so I’d advise you to do your own testing in your own environment. In particular, I don’t know how it will interact with older versions of iLife if they happened to be in place, so if you have such a situation, you’ll want to do your own testing before rolling into production.
After a tiny bit of experimentation with Leopard launchd jobs that have multiple triggers, I found that they can in fact run as expected when either trigger is hit.
I had a working launchd job that used the StartCalendarInterval key. I then added a WatchPath key and rebooted for good measure. When I used `touch` to modify the watched path, the job executed.
Good to know, even though I’d never tried this in Tiger. I don't yet know if there is a limitation on how many triggers can be added successfully, or if there are triggers which cannot be combined.
One immediate benefit of this, however, is that you can have one job that will definitely be coalesced into a single execution whenever it is missed (as with system sleep). If they don't coalesce into a single execution, it sounds as if the developers at Apple would consider that a bug. Multiple individual jobs do not coalesce if they are defined in separate plists, even if they call the same script or command line tool.
Following McAfee's use of the Mac OS X filesystem is quite a job. If you're a systems administrator, the way their VirusScan product is installed makes it complex to understand and deploy. If you're deploying with a system like Radmind, it pays to take the time to look into what gets installed and where.
Take the recently-released McAfee VirusScan 8.6 for Mac OS X — the first post-Leopard update to their anti-virus software. I must give them credit for that. They did provide support for Leopard more quickly than I can recall them doing for any other previous version of Mac OS X, going back to when the product was called Virex.
This new VirusScan still has files that are named "Virex," in the second version after the product was renamed. (Or third, if you count their questionably-named "VirusScan 8 for Mactel" product.) Look in /Library/Application Support for one example.
The /Library/Application Support/Virex/ directory includes the StuffIt and StuffItCore frameworks, presumably to scan within StuffIt archives now that the operating system itself no longer includes StuffIt. This directory also stores the VirusScan Reporter application.
VirusScan 8.6 installs a kernel extension, but it doesn't necessarily get put where you might expect. Not in /System/Library/Extensions, no — it is placed, instead, in /usr/local/vscanx/Extensions. Oh, it's named Virex.kext.
Speaking of /usr/local/vscanx, McAfee apparently decided to duplicate similar system-supplied directories there. So, you'll find "Extensions," "lib," "var," and "var/tmp" directories inside it.
There are four setuid/setgid applications in VirusScan 8.6.
The installer also modifies the local syslog.conf, adding the line:
local7.info /var/log/VirusScan.log
In VirusScan 8.5, a local user and group were created during the installation — they became UID and GID 500, as I recall. I haven't yet confirmed that with version 8.6, but it seems likely that practice has continued, so be alert because it can affect the ownership of the installed files.
Update 01/21/2008: Nathan Lewan posted helpful information about managing VirusScan 8.6 to the Radmind mailing list. (If you need to know how software gets installed, ask a Radmind admin.)
In reading the man page for the updated installer utility in Leopard, it looks like it offers the equivalent of "answer files" on Windows. This sounds like a big improvement, especially for systems administrators who want to automate the installation of packages on Mac OS X.
The "install choice change XML file" can be used to apply changes to the default option in an installer package. This uses the -applyChoiceChangesXML flag.
The installer can also show the defaults as well as the result after applying a choice changes XML file to them. Use the -showChoicesXML to find the choices and -showChoicesAfterApplyingChangesXML to see the outcome of choice changes, respectively.
For what it's worth, the -dominfo and -query flags are also new and have functions that I don't recognize from Tiger, comparing the two man pages.
Anyway, the install choices sound like a "win" — even just to get reliable, reproducible Radmind transcripts, if you're into that sort of thing. Imagine creating an installer choice change file for a package you install/update all the time — the system software itself, and the Apple Xcode Developer Tools, both spring to mind. Whether you're creating install choice change files for bulk installation or just automation of your build process, it sounds as if this new capability should be really helpful.
Every time I do hashing in Python, I have to look it up. I forget how to do it. That's probably a bad thing, at least compared to the shell. The shell way isn't exactly simple, but I find it something I can do by rote.
I'm going to write down how I got MD5 and SHA-1 hashes for a file — which is something I occasionally have to do when posting a download, for example — thereby making it possible to find my own perfectly-tailored how-to next time:
>>> import hashlib # hashlib is new in Python 2.5
>>> file_reference=open('/path/to/file', 'rb').read() # open the file for reading, in binary mode
>>> hashlib.md5(file_reference).hexdigest()
'11fb57ba7927ad04534d0a341dd9c943'
>>> hashlib.sha1(file_reference).hexdigest()
'bff8e8bcd74662ee52dde369e9387cb10d5a5ece'
There, that wasn't so bad. I just have to remember the name of the built-in hashlib module and how to call for a hash of some data with it. You're missing the twenty other lines I tried which didn't work, of course, but you don't really need to see that. Sigh.
Without specifying hexdigest(), the result is a hash object rather than the hash value.
>>> hashlib.md5(file_reference)
<md5 HASH object @ 0x639c0>
I compared the Python hashlib results above with the following output from OpenSSL, and they are the same:
$ openssl md5 /path/to/file
MD5(/path/to/file)= 11fb57ba7927ad04534d0a341dd9c943
$ openssl sha1 /path/to/file
SHA1(/path/to/file)= bff8e8bcd74662ee52dde369e9387cb10d5a5ece
On balance, I think I'd still like comparing that hash against another string better in Python, but getting the hashes was quite a bit more confusing to me. It was enough to interrupt my flow.