Thursday, May 15, 2014

Bootcamp adventures

I needed to replace a drive in a Mac mini with a bigger one. The drive had Mac OS X 10.9 (Mavericks) and Bootcamp with Windows 7. After using Clonezilla to backup the drive and restore it to the bigger one, the partitions were obviously still the same size. There was just a lot of free unpartitioned space at the end of the new drive.

How to resize and move all the partitions (including the hidden EFI and Recovery partitions), to fill the free space?

Disk Utility will not let you touch the Bootcamp partition. Windows 7 looked like it could resize it, but not move it. Resizing it with Win7 created a mess: the Mac would still see the original size.

The heart of the problem seems to be that the Mac wants a GPT partition table, but for Bootcamp, it creates a hybrid MBR partition which is what Win7 sees. Win7 would have no problem with a GPT-only partition, but Bootcamp makes a hybrid MBR anyway. Win7 then resizes that MBR partition, but doesn't update the GPT partition table, which is what the Mac sees. And the Mac doesn't let you fix it either.

At this point, I tried Gparted, but it wouldn't touch this mess (giving some error which I forgot).

Paragon's Camptune X looked like the best solution. However, after paying $20 for it, it turned out it couldn't do anything either. All it does is to let you move a cursor for the relative sizes of the Mac and Windows partitions. But you cannot increase the size to use the free space.

Finally, Rod Smith's Gdisk saved the day again.

What I ended up doing worked in the end:

  • Booted a Gparted USB key, and resized the Windows partition to fill the entire disk.
  • Booted to Mac, and used Camptune X to enlarge the Mac partition while reducing the Windows one.
  • Now, Windows would not boot.
  • Used gdisk to re-create the hybrid MBR, and mark the Windows partition as bootable, as explained in detail in this post.

Labels: , , , , ,

Monday, August 12, 2013

ffmpeg burnt-in timecode

Burning-in timecode is easy in Avid or Final Cut, but if for any reason you need to do it the hard way with command-line ffmpeg, here is how.

To not make it harder than necessary, there are links to pre-compiled versions of ffmpeg on their download page. For Mac OS X, as of August 2013, there were these 2 versions:

  •, which unfortunately didn't have the needed filter. It would give the error
    "AVFilterGraph ...] No such filter: 'drawtext'".
  • the version 2.0.1 built by Helmut Tessarek worked fine. Unfortunately, it is compressed with 7-zip, so you may need to get a decompressor first. I used Keka (not open source, but free).

Below is the command I used to quickly encode Sony mpeg2 MXF files into H264 Quicktimes, preserving the original timecode in the QT TC track (ffmpeg does this automatically), and also burning it into the picture.

Since the command itself is quite awful, it is best to predefine variables, so that the long command itself can be copy/pasted directly, without further editing, or at least not too much...

# set variables for the input and output files:


# the timecode rate must be set. Should be identical to the FPS.


# select a monospaced font file on your machine. On Linux, try:


# or on Mac:

font="/Library/Fonts/Andale Mono.ttf"

# size and position:

position="x=w-text_w-(text_w/6):y=text_h" # top right

# For bottom right, try this instead: position="x=(w-tw)/2: y=h-(2*lh)"

# get the timecode, and escape the ":" to be able to use it in the burn-in filter

timecode=$( ffmpeg -i "$in" 2>&1 | awk '$1 ~ /^timecode/ {print $NF}' )

# To test encoding only the first x seconds, use:

test_secs="-t 20"

# or for the whole video, leave this empty:


# quality/size/speed : (try crf between 18 and 25? lower is better quality and bigger file.)

preset=ultrafast # (superfast, fast, slow, ...)

# And finally (with de-interlacing and without scaling):

ffmpeg -threads 0 -i "$in" $test_secs -acodec copy -vcodec libx264 -preset $preset -crf $crf -deinterlace -vf "drawtext=fontfile=$font: timecode='$tc_escaped': r=$tc_rate: $position: fontcolor=white: fontsize=$fontsize: box=1: boxcolor=black@0.2" "$out"

or to keep only video with audio channel 1 (throwing away audio channels 2, etc. ):

ffmpeg -threads 0 -i "$in" $test_secs -map 0:0 -map 0:1 -acodec copy -vcodec libx264 -preset $preset -crf $crf -deinterlace -vf "drawtext=fontfile=$font: timecode='$tc_escaped': r=$tc_rate: $position: fontcolor=white: fontsize=$fontsize: box=1: boxcolor=black@0.2" "$out"


Labels: , ,

Wednesday, May 22, 2013

Windows 7 profile trouble

Event ID 1511: Windows cannot find the local profile and is logging you on with a temporary profile. Changes you make to this profile will be lost when you log off.

  • Login as a different user (with admin rights)
  • Under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList, find Keys named SID.bak (like "S-1-5-21-4129847285-3583514821-2567293568-1001.bak")
  • Delete them
  • If needed, delete C:\Users\USERNAME

Labels: , , , ,

Saturday, May 11, 2013

Mediawiki with Postgres on Debian

A short guide to install Mediawiki on Debian with PostgreSQL 9.1.With a fix for this error:

"Attempting to connect to database "postgres" as superuser "postgres"... error: No database connection"

Installing packages

The server is still using Debian Squeeze, but I expect it would quite the same for the new Debian Wheezy. Here I used squeeze-backports.

 Add the backports repository if needed:

echo "deb squeeze-backports main contrib non-free" >> /etc/apt/sources.list

Install everything:

apt-get update
apt-get -t squeeze-backports install apache2 postgresql-9.1 postgresql-contrib php5-pgsql
apt-get -t squeeze-backports install imagemagick libdbd-pg-perl
apt-get -t squeeze-backports install mediawiki

I use a separate IP for the wiki, so need to add it to the interface:

mcedit /etc/network/interfaces
# wiki on it's own IP
auto eth0:3
iface eth0:3 inet static

/etc/init.d/networking restart

Apache configuration

# I use the mod_rewrite module in Apache
a2enmod rewrite

# I prefer the config file in sites-enabled
# (but it's really just a symlink to /etc/mediawiki/apache.conf):
mv /etc/apache2/conf.d/mediawiki.conf /etc/apache2/sites-enabled

My virtual host config:

<VirtualHost *:80>
    ServerName wiki.example.lan
    ServerAlias wiki.example.lan
    DocumentRoot /docs/www-wiki

    ErrorLog /var/log/apache2/wiki-error.log
    CustomLog /var/log/apache2/wiki-access.log combined

    ServerSignature On

    Alias /icons/ "/usr/share/apache2/icons/"

    RewriteEngine On
    RewriteRule ^/w(iki)?/(.*)$  http://%{HTTP_HOST}/index.php/$2 [L,NC]

    <Directory /docs/www-wiki/>
        Options +FollowSymLinks
        AllowOverride All
        # Default is Deny. Exceptions listed below with "Allow ...":
        Order Deny,Allow
        Deny from All
        Satisfy any
        # LAN
        Allow from
        # VPN
        Allow from

# If using LDAP
#        AuthType Basic
#        AuthName "Example Wiki. Requires user name and password"
#        AuthBasicProvider ldap
#        AuthzLDAPAuthoritative on
#        AuthLDAPURL ldap://localhost:389/ou=People,dc=example,dc=lan?uid
#        AuthLDAPGroupAttribute memberUid
#        AuthLDAPGroupAttributeIsDN off
#        Require ldap-group cn=users,ou=Groups,dc=example,dc=lan

    # some directories must be protected
    <Directory /docs/www-wiki/config>
        Options -FollowSymLinks
        AllowOverride None

    <Directory /docs/www-wiki/upload>
        Options -FollowSymLinks
        AllowOverride None

    <Directory "/usr/share/apache2/icons">
        Options Indexes MultiViews
        AllowOverride None
        Order allow,deny
        Allow from all

Moving files

I used a directory other than the default /var/lib/mediawiki. So I had to move things over:

cp -rp /var/lib/mediawiki /docs/www-wiki

The only tricky part, with the fix:

Before starting the web configurator in http://wiki.example.lan/config/ you need to define a password for the "postgres" database user. Mediawiki will start the psql client as the www-data system user, but with the -U argument to set the user to "postgres". Even if you defined a password for the system user "postgres", this is not the password of the database user "postgres".

So you need to start psql as the postgres system user, which you can do as root using sudo -c, and then set the password inside the psql client:

sudo -u postgres psql
psql (9.1.9)
Type "help" for help.

postgres=# \password
Enter new password:
Enter it again:
postgres=# \q

If you don't do this, the Mediawiki config will end with this error:

Attempting to connect to database "postgres" as superuser "postgres"... error: No database connection

And a big pink and unhelpful error box below.

The Postgresql log (tail /var/log/postgresql/postgresql-9.1-main.log) will show:

FATAL:  password authentication failed for user "postgres"


Now you just have to move LocalSettings.php to /etc/mediawiki/.

And if you used a different install root, you have to edit it to change the MW_INSTALL_PATH:


Labels: , , , , , , , ,

Thursday, January 31, 2013

rsync server daemon on Mac OS X with launchctl

There are many web pages describing how to enable the rsync daemon on Mac OS X using launchd/launchctl mechanism. But I had to use a different (and simpler) plist file in LaunchDaemons to make it work across reboots on Lion (10.7.4).

(I started by following this guide , and this very similar one. And I also read this and this. In the end, what helped me getting the plist file right was this thread. Particularly this post: "For one you have both a Program and a ProgramArguments key, when you should have only one or the other (you use Program if there is just one element to the command, or ProgramArguments if there are multiple." And this one.)

This is the .plist file I used in /Library/LaunchDaemons/org.samba.rsync.plist : 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "">
<plist version="1.0">

This is an example /etc/rsyncd.conf file:

secrets file = /etc/rsyncd.secrets
hosts allow = *.cust.isp.tld

pid file = /var/run/
uid = nobody
gid = nobody
use chroot = yes
max connections = 5
syslog facility = local5
list = yes
read only = yes

path = /Users/Shared
comment = Users-Shared
uid = someuser
gid = admin
auth users = user_in_secrets

The file /etc/rsyncd.secrets looks like:


To install it:

sudo -s
chown root:wheel /etc/rsyncd.*
chmod 644 /etc/rsyncd.conf
chmod 600 /etc/rsyncd.secrets
launchctl load /Library/LaunchDaemons/org.samba.rsync.plist
launchctl start org.samba.rsync ## (this last command is probably unneeded)

To check if it is installed and running:

launchctl list | grep rsync
808  -    0x7fddb4806c10.anonymous.rsync
-    0    org.samba.rsync

ps ax | grep [r]sync
  808   ??  Ss     0:00.00 /usr/bin/rsync --daemon

rsync --stats someuser@localhost::

To remove it:

sudo launchctl unload /Library/LaunchDaemons/org.samba.rsync.plist
sudo killall rsync


Labels: , , ,

Sunday, January 06, 2013

scripting disk partitionning in Linux - take 2

It is possible to use parted to script/automate disk partitioning in Linux, as described in "Command-line partitioning and formatting".

Another way is to use sgdisk from the GPT fdisk programs.

In Debian and derivatives, it can be installed with sudo apt-get install gdisk.

The current version 0.8.1 from the Ubuntu 12.04 repository would partition only the first 2TB of a 4 TB. disk. So you may need to get a more recent version from the downloads page. I got version 0.8.5 for x64, and that worked very well.

The following will create and format a single NTFS partition on an entire drive:

disk=/dev/sdb            # Make sure you got this right !!
echo "disk $disk will be completely erased."

sudo sgdisk -Z $disk
sudo sgdisk --new=0:0:-8M -t 1:0700 $disk
sudo sgdisk -p $disk
sudo mkntfs --verbose --fast --label "$label" --no-indexing --with-uuid ${disk}1

-Z removes any left-over partitions

--new=0:0:-8M creates a single partition from the start of the disk to 8MB before the end (just in case it's useful to not end on the very last sector)

-t 1:0700 sets the first partition we just created to type "Microsoft Basic Partition", which is the type we want for a simple NTFS partition. Linux would be -t 1:8300. Use sgdisk -L to get a list of partition types.

Note that for comfortable (and safer) manual partitioning, there is also cgdisk. It is like the old cfdisk, but works with new disks over 2TB.

Labels: , , , , , , , ,

Wednesday, January 02, 2013

Set up your own Dynamic DNS

The problem with external dynamic DNS services like,, etc. is that you constantly have to look after them. Either they are free, but they expire after 1 month and you have to go to their web site to re-activate your account. Or you pay for them, but then you need to take care of the payments, update the credit card info, etc. This is all much too cumbersome for something that should be entirely automated.

If you manage your own DNS anyway, it may be simpler in the long run to set-up your own dynamic DNS system.

Bind has everything needed. There is a lot of info on the Internet on how to do it, but what I found tended to be more complicated than becessary or insecure or both. So here is how I did it on a Debian 6 ("squeeze") server.

The steps described below are:

Initialize variables

To make it easier to copy/paste commands, we initialize a few variables


(In Debian, you can use grep directory /etc/bind/named.conf.options to find the correct binddir value)

For dynamic hosts, we will use a subdomain of our main zone:


Create key

Most example use the dnssec-keygen command. That would create 2 files (with ugly names): one .private and one .key (public) file. This is useless since the secret key is the same in both files, and the nsupdate method doesn't use a public/private key mechanism anyway.

There is a less-known and more appropriate command in recent distributions : ddns-confgen. By default, it will just print sample entries with instructions to STDOUT. You can try it out with:

ddns-confgen -r /dev/urandom -s $host.$zone.

The options we use here are to use an "hmac-md5" algorithm instead of the default "hmac-sha256". It simplifies things with nsupdate later. And we also specify the key name to be the same as the host's name. That way, we can use a wildcard in the "update-policy" in named.conf.local and don't need to update it every time we add a host.

ddns-confgen -r /dev/urandom -q -a hmac-md5 -k $host.$zone -s $host.$zone. | tee -a $etcdir/$zone.keys

chown root:bind   $etcdir/$zone.keys
chmod u=rw,g=r,o= $etcdir/$zone.keys

Depending on how you intend to use nsupdate, you may want to also have a separate key file for every host key. nsupdate cannot use the $zone.keys file if it contains multiple keys. So you might prefer to directly create these individual keyfiles by adding something like > $etcdir/key.$host.$zone :

ddns-confgen -r /dev/urandom -q -a hmac-md5 -k $host.$zone -s $host.$zone. | tee -a $etcdir/$zone.keys > $etcdir/key.$host.$zone

chown root:bind   $etcdir/$zone.keys $etcdir/key.*
chmod u=rw,g=r,o= $etcdir/$zone.keys $etcdir/key.*

Configure bind

Create zone file

Edit $binddir/$zone :

$TTL  3600 ; 1 hour IN SOA (
         1 ; serial (start at 1 for a dynamic zone instead of the usual date-based serial)
      3600 ; refresh by secondaries (but they get NOTIFY-ed anyway)
       600 ; retry (every 10 minutes if refresh fails)
    604800 ; expire (slaves remove the record after 1 week if they could not refresh it)
       300 ; minimum ttl for negative answers (5 minutes)


Edit /etc/bind/named.conf.local

Edit /etc/bind/named.conf.local to add :

// DDNS keys
include "/etc/bind/";

// Dynamic zone
zone "" {
    type master;
    file "/var/cache/bind/";
    update-policy {
        // allow host to update themselves with a key having their own name
        grant * self;

Reload server config

rndc reload && sleep 3 && grep named /var/log/daemon.log | tail -20

(adjust the sleep and tail values depending on the number of zones your DNS server handles, so that it has time to report any problems)


If you created individual key files, or your $zone.keys file contains only a single key, you can test like this:

host=myhost; ip=;;; keyfile=$etcdir/key.$host.$zone
echo -e "server $server\n zone $zone.\n update delete $host.$zone.\n update add $host.$zone. 600 A $ip\n send" | nsupdate -k "$keyfile"

Or, more readable and with an extra TXT record:

cat <<EOF | nsupdate -k $keyfile
server $server
zone $zone.
update delete $host.$zone.
update add $host.$zone. 600 A $ip
update add $host.$zone. 600 TXT "Updated on $(date)"

(If you get a could not read key from $keyfile: file not found error, and the file actually exists and is owned by the bind process user, you may be using an older version of nsupdate (like the version in Debian Etch). In that case, replace nsupdate -k $keyfile with nsupdate -y "$key_name:$secret" using the key name and secret found in your key file.)

Check the result:

host -t ANY $host.$zone

It should output something like descriptive text "Update on Tue Jan  1 17:16:03 CET 2013" has address
If you try to use a file with multiple keys in the -k option to nsupdate, you will get an error like this:

... 'key' redefined near 'key'
could not read key from FILENAME.keys.{private,key}: already exists


In a /etc/network/if-up.d/ddnsupdate script.

If you have setup an update CGI page on your server, you could use something like this, letting the web server use the IP address it received anyway with your request.

secret="xBa2pz6ZCGQJ5obmvmp26w==" # copy the right key from $etcdir/$zone.keys

wget -O /dev/null --no-check-certificate "https://$server/ddns/update.cgi?host=$host;secret=$secret"
Otherwise, you can use nsupdate, but you need to determine your external IP first :

secret="xBa2pz6ZCGQJ5obmvmp26w==" # copy the right key from $etcdir/$zone.keys

ip=$(wget -q -O -

cat <<EOF | nsupdate
server $server
zone $zone.
key $host.$zone $secret
update delete $host.$zone.
update add $host.$zone. 600 A $ip
update add $host.$zone. 600 TXT "Updated on $(date)"

I used a very simple myip.cgi script on the web server, to avoid having to parse the output of the various existing services which show your IP in the browser:

echo "Content-type: text/plain"
echo ""

This alternative script example uses SNMP to get the WAN IP from the cable router. It only does the update if the address has changed, and logs to syslog.


server=$(dig +short -t SOA $zone | awk '{print $1}')

ip=$( snmpwalk -v1 -m RFC1213-MIB -c public $router ipAdEntAddr | awk '!'"/$router/ {print \$4}" )

if [ -z "$ip" ]; then
 echo "Error getting wan ip from $router" 1>&2
 exit 1

oldip=$(dig +short $host.$zone)

if [ "$ip" == "$oldip" ]; then
 logger -t `basename $0` "No IP change for $host.$zone ($ip)"

cat <<EOF | nsupdate
server $server
zone $zone.
key $host.$zone $secret
update delete $host.$zone.
update add $host.$zone. 600 A $ip
update add $host.$zone. 600 TXT "Updated on $(date)"

logger -t `basename $0` "IP for $host.$zone changed from $oldip to $ip"

Web server update.cgi

An example update.cgi :


## Use nsupdate to update a DDNS zone.

## (This could be done with the Net::DNS module. It
##  would be more portable (Windows, etc.), but also
##  more complicated. So I chose the nsupdate utility
##  that comes with Bind instead.)

# "mi\", 2013

use strict;

my $VERSION = 0.2;
my $debug = 1;

my $title = "DDNS update";

my $zone     = "";
my $server   = "localhost";
my $nsupdate = "/usr/bin/nsupdate";

use CGI qw(:standard);

my $q = new CGI;

my $CR = "\r\n";

print $q->header(),
      $q->start_html(-title => $title),

if (param("debug")) {
    $debug = 1;

my $host   = param("host");
my $secret = param("secret");
my $ip     = param("ip") || $ENV{"REMOTE_ADDR"};
my $time   = localtime(time);

foreach ($host, $secret, $ip) {
    s/[^A-Za-z0-9\.\/\+=]//g; # sanitize, just in case...
    unless (length($_)) {
        die "Missing or bad parameters. host='$host', secret='$secret', ip='$ip'\n";

my $commands = qq{
server $server
zone $zone.
key $host.$zone $secret
update delete $host.$zone.
update add $host.$zone. 600 A $ip
update add $host.$zone. 600 TXT "Updated by $0 v. $VERSION, $time"

print $q->p("sending update commands to $nsupdate:"), $CR,
      $q->pre($commands), $CR;

open( NSUPDATE, "| $nsupdate" ) or die "Cannot open pipe to $nsupdate : $!\n";
print NSUPDATE $commands        or die "Error writing to $nsupdate : $!\n";
close NSUPDATE                  or die "Error closing $nsupdate : $!\n";

print $q->p("Done:"), $CR;

my @result = `host -t ANY $host.$zone`;

foreach (@result) {
    print $q->pre($_), $CR;

if ($debug) {
# also log received parameters
    my @lines;
    for my $key (param) {
        my @values = param($key);
        push @lines, "$key=" . join(", ", @values);
    warn join("; ", @lines), "\n";

print $q->end_html, $CR;


Labels: , , , , , , , ,

Tuesday, June 26, 2012

USB OSX Lion installer for the impatient

Update: The following is also for Mountain Lion (10.8). For Mavericks (10.9), it's even easier; see below.

To make a bootable USB disk with the Mac OS X Lion installer, the guides I found are much too verbose for my taste, and have too many cute screenshots and ads. Here is a summary for the impatient.

The downloaded installer image is deleted after install, so copy it before installing.

The installer disk image can be found in
Applications / Install Mac OS X (right-click -> Show Package Contents) / Contents / SharedSupport / InstallESD.dmg

  • Open InstallESD.dmg. You get a "Mac OSX Install ESD" disk on the desktop
  • Partition and format the (8 GB.) USB key as standard Mac OSX Extended with journal. (The partition table defaults to MBR for USB drives; that's OK)
  • In the "Restore" tab of Disk Utility:
    • the source is the mounted image on your desktop: "Mac OS X Install ESD" (NOT the .dmg file)
    • the destination is your new USB Mac partition (not the drive itself)

Other instructions suggest using the InstallESD.dmg file as the source, and the USB key itself (not the partition it contains) as the destination. That may work too. Just don't mix both methods. I had tried that and failed, but maybe it was because I had first made a GPT partition table instead of MBR?

If you only have a 4GB key, it seems to work using Carbon Copy Cloner and de-selecting all unneeded language packs. But I haven't tried an install from such a key.

USB OSX Mavericks installer for the impatient

In Mavericks (and hopefully subsequent versions), there is a handy "createinstallmedia" command

Create a single GPT (GUID) partition on the USB key, and format it. Then run:

sudo "/Applications/Install OS X" --volume /Volumes/untitled --applicationpath "/Applications/Install OS X" --nointeraction

"/Volumes/untitled" is the mount point of your USB key. For the verbose version with pictures, see here.

Labels: , , , ,

Sunday, June 03, 2012

Microsoft Security Essentials trouble

It seems that there is a pretty bad problem with Microsoft Security Essentials. I was surprised to notice that it wasn't running on several machines. It turns out that an automatic upgrade through Windows Update fails in a very bad way: it sort of uninstalls the old version, and then fails to install the new version. Users don't notice anything special.

Trying to re-install it by hand also fails with a very informative message (as usual for MS error messages):

Cannot complete the Security Essentials installation
An error has prevented the Security Essentials setup wizard from completing successfully. Please restart your computer and try again.
Error code:0x80070005

Of course, clicking on the "Get help" link is of no help at all.

Apparently, the code "0x80070005" means "Access denied", but there is no way to find out to what the access was denied.

Searching through the event logs reveals other errors, which I will list here in the hope that it helps other Googlers

Log Name:      System
Source:        Microsoft-Windows-WindowsUpdateClient
Event ID:      20
Task Category: Windows Update Agent
Description: Installation Failure: Windows failed to install the following update with error 0x80070643: Microsoft Security Essentials Client Update Package - KB2691905.
Log Name:      Application
Source:        Microsoft Security Client Setup
Event ID:      100
Description: HRESULT:0x80070005
Description:Cannot complete the Security Essentials installation. An error has prevented the Security Essentials setup wizard from completing successfully. Please restart your computer and try again. Error code:0x80070005. Access is denied.

And also older errors which may or may not be related:

Log Name:      Application
Source:        SideBySide
Event ID:      72
Description: Activation context generation failed for "c:\program files\microsoft security client\MSESysprep.dll".Error in manifest or policy file "c:\program files\microsoft security client\MSESysprep.dll" on line 10. The element imaging appears as a child of element urn:schemas-microsoft-com:asm.v1^assembly which is not supported by this version of Windows.

Advice found on the web which didn't work:

  • Uninstall MSSE then re-install (it was not listed in the installed programs, so I couldn't uninstall it)
  • Uninstall any other anti virus software (I didn't have any)
  • Run OneCareCleanup (silly because it was never installed)
  • etc.

Anyway, after a lot of useless searching and trying, what worked for me was to simply
rmdir /S /Q "%PROGRAMFILES%\Microsoft Security Client"

(Be careful with rmdir /s /q ! It deletes the whole folder and sub-folders without asking first!)

After that, I could re-install normally.

But it is very disturbing to see that an antivirus can just stop working without any obvious alert or user notification.

PS: It turns out that even Mark Russinovich had a problem with MSSE. His immediate error was different, but was one I also eventually found in the logs. His solution was to delete the HKCR\Installer\UpgradeCodes\11BB99F8B7FD53D4398442FBBAEF050F registry key. I had tried his procmon tool to try to find what returned "access denied", but then decided to resort to some primitive and brutal approaches first...

Labels: , ,

Wednesday, February 15, 2012

WPKG client in Windows 7

Wpkg is a fantastic tool to manage software installs on groups of Windows machines without a Windows server with Active Directory. However, I had a few problems with it in Windows 7. These were solved by replacing the Wpkg Client with Wpkg-GP.

By default, the Wpkg service runs at startup and does it's installs in the background. But very often, it failed for some reason to get a connection to the network share at the right time when the service was starting, and aborted. The log showed

WNetAddConnection2-> The network location can not be reached.

I tried to add dependencies to the service, but didn't really find a reliable solution.

So in services.msc, I changed the service startup to "Automatic (delayed)". That solved the connection problem, but brought another. If I want to upgrade Thunderbird for example, the installer has a taskkill command to close Thunderbird before upgrading it. But with a delayed start, the user probably has already started Thunderbird, and it seems quite inappropriate to just kill it while it may actually be in use.

In Windows XP, it was possible to delay the login window, so that wpkg could have done it's thing before the user logged in, but for some reason, this doesn't work in Windows 7 anymore.

So the next step was to change the configuration in settings.xml to have wpkg run at shutdown instead. This also failed because, as far as I understand, Windows Vista/7 don't allow a process to prevent shutdown for more than 5 seconds.

Finally, the solution was to remove the standard Wpkg Client, and replace it with Wpkg-GP. That seems to work. I changed the wpkg configuration back to running at startup, and added a wpkg-gp package which also takes care of uninstalling the original wpkg client:

<package id="wpkg-gp" name="Wpkg-GP" revision="%version%">

    <variable name="version" value="0.15" />

    <check type="uninstall" condition="versiongreaterorequal" path="Wpkg-GP %version% .*" value="%version%"/>

    <install cmd="%SOFTWARE%\wpkg-gp\Wpkg-GP-0.15_x64.exe /S /INI %SOFTWARE%\wpkg-gp\Wpkg-GP.ini">
        <exit code="3010" reboot="delayed" />
    <install cmd='msiexec /x "%SOFTWARE%\wpkg\WPKG Client 1.3.14-x64.msi" /qn /norestart' />

    <upgrade cmd="%SOFTWARE%\wpkg-gp\Wpkg-GP-0.15_x64.exe /S /INI %SOFTWARE%\wpkg-gp\Wpkg-GP.ini">
        <exit code="3010" reboot="delayed" />

Labels: , , , , ,