Dual-license your content for inclusion in The Perl 5 Wiki using this HOWTO, or join us for a chat on irc.freenode.net#PerlNet.

Melbourne Perl Mongers/Meeting History 2010 08

From PerlNet

Jump to: navigation, search

Contents

Deploying Win32 Perl with WiX - David Dick

Windows Installer files

  • Used to be known as Microsoft Installer files
  • Usually has a '.msi' file suffix
  • Equivalent to rpm or dpkg files
  • What privileges are required to install?

if the 'softies say '...and it uses a database...' about anything other than an actual database, run screaming. Examples: the registry, Outlook, MSI.

WiX - Windows Installer XML

  • Uses an XML based description of a package and builds an MSI file.
  • Lead programmer - Rob Mensching
  • Currently hosted on sourceforge, on the way to codeplex
  • equivalent to rpmbuild or dpkg --build aka ar & tar

ICE rules - Internal Consistency Evaluators

  • picks up errors/warnings with an MSI file
  • more or less equivalent to lintian or rpmlint
  • except run automatically as part of WiX toolchain (since v3)
  • Unlike rpmlint, at least ICE dosen't consider perl.exe to be dangerous

Dependencies

  • Done via registry and file checks
  • Can handle Requires/Depends and Conflicts type requirements
  • No explicit concept of Recommends
  • Specified via Conditions

Package Management

  • Windows Update - for Microsoft packages _only_
  • No apt-get/yum ability to install your own sources

Windows Installer Features

  • Smallest unit that Windows Installer can install
  • Is a collection of Components
  • May have one or more Conditions determining whether it may be installed

Windows Installer Components

  • Smallest thing that Windows Installer can track
  • Can be a file, group of files, registry keys, etc
  • Need to be grouped into Features
  • Each Component must be contained in a directory

WiX Custom Actions

  • Equivalent to pre/post install/uninstall scripts for dpkg/rpm
  • Standard actions for directories, files, registry keys and services
  • Pretty much anything else requires a Custom Action
  • WiX has already pre-written a lot of good custom actions
  • can add a User
  • can add a WebSite/VirtualDirectory to IIS
  • cannot add a User Group (no reason you can't contribute the patch)

WiX Custom Actions - Write your own

  • C++, and Windows Installer SDK
  • Visual Basic/Jscript Custom Actions
  • External Command

WiX Custom Actions - C++ and Windows Installer SDK

  • Recommended solution
  • Lots of existing custom actions in WiX source
  • Provides easy access to Installer logging in case of failure
msiexec /L* install.txt /i foo.msi
msiexec /L* uninstall.txt /x foo.msi

WiX Custom Actions - Visual Basic/Jscript

  • Why not to - just in case extra motivation is needed
  • Hard to write Visual Basic that handles errors correctly
  • Nearly impossible to debug
  • Anti-Virus products silently kill them

VBScript and Jscript suck for CustomActions

WiX Custom Actions - External Commands

  • Get to write Custom Action in Perl
  • Cannot log via Windows Installer

IIS - Apache spoils us

  • like repeating Apache 1.3 to Apache 2 but every 3 years
  • IIS 5 -> IIS 6 (Web Service Extensions, changed the IIS user)
  • IIS 6 -> IIS 7 (Completely re-wrote IIS)
  • IIS 7 -> IIS 7.5 (Personal favourite, changing the user (and type of user) the web server runs as in a minor release)
  • IIS now runs as IIS APPPool\DefaultAppPool, a virtual user
  • All HTTP Auth methods depend on shell accounts? WTF?

IIS - Sympathy for the WiX team

  • WiX has coped with the horror
  • WiX 3.5 (latest snapshots) works for IIS 5 -> IIS 7.5
  • Maybe pre IIS 5 too. Just can't boot NT4 to check
  • but no realistic hope of an application running on the next IIS
  • IIS 7 has only recently been supported by WiX (only in beta)
  • Probably b/c they had to re-write all their Custom Actions

IIS - what about mod_perl

  • Apache modules equivalent used to be ISAPI
  • Now it's IIS Modules
  • ISAPI is officially deprecated
  • PerlEx and PerlIS from ActiveState still work ... stay tuned
  • Write IIS code as CGI scripts and pray

Apache2 on win32

Perl binaries

  • Build your own
  • Specify a dependency on ActiveState and/or Strawberry
  • No major dramas either way

Build Principles

  • very similar to rpm/dpkg
  • create a temporary directory
  • build the directory/file structure(s) in there
  • specify the custom actions aka pre/post install/uninstallscripts
  • describe the package in a .wxs file
  • call candle.exe to generate .wixobj file
  • call light.exe to generate .msi file

Compiling C/C++ code on win32

  • no make install concept. Manually copy the files to intended location
  • no LD_LIBRARY_PATH
  • no LDFLAGS="-Wl,--rpath -Wl,$prefix/lib" concept
  • no LDDLFLAGS="-shared -Wl,--rpath -Wl,$prefix/lib -L$install_root$prefix/lib"
  • just set Path, or copy dlls to the correct perl lib directory
  • probably use visual studio or mingw

Generating the Wxs file - wix tag

use XML::Writer();

# open the $wxs_handle for writing here

my $wxs_writer = XML::Writer->new(
	'OUTPUT' => $wxs_handle,
	'ENCODING' => 'utf-8',
	'DATA_MODE' => 1 );
$wxs_writer->startTag('Wix', 
	'xmlns' => 'http://schemas.microsoft.com/wix/2006/wi');

Generating the Wxs file - product tag

# Remember the upgrade code !!!
my $upgrade_code = '{CONSTANT-UPGRADE-GUID-HERE}'; # Win32::GuidGen();
$wxs_writer->startTag('Product', 'UpgradeCode' => $upgrade_code, # blah, blah
$wxs_writer->startTag('Upgrade', 'Id' => $upgrade_code);
$wxs_writer->emptyTag('UpgradeVersion',
		'Minimum' => '1.0.0',
		'Maximum' => $version,
		'IncludeMinimum' => 'yes',
		'IncludeMaximum' => 'no',
		'Property' => 'OLDERVERSIONBEINGUPGRADED');
$wxs_writer->endTag('Upgrade');

Generating the Wxs file - Installing Files

  • use File::Find() or equivalent
$wxs_writer->startTag('Directory', 'Id' => 'ProgramFilesFolder',
				'Name' => 'PFiles');
$wxs_writer->startTag('Directory', 'Id' => 'Dir7', 'Name' => 'foo');
$wxs_writer->startTag('Directory', 'Id' => 'Dir8', 'Name' => 'bin');
$wxs_writer->startTag('Component', 'Id' => 'FileComp1',
				'Guid' => Win32::GuidGen());
$wxs_writer->emptyTag('File', 'Id' => 'File1',
		'Name' => 'sqlite.exe', 'Source' => $fromPath,
				'ReadOnly' => 'yes');
$wxs_writer->endTag('Component');
$wxs_writer->endTag('Directory');
$wxs_writer->endTag('Directory');
$wxs_writer->endTag('Directory');

Generating the Wxs file - Registry value as a Property

$wxs_writer->startTag('Property', 'Id' => 'ACTIVESTATE_PERL_REGISTRY_VERSION');
$wxs_writer->emptyTag('RegistrySearch',
    'Id' => 'ActiveStateActivePerlVersionSearch',
    'Root' => 'HKLM',
    'Key' => 'SOFTWARE\ActiveState\ActivePerl',
    'Name' => 'CurrentVersion',
    'Type' => 'raw');
$wxs_writer->endTag('Property');
$wxs_writer->startTag('Property', 'Id' => 'PERL_HOME_DIRECTORY');
$wxs_writer->emptyTag('RegistrySearch',
    'Id' => 'ActiveStateActivePerlDirectorySearch',
    'Root' => 'HKLM',
    'Key' =>

      'SOFTWARE\ActiveState\ActivePerl\[ACTIVESTATE_PERL_REGISTRY_VERSION]',
    'Type' => 'raw');
$wxs_writer->endTag('Property');

Generating the Wxs file - Path as a Property

$wxs_writer->startTag('Property',
    'Id' => 'ACTIVESTATE_WPERL_PATH');
$wxs_writer->startTag('DirectorySearch',
    'Id' => 'ActiveStateWPerlBinarySearch',
    'Path' => '[PERL_HOME_DIRECTORY]bin');
$wxs_writer->emptyTag('FileSearch',
    'Name' => 'wperl.exe',
    'MinVersion' => '5.10.1.1006');
    # this means 5.10.1.1007 is the minimum acceptable
$wxs_writer->endTag('DirectorySearch');
$wxs_writer->endTag('Property');

Generating the Wxs file - ARP Properties

  • Stands for Add/Remove Programs
  • A set of Properties to alter appearance/functions
  • Examples would be Contact details and disabling "Remove"

Generating the Wxs file - Launch Conditions

$wxs_writer->startTag('Condition',
	'Message' => "ActiveState ActivePerl wperl.exe " .
			"at least at version 5.10.1 (Build 1007) is required");
$wxs_writer->characters('Installed OR ACTIVESTATE_WPERL_PATH');
$wxs_writer->endTag('Condition');

Generating the Wxs file - Features

$wxs_writer->startTag('Feature', # blah, blah
$wxs_writer->emptyTag('ComponentRef', 'Id' => 'FileComp1');

# Probably lots of ComponentRefs for a feature

$wxs_writer->startTag('Condition');
$wxs_writer->characters('Property1 OR Property2');
$wxs_writer->endTag('Condition');
$wxs_writer->endTag('Feature');

Generating the Wxs file - External Custom Action

$xmlWriter->emptyTag('CustomAction',
    'Id' => 'RunFoo',
    'Directory' => 'Dir6',
    'ExeCommand' => '"[ACTIVESTATE_WPERL_PATH]" -wT "[Dir8]foo.pl"',
    'Impersonate' => 'no',
    'Execute' => 'deferred',
    'Return' => 'check');
$wxs_writer->startTag('InstallExecuteSequence');
$wxs_writer->startTag('Custom', 'Action' => 'RunFoo',
    'Before' => 'InstallFinalize');
$wxs_writer->characters('NOT Installed');
$wxs_writer->endTag('Custom');
$wxs_writer->endTag('InstallExecuteSequence');

Generating the Wxs file - Installing Registry Keys

$wxs_writer->startTag('RegistryKey', 'Root' => 'HKLM',
    'Key' => "Software\\$organisation\\$program",
    'Action' => 'create'); # createAndRemoveOnUninstall
$wxs_writer->emptyTag('RegistryValue',
    'Type' => 'string',
    'Name' => 'SMTPHost',
    'Value' => 'localhost');
$wxs_writer->emptyTag('RegistryValue',
    'Type' => 'integer',
    'Name' => 'SMTPTimeout',
    'Value' => '20');
$wxs_writer->endTag('RegistryKey');

Filesystem issues

  • Expect base file paths to change
  • due to localization and OS changes
  • WiX can cope with this easily
  • Make sure your programs can
  • Permissions are difficult to manage

Win32 Permissions - Quick Summary

  • Been around in current form since W2K
  • Can do anything
  • But not easily
  • except with Win32::Security

Win32 Permissions - SID (Security Identifier)

  • No, not the Debian one or the Oracle one
  • equivalent to a uid OR gid
  • well known SIDs may be relied on across machines and found at http://support.microsoft.com/kb/243330
  • similar to the 0 uid always mapping to the root user
# Get the localized name for the 'Administrators' Group
my ($uname) = Win32::Security::SID::ConvertSidToName(
		Win32::Security::SID::ConvertStringSidToSid(
			'S-1-5-32-544'));

Win32 Permissions - Named Object

  • Basic term for anything that can be named
  • All Named Objects are Securable Objects
  • Win32::Security supports files, directories and registry keys
my ($named_object) = Win32::Security::NamedObject->new(
			'SE_FILE_OBJECT', $file_path);

my ($regty_object) = Win32::Security::NamedObject->new(
		'SE_REGISTRY_KEY', "MACHINE\\SOFTWARE\\$organisation");

Win32 Permissions - Security Descriptor

  • Contains all of the permissions information for a Securable Object
  • There is one Security Descriptor per Securable Object
  • Win32::Security doesn't bother to make a distinction

Win32 Permissions - Object Owner

my ($owner) = $named_object->ownerTrustee();

Win32 Permissions - ACE (Access Control Entry)

  • Describes a set of permissions for one SID
  • Specifies if the ACE is to deny or allow the listed rights
  • Allows inheritance to child objects and/or containers
my ($ace) = Win32::Security::ACE->new(
  'SE_FILE_OBJECT', # or 'SE_REGISTRY_KEY'
  'ACCESS_ALLOWED_ACE_TYPE', # or 'ACCESS_DENIED_ACE_TYPE',
  'CONTAINER_INHERIT_ACE|OBJECT_INHERIT_ACE',
  'STANDARD_RIGHTS_ALL|GENERIC_ALL',
  Win32::LoginName()
				);

Win32 Permissions - ACL

  • Access Control List
  • Simply a list of Access Control Entries
my (@aces);
push @aces, $ace;
my ($acl) = Win32::Security::ACL->new('SE_FILE_OBJECT', @aces);

Win32 Permissions - DACLs

  • Discretionary Access Control List
  • Controls who is allowed to access the Securable Object
  • Allows the Object to accept/deny parent inherited ACEs
$named_object->dacl($acl,
	'UNPROTECTED_DACL_SECURITY_INFORMATION');

# or 'PROTECTED_DACL_SECURITY_INFORMATION'

Win32 Permissions - SDDL

  • Security Descriptor Definition Language
  • O:SYG:BAD:(A;;FA;;;SY)
O:  - Define the Owner
SY  - Local System - a well known sid string, can be an actual sid
G:  - Primary Group
BA  - Administrators - a well known sid string, as above
D:  - Discretionary Access Control List (DACL)
(   - Begin an Access Control Entry (ACE)
A;  - for Allow permissions
;   - Inheritance for this ACE
FA; - Full Access
;;  - no idea
WD  - Everyone - another well known sid string, as above

well known sid strings may be found at http://msdn.microsoft.com/en-us/library/aa379602.aspx

Win32 Permissions - Krazy Stuff

  • DACL - Discretionary Access Control List
  • SACL - System Access Control List - Apparently for auditing
  • CACL, ICACL, SubInACL, XCACLs - Command line ACL binaries

Win32 Permissions - Not Supported in Win32::Security

  • Primary Group is only intended for Services for Unix subsystem
  • Un-named Objects such as processes
  • Named Objects other than Registry Keys, Files and Directories
  • SACL - Unlikely to require this for packaging

Win32 Permissions - Problems in WiX

  • Impossible to add permissions for "the IIS process"
  • No existing Custom Action for adding User Groups
  • Permission tag -> trips up on names such as 'Users'
  • PermissionEx tag -> SDDL based, but only applies to Windows 7
  • util:PermissionEx -> Appends instead of replacing permissions

Discussion on stackoverflow at http://stackoverflow.com/questions/2260634/wix-problem-to-give-permissions-to-a-folder

Win32 Permissions - Problems in Windows Installer

  • win32 permissions can lockout anyone, including Administrators
  • beware rendering your application as un-un-installable
  • unless your user has a GSOH
  • or you are a good runner
  • This can actually be quite tricky
  • Recommend never change file owner
  • Recommend assigning full permissions to file owner

Win32 Permissions - General Complexity

  • File ReadOnly attribute always applies
  • File Permissions only apply on NTFS. see Win32::FsType()
  • IIS File Permissions also apply
  • Beware authentication methods for denying write access
  • Such as the now deprecated NTLM authentication

Win32 Permissions - Summary

  • A Securable Object can be a directory, file or registry key
  • A Security Descriptor is applied to a Securable Object
  • A Security Descriptor has an Owner, a Group, a DACL and a SACL
  • A DACL is a list of ACEs and determines if parent ACEs apply
  • An ACE is a set of (inheritable) permissions attached to a SID
  • A SID identifies a User or a User Group
  • There are other reasons why write access may be denied

Building an MSI file from your WXS file

  • uses the candle and light binaries
  • need to make two command line calls to build a msi file
candle.exe foo.wxs
light.exe foo.wixobj


WiX Extensions

  • Can extend the WiX namespace via extensions
  • we will look at the IIS extension
$wxs_writer->startTag('Wix', 'xmlns' =>
			'http://schemas.microsoft.com/wix/2006/wi',
				'xmlns:iis' =>
			'http://schemas.microsoft.com/wix/IIsExtension');

candle.exe -ext WixIISExtension foo.wxs
light.exe -ext WixIISExtension foo.wixobj

Define a Web Virtual Directory element

$wxsWriter->startTag('iis:WebSite',
    'Id' => 'DefaultWebSite',
    'Description', => 'Default Web Site');
$wxsWriter->emptyTag('iis:WebAddress',
    'Id' => 'AllUnassigned',
    'Port' => '80');
$wxsWriter->endTag('iis:WebSite');
$wxsWriter->startTag('Component',
    'Id' => 'WebComponent',
    'Guid' => Win32::GuidGen(),
    'Directory' => 'Dir54');
$wxsWriter->emptyTag('CreateFolder');
$wxs_writer->startTag('iis:WebVirtualDir',
    'Id' => 'WebVirtualDirId1',
    'Alias' => 'foo',
    'Directory' => 'Dir54',
    'WebSite' => 'DefaultWebSite');

Define Web Directory Properties element

$wxs_writer->emptyTag('iis:WebDirProperties',
    'Id' => 'WebDirPropertiesId1',
    'DefaultDocuments' => 'index.pl',
    'LogVisits' => 'yes',
    'WindowsAuthentication' => 'no',
    'Execute' => 'yes',
    'Read' => 'yes',
    'BasicAuthentication' => 'yes',
    'AnonymousAccess' => 'no',
    'DigestAuthentication' => 'no',
    'Write' => 'no');

Define Web Application element

$wxs_writer->startTag('iis:WebApplication',
    'Id' => 'WebApplicationId1',
    'Name' => 'Application',
    'AllowSessions' => 'no',
    'Buffer' => 'no',
    'ClientDebugging' => 'no',
    'Isolation' => 'high',
    'ServerDebugging' => 'no');

Define Web Application Extension element

if ($interfaceType eq 'cgi') {
  $wxs_writer->emptyTag('iis:WebApplicationExtension',
      'Executable' => '"[PERL_HOME_DIRECTORY]bin\\perl.exe" -wT "%s" %s',
      'Extension' => 'pl',
      'Script' => 'yes');
} elsif ($interfaceType eq 'isapi') {
  $wxs_writer->emptyTag('iis:WebApplicationExtension',
      'Executable' => '[PERL_HOME_DIRECTORY]bin\\perlEx30.dll',
      'Extension' => 'pl',
      'Script' => 'yes');
}
$wxs_writer->endTag('iis:WebApplication');
$wxs_writer->endTag('iis:WebVirtualDir');

  • Could probably use Features and Conditions to achieve this

Define Web Service Extension element

if ($interfaceType eq 'cgi') {
  $wxs_writer->emptyTag('iis:WebServiceExtension',
      'Id' => 'WebServiceExtensionId1',
      'Allow', => 'yes',
      'Description' => $description,
      'File' => '"[PERL_HOME_DIRECTORY]bin\\perl.exe" -wT "%s" %s');
} elsif ($interfaceType eq 'isapi') {
  $wxs_writer->emptyTag('iis:WebServiceExtension',
      'Id' => 'WebServiceExtensionId1',
      'Allow', => 'yes',
      'Description' => $description,
      'File' => '[PERL_HOME_DIRECTORY]bin\\perlEx30.dll');
}

Conclusion

  • WiX is a good tool for packaging Perl applications on win32 platforms
  • WiX makes IIS support relatively easy for the moment
  • Apache2/mod_perl2 on win32 looks tempting
  • Win32 permissions are quite complex
  • Win32::Security is at least a good starting point
  • see also Perl::Dist::WiX for the Strawberry/WiX build


Quick Padre Demo - Hamish Carpenter

Perl Application Development and Refactoring Environment (Padre) [1]

Padre is a text editor aimed to be an IDE for Perl.

Getting Padre

There are many ways to download Padre. I would strongly recommend using the version from CPAN (v0.68 at time of writing). Earlier versions have far fewer features and are not a fair representation of the current capabilities.

Installing Padre on Ubuntu Lucid 10.04 with CPAN Minus

Using CPAN Minus was a great experience and made installation really straightforward. The only hiccup was needing to install several ubuntu/debian packages for the graphic libraries. See meeting on 10th March 2010 (Meeting_History_2010) for more info on cpanminus.

# install curl
sudo apt-get install curl
# install cpanminus [2]
curl -L http://cpanmin.us | perl - --sudo App::cpanminus
# install Padre
cpanm Padre
# wait.....
# Failed on gtk libs. Googled and found list of ubuntu packages 
#  http://padre.perlide.org/trac/wiki/Download#Ubuntu1 (source code section of web page)
sudo apt-get install libwxgtk2.8-0 libwxgtk2.8-dev libwxbase2.8-dev \
                     libwxbase2.8-0 libmodule-build-perl libextutils-cbuilder-perl \
                     libmodule-pluggable-perl g++ libgtk2.0-dev \
                     libgl1-mesa-dev libglu1-mesa-dev
# run padre
padre
# Failed on paths. Need to set up for cpanminus
export PATH=$PATH:~/perl5/bin
export PERL5LIB=~/perl5/lib
padre

[add your OS here]

If you install it on a different OS, add your instructions here.

Built-in features

There are a lot of features in Padre and the quick demo only touched on some of them. Many can be enabled from the view menu.

Good ones to try are:

  • show syntax check
  • show outline
  • show functions

Plugins

Plugins give Padre many more features. Plugins I've tried are:

  • Padre::Plugin::PerlCritic[3]
  • Padre::Plugin::PerlTidy[4]
  • Padre::Plugin::Plack[5]

These can be installed using cpanminus which makes it very painless.

Once installed, plugins need to be turned on (enabled) using the plugin-manager (tools menu).

Demonstration

This demonstration borrowed heavily from Miyagawa's excellent Plack advent calendar available here: http://advent.plackperl.org/

If you have installed the Padre::Plugin::Plack plugin then you can save a file as psgi and paste the contents below into it. You will then need to close and re-open the file as Padre didn't open it as a psgi file, this time there will be a new control panel where you can "Start" the plack server then click on the "View in Browser" button to see the demo web application.

PSGI File: pod.psgi

use strict;
use warnings;

# plack uses sub ref returned implicitly
my $app = sub {
   my $env  = shift;

   if ( $env->{PATH_INFO} eq '/favicon.ico' ) {
       open my $fh, "<:raw", "/usr/share/transmission/web/images/favicon.ico"
         or die $!;
       return [ 200, [ 'Content-Type' => 'image/x-icon' ], $fh ];
   }
   elsif ( $env->{PATH_INFO} eq '/certain_death' ) {
       die "computer says NO!!!111!!one!eleven";
   }
   elsif ( $env->{PATH_INFO} eq '/' ) {
       return [ 200, [ 'Content-Type' => 'text/html' ], [<DATA>] ];
   }
   else {
       my $module = $env->{PATH_INFO};
       $module =~ s{\A/}{};

       # never do this in real life
       my $content = `pod2html --infile=\`perldoc -l $module\``;

       if ($content) {
           return [ 200, [ 'Content-Type' => "text/html" ], [$content] ];
       }
       else {
           return [ 404, [ 'Content-Type' => 'text/html' ],
               ['404 Not Found'] ];
       }
   }
}
__DATA__
<html>
<head>
   <title>POD Viewer</title>
</head>
<body>
<h1>POD Viewer</h1>
<p>Append name of perl module to end of url eg /Data::Dumper.</p>
<h2>Here's a few to get you started:</h2>
<ul>
<li><a href="/perltoc">perltoc</a> - table of contents
<li><a href="/perlpod">perlpod</a> - how to write POD
<li><a href="/Padre">Padre</a> - The Perl IDE
<li><a href="/Plack">Plack</a> - Perl superglue for web frameworks
<li><a href="/Padre::Plugin::Plack">Padre::Plugin::Plack</a> - Plack plugin for Padre
</ul>
</body>
</html>
Personal tools