For the nonspoiler portion:

Art – 9/10 – is gorgeous, great style. The different environments are tied well together.
Story – 10/10 – take the time to talk to everyone, and read, don’t skim/skip. There’s humor hidden, a couple of in-jokes and references, and this is the main part of the game.
Music – 15/10 – The music is amazing. It’s tied together with the scenes and the story in a perfect fashion.

Overall – This is a must play game; but don’t play it in an emotionally compromised state. It’s not a light game if you pay attention. It’s really quite heavy. I’m surprised this only got a Teen rating.

Some spoilers below:

The initial screenshots and tidbits make you think this is a fun little resource management game; you know, building houses and upgrading your boat while making sure you keep everyone happy. And while that’s a part of the game, it’s pretty much just a vehicle for the true game.

Which is the passengers on this boat.

The game leads off with dumping you into your role as a replacement for Charon (oh he of the River Styx). No other intro or anything. Which I thought was odd, did I miss something? But it turns out, no, I didn’t.

You end up finding a number of spirits and hearing their stories, helping them get closure… or is this closure for you?

Eventually they tell you they’re ready to leave, and you take them to the “Everdoor”. And there’s another conversation there. Which tends to be very heavy.

While the art of the game tends towards bright colours and clean style, that only serves as a counterpoint to what the story is telling you, and that’s emphasized by the music. It turns into this emotional hammerblow. The content of the stories deals with suicide, infidelity (and whether it’s better to know or not), forgiveness, the struggle for meaning and more.

Again, I have to mention the music, because it’s just so well done that it increases the impact of these stories and scenes immensely. ( I’m going to plug the composer here, because 1) the music is JUST THAT GOOD, and 2) it turns out he’s Canadian. See )

I can’t recommend this game enough. Just be warned, you’re likely going to cry if you pay attention; at the very least, it’s going to give you something to think about.

(Originally posted 2015-06-24)

So, I ran into something interesting and confusing today.

I found that my PDC was stuck on ‘Local CMOS Clock’ as it’s source, despite it being configured to use an external NTP server (in this case, my local Cisco router is configured as an NTP server). Finding this odd, I verified the configuration and then executed a resync command:
w32tm /resync
and received this output:
Sending resync command to local computer
The computer did not resync because no time data was available.
Curious, as the NTP server is running and other devices are syncing correctly. Logging into the Cisco router and executing ‘show ntp associations’ resulted in this:
 address         ref clock       st   when   poll reach  delay  offset   disp    .INIT.          16      –  32768     0  0.000   0.000 15937.    .INIT.          16      –  32768     0  0.000   0.000 15937.
                  .STEP.          16      –   1024     0  0.000   0.000 15937.
*~  .NIST.           1      2     64   377 44.098   0.552  3.122
Well, that’s odd. The system is syncing correctly, but why are the domain controllers showing up as stratum 16?
‘show ntp associations detail’ dynamic, insane, invalid, unsynced, stratum 16
ref ID .INIT., time 00000000.00000000 (16:00:00.000 PST Wed Dec 31 1899)
our mode passive, peer mode unspec, our poll intvl 32768, peer poll intvl 131072
root delay 0.00 msec, root disp 0.00, reach 0, sync dist 15956.69
delay 0.00 msec, offset 0.0000 msec, dispersion 15937.50
precision 2**24, version 3
Well, that’s not good, it’s trying to register itself as an NTP peer with the Cisco router. Googling some bits finally led me to a bug report between certain versions of Cisco IOS and W32Time.
The fix?
Change the w32time configuration:
w32tm /config /manualpeerlist:IPADDRESS,0x8 /syncfromflags:manual /reliable:yes /update
net stop w32time && net start w32time
w32tm /resync
That ‘0x8’ allows it to work.

(Originally posted 2013-05-29)

Man, I’m so tired of dealing with scamware… fortunately I don’t have to do it much, but when your mother calls…

So the latest variant of this Internet Security malware is out and infecting people, and there are already guides on how to fix it. Most of them are fine, use Malwarebytes, re-run your AV afterwards etc… the problem is, if you are running WinXP, it plays a nasty trick against your AV software: it changes the files to junction points. In Vista+, this isn’t a problem, since the system can handle it. XP can’t, and also sees the junctions as corrupted. I’ve seen this with MS Security Essentials so far.

There are two ways to fix this: 1) Slave the drive to another computer or use a Linux-based rescue CD to remove the junctions.

Or, if you are running XPSP3 (which you should be!) you can use the ‘fsutil’ tool to delete the junction point and restore the file.

If you try and reinstall MS Security Essentials without fixing the junctions, you’ll get odd error codes.

To remove the junctions:

Command window (cmd)

Switch to the directory containing the junctions ( C:\Program Files\Microsoft Security Client\  for example)

Dir /p

You should see a list of files/directories with the type <JUNCTION>.

For each file, run ‘fsutil reparsepoint delete filename

After that is done, you can remove and reinstall the software.

(Originally posted 2012-11-13)

DISCLAIMER: I take no responsibility for any disasters that occur due to your use or non-use of these commands. You are supposed to ask Equallogic support for permission 😉

support max-repl-segments # – this sets how many segments of data will be “in-flight” at a time

support repl-use-jumbo YES/NO – enable/disable jumbo frame use for replication traffic ONLY

support max-repl-xfer # – how many simultaneous replications will run at a time; replications beyond this number will wait for an existing transfer to complete in full before starting

support repl-window-size #KB – TCP window size in kilobytes; tune this according to your line speed, latency and how much bandwidth you want to utilize per replication.

(Originally posted 2012-06-16)

Based on ASDM 6.4(7), and ASA OS 8.2(5).

If you want to use your AD accounts for Administrative logins to the ASA, as well as Execute access, this will help you configure it. You’ll need to configure a group in AD and add the relevant accounts that you wish to grant access to beforehand.

ASA AAA for Administration

Expand: ASDM > Configuration > Device Management > Users/AAA

Select AAA Server Groups
Select ADD under ‘AAA Server Groups‘ pane
Protocol: LDAP
Reactivation: Depletion
Dead Time: 10 minutes
Max Failed Attempts: 3

Highlight ‘LDAP_DOMAIN‘ in the list

Select ADD under ‘Servers in the Selected Group‘ pane
Interface Name: Select Interface which the LDAP server is accessible on
Server Name or IP Address: IP of LDAP server
Timeout: 10 seconds
Enable LDAP over SSL: uncheck  (if you have a proper PKI certificate configured, you can enable this)
Server Port: 389   (port 636 by default for SSL, if you have enabled it)
Server Type: Microsoft
Base DN: Base bind point of the directory search in CN=XXX,OU=XXX,DC=XXX,DC=XXX format
Scope: Sub
Naming Attributes: sAMAccountName
Login DN: CN=LDAP Reader,OU=XXXX,DC=XXXX,DC=XXXX  (just a domain user, not a domain admin)
Login Password: password of LDAP Reader account
LDAP Attribute Map: –None–  (note, we’ll come back to this setting)
SASL MD5 Authentication: uncheck
SASL Kerberos Authentication: uncheck
Group Base DN: leave blank
Group Search Timeout: 10

Expand ‘LDAP Attribute Map‘ at the bottom

Select ADD
Name: LDAP_MemberOf_ServiceType
Under Mapping of Attribute Name tab:
LDAP Attribute Name: memberOf
Cisco Attribute Name: IETF-Radius-Service-Type
Select ‘Add >>
Select ‘Mapping of Attribute Value’ tab
LDAP Attribute Value: group name in CN format to valid users
Cisco Attribute Value: 6
Select ‘Add >>

Select Server added under ‘Servers in the Selected Group

Select Edit
Change LDAP Attribute Map to ‘LDAP_MemberOf_ServiceType

Highlight the Server that was just added in the list

Select Test
Select Authorization
Enter user that should be authorized successfully
Select Test
Select Authorization
Enter user that should NOT be authorized successfully

You can repeat the above steps, except for the LDAP Attribute Map, which can be reused, for multiple servers for redundancy. I highly recommend this.

Select ‘User Accounts‘ in navigation pane

Select ‘Add
Username: some_admin_user
Password: some_admin_password
Confirm Password: some_admin_password
Select ‘Full Access
Change Privilege Level to ‘15

**This is a LOCAL database user that will be used for emergency access to the ASA if the LDAP servers are unavailable. This account cannot log in if the LDAP servers are available.**

Select AAA Access in navigation pane

Select ‘Authentication‘ tab
Under ‘Require authentication to allow use of privileged mode commands
Check ‘Enable‘, Server Group: LDAP_DOMAIN, check ‘Use LOCAL when server group fails
Under ‘Require authentication for the following types of connections
Check ‘SSH‘, Server Group: LDAP_DOMAIN, check ‘Use LOCAL when server group fails
Leave HTTP/ASDM, Serial and Telnet unchecked, this way if you’ve screwed up somewhere, you haven’t locked yourself out of the ASA.
Select ‘Authorization‘ tab
Under ‘Enable authorization for ASA command access
Uncheck ‘Enable
Under ‘Perform authorization for exec shell access
Check ‘Enable‘, ‘Remote server

Apply configuration

Test connecting to the ASA via SSH via domain credentials; no need to specify domain name with the username. Verify that executing ‘enable’ and specifying your AD password works.

If successful, save configuration to flash. Otherwise, review configuration via ASDM and change/test until it does.

Once satisfied that access is correctly being handled via SSH, enable LDAP AAA for HTTP/ASDM as well.

Select AAA Access in navigation pane
Select ‘Authentication‘ tab
Under ‘Require authentication for the following types of connections
Check ‘HTTP/ASDM‘, Server Group: LDAP_DOMAIN, check ‘Use LOCAL when server group fails

Save configuration to flash/startup-configuration.

(Originally posted 2011-06-16)

For Windows clients to automatically find a KMS server, it needs to have a SRV record in DNS. This should be located as follows:

DNS Server -> Domain Name -> Other New Records

Select Service Location (SRV)

  • Service: _vlmcs
  • Protocol: _tcp
  • Priority: 0
  • Weight: 0
  • Port number: 1688
  • Host offering the service: FQDN of KMS Server

Once you have this, you can either wait for the next time Product Activation runs in the background, or you can force it by running the two following commands via an elevated command shell:

  • cscript %windir%\system32\slmgr.vbs -skms FQDN.OF.KMS.SERVER
  • cscript %windir%\system32\slmgr.vbs -ato

This will run activation against the specified KMS server.

(Originally published 2010-08-19)

Configuring ASA 8.2 for Remote VPN Access, with LDAP Authentication and Authorization

The following components need to be configured:
– AAA Server
– LDAP Attribute Map
– Address Pools
– Group Policy
– Connection Profile/Tunnel Group

This guide assumes you are using ASDM 6.2 for configuration of the ASA.

This guide was written and tested against a Cisco ASA 5520, Firmware version 8.2(2), ASDM version 6.2(5)

LDAP Attribute Map Configuration

– Create a normal Domain User account for the ASA to use to bind to the Directory to query. This user needs no special access other than to be able to query the directory. It certainly does not need to be a domain administrator account!
– Create an AD Group to be used for access to the VPN. Name it something sensible.

– Get the DN’s for the user and group created, you’ll need them for the next steps. You can use dsquery or the Object tab in ADUC to get them.

– On the ASA, go to Configuration > Remote Access VPN > AAA/Local Users > LDAP Attribute Map
– Create a new map, call it something like ‘LDAP_memberOf
– For the Mapping of Attribute Name:
LDAP Attribute Name: ‘memberOf
Cisco Attribute Name: ‘Group-Policy
*NOTE: Below ASA 8.2, this is IETF-Radius-Class, it has been renamed for 8.2+
– For the Mapping of Attribute Value:
LDAP Attribute Value: full DN of the Remote Access Group from AD e.g. ‘CN=RemoteGroup,OU=Groups,DC=domain,DC=com
Cisco Attribute Value: The name of the ASA VPN Group Policy you are going to use (we haven’t created it yet) e.g. ‘RemoteAccess_Grp

AAA Server Configuration

– Go to Configuration > Remote Access VPN > AAA/Local Users > AAA Server Groups
– Add a new AAA Server Group:
Server Group: Descriptive Name i.e. ‘LDAP_DOMAIN
Protocol: LDAP
Reactivation Mode: Depletion
Dead Time: 10 minutes
Max Failed Attempts: 3
– OK
– Now, with the LDAP_DOMAIN Server Group selected in the top frame, click on Add for the ‘Servers in the Selected Group’ frame
Interface Name: INSIDE or whatever interface you have your Domain Controller behind
Server Name or IP Address: IP Address of a Domain Controller
Timeout: 10 seconds
Server Port: 389 (unless you enable the LDAP over SSL option)
Server Type: Microsoft
Base DN: ‘DC=domain,DC=com‘ or whatever your base DN is
Scope: ‘All levels beneath the Base DN
*Note: One level is quicker, but you need to set the base DN to where your AD Access Group is located
Naming Attribute(s): ‘sAMAccountName
Login DN: full DN of the normal AD User created above
Login Password: password of the AD User account
LDAP Attribute Map: ‘LDAP_memberOf‘ or whatever you called the LDAP Attribute Map previously created
SASL options unchecked
Group Base DN: leave this blank
– OK

Address Pools

– You can either use a DHCP server, or assign a static pool to the ASA to hand out to VPN clients.
– For DHCP: Configuration > Remote Access VPN > Network (Client) Access > Address Assignment > Assignment Policy:
– Uncheck ‘Use Authentication Server
– Check ‘Use DHCP
– Uncheck ‘Use Internal Address Pools
– For Static Pool (which I recommend): Configuration > Remote Access VPN > Network (Client) Access > Address Assignment > Assignment Policy:
– Uncheck ‘Use Authentication Server
– Uncheck ‘Use DHCP
– Check ‘Use Internal Address Pools
– Uncheck ‘Allow the reuse of an IP address XX minutes after it is released
Configuration > Remote Access VPN > Network (Client) Access > Address Assignment > Address Pools
– Add New Pool
Name: Descriptive Name e.g. ‘RemoteVPN_Pool’
Starting Address: IP
Ending Address: IP (note these are inclusive!)
Subnet Mask: Mask
– Ok

*Note: The rest of this guide assumes a Static Address pool was created.

Address Pool created

Group Policies (Note, the terminology sucks. This is a policy for a group on the ASA, not an AD GPO)

– Go to Configuration > Remote Access VPN > Network (Client) Access > Group Policies
– Create a new policy to deny access:
– Add Internal Group Policy
– General:
Name: NoAccess
Address Pools: uncheck ‘Inherit‘ and leave blank
Expand ‘More Options’:
Simultaneous Logins: uncheck ‘Inherit‘ and set to 0
– Ok

This policy will be used to deny unauthorized & unauthenticated users from connecting.

– Create a new policy to for authenticated users:
– Add Internal Group Policy
– General:
Name: ‘RemoteAccess_Grp
Address Pools: uncheck ‘Inherit‘ and select the Address Pool previously created
Simultaneous Logins: Uncheck ‘Inherit’ and set the value to the number of Remote users you have licenses for or the number of valid IP addresses in your VPN pool
– Servers:
DNS Servers: I generally explicitly set them. Delimit with comma or space
WINS Servers: If in use, I generally explicitly set them. Delimit with comma or space
Expand More Options:
Default Domain: uncheck ‘Inherit‘ and set explicitly
– Advanced:
Split Tunneling: If you want to enable split tunneling, set as below. The default is tunnel all.
DNS Names: ‘Inherit
Policy: uncheck ‘Inherit‘ and select ‘Tunnel Network List Below
Network List: uncheck ‘Inherit‘, click ‘Manage
Create a new ACL with permit ACEs that include the hosts/netblocks you WANT tunneled.
Other options: Leave rest at default of ‘Inherit
– Ok

Group Policies created

Connection Profiles / Tunnel Groups:

Go to: Configuration > Remote Access VPN > Network (Client) Access > IPsec Connection Profiles
Click on Add
Name: Descriptive name e.g. ‘RemoteAccess_TunnelGroup’
IKE Peer Authentication:
Pre-shared Key: enter IKE Pre-shared key here
Identity Certificate: None
User Authentication:
Server Group: ‘LDAP_DOMAIN
Fallback: ‘Use LOCAL if Server Group fails‘, check this if you want to fall back to local users if AD is unavailable
Client Address Assignment:
DHCP Servers: leave blank
Client Address Pools: Select Address Pool previously created
Default Group Policy:
Group Policy: NoAccess
*Important, the default policy should be to lock them out, successful authentication&authorization provides the access policy
Server Group: ‘LDAP_Domain
Users must exist in the authorization database to connect: Check this

Enabling access to VPN clients:

To enable IPsec access:
– Go to Configuration > Remote Access VPN > Network (Client) Access > IPsec Connection Profiles
– Under Access Interfaces, check ‘Allow Access‘ for your ‘OUTSIDE‘ interface

To enable AnyConnect SSL / Legacy SSL:
– Go to Configuration > Remote Access VPN > Network (Client) Access > AnyConnect Connection Profiles
– Under Access Interfaces, check ‘Allow Access‘ for your ‘OUTSIDE‘ interface
– Under Connection Profiles, edit the tunnel group you created and give it an alias (The end users will see this in AnyConnect)
Login Page Setting: check ‘Allow user to select connection profile….
– SSL VPN clients also require a SSL certificate to be installed on the interface:
Configuration > Remote Access VPN > Advanced > SSL Settings
Edit your ‘outside’ interface and either create a self-signed certificate (warning will show on the client, but will work) or provide an existing certificate

NAT Rules
Configuration > Firewall > NAT Rules
If ‘Enable traffic through the firewall without address translation‘ is checked, you don’t need to add anything here
Create a new NAT Exempt Rule:
Interface: INSIDE
Source: Network object containing tunneled networks (generally your internal netblock, but you can use this to restrict devices VPN users can get to)
Destination: Create a new network object containing your VPN Client addresses
NAT Exempt Direction:
NAT Exempt outbound traffic from interface ‘INSIDE’ to lower security interfaces(Default)
Write a handy reminder for yourself when you are staring at this rule 6 months from now during a security audit.
– Ok

NAT Rules done

That should be everything, you should have IPsec and SSL VPN capability to your ASA now. Install the appropriate client on the end device and test.


Both the Anyconnect SSL client and the IPsec VPN client are available for a variety of operating systems, and can be pre-installed and configured for the end-user.

On the IPsec VPN client, the Group name and Password are the Tunnel-Group name and the IKE pre-shared key. It will prompt for the user credentials afterwards.
PCF files can be created to securely distribute the IKE PSK to remote end-users (saved as a one way hash in the PCF file).

(Note this was originally published 2010-04-05 and has not been updated)

Pulling your Exchange valid addresses to a Postfix server

WARNING: Make sure you understand the security consequences of creating a passwordless/passphraseless account on your system. Appropriate security measures should be taken to prevent systems from being compromised.

You’ll need to decide where to set up this function. You can do it directly on the receiving Postfix server, but since it’s probably in a DMZ, that would mean allowing access from your DMZ back into your protected LAN with AD. It’s generally a bad idea to open access from the DMZ to the LAN (obviously, there are times where this is unavoidable). Instead, what I recommend is you have a system on your LAN that will push the information up to the DMZ host. This is more secure, since the connection is initiated from your LAN.

The general method is as follows:

  1. Management box polls AD servers for addresses and builds a file
  2. Management box then sends the file to Postfix server
  3. Management box initiates remote update of the Postfix server mapping

What you need on the Management box: You’ll need a working install of Perl, with the Net::LDAP modules installed. You’ll also need this script, which polls the LDAP information and builds the file for sending to Postfix.

First off, grab that script. You’ll need to edit the settings at the beginning to fit your AD domain. You’ll need an AD account that can read AD information. DO NOT USE A DOMAIN ADMIN ACCOUNT! You should use an account with privileges to only access the domain. The password for the account will be in the file as plain-text, so choose accordingly. You can also change the name and location of the resultant file the script creates; I’ve left in my default of /scripts/exchange_recipients

Once the script is appropriately modified, upload it to the management box and place it wherever you like to keep scripts. I generally make a /scripts directory and put it there. I’ll assume for the remainder of the tutorial you’ve done the same. You’ll need to make sure it’s executable as well, so run ‘chmod 664 /scripts/‘ on it.

Now, let’s create our automation account. First on the management server, add a new user, with a blank password. For the guide, lets refer to this account as autoscript. Once the account is created, you’ll need to change to it ‘su autoscript‘ and create an SSH key for it. First change to the home directory ‘cd ~‘ and create a new directory, ‘mkdir .ssh‘. Set permissions: ‘chmod 700 .ssh‘. Change to: ‘cd .ssh‘. Create an RSA SSH keypair by running ‘ssh-keygen -t rsa‘. Answer the questions, leaving the passphrase blank. You should have two files in your .ssh directory now, one is the private key (id_rsa by default) and the other is the public key ( by default).

Now, on the Postfix server, you’ll need to create the same account and .ssh directory. Once you’ve done this, copy the file from the Management server, to the Postfix server. Then, from wherever you have stored the public key, you’ll want to run the following commands:

  1. cat >> /home/autoscript/.ssh/authorized_keys
  2. cat >> /home/autoscript/.ssh/authorized_keys2
  3. chmod 664 /home/autoscript/.ssh/authorized_keys
  4. chmod 664 /home/autoscript/.ssh/authorized_keys2

While we are on the Postfix server, we need to do some other setup. First off, lets make it so that the autoscript account can run certain commands as root without providing a password. To do this, we’ll need to edit the sudo control file: ‘visudo‘. Add the following line at the bottom of the file: ‘%autoscript ALL=NOPASSWD: /usr/sbin/postmap‘. This allows autoscript to run postmapwithout providing a password.

We also need to set up some files in the postfix directory to receive our Exchange address file. Do the following (assuming postfix is in /etc/postfix):

  1. cd /etc/postfix
  2. touch exchange_recipients (assuming default name of file)
  3. touch exchange_recipients.db
  4. chown root:autoscript exchange_recipients
  5. chown root:autoscript exchange_recipients.db
  6. chmod 664 exchange_recipients
  7. chmod 664 exchange_recipients.db

This precreates the files that will be updated by our script with the proper permissions. If you try and write these files remotely, it will fail because autoscript doesn’t have privileges to create files in the /etc/postfix directory.

Almost done!

TEST THE SCRIPT!– Make sure the script is creating a valid file and placing it where you want it. Once you’ve verified this works, continue on.

On the management server, you’ll need to create a script to run and send the results to the Postfix server. Lets do that now:

vi /scripts/

Copy in the following code, updating $REMOTESERVER for the IP or hostname of the Postfix server (I recommend IP for this):


#Transfer and Update Script for Postfix
#Requires and exchange_recipients file to be created and accessible by autoscript user
#Remote server needs to be prepared before update script will work: remote exchange_recipients and .db file need
#to be writeable by autoscript user

scp /scripts/exchange_recipients autoscript@$REMOTESERVER:/etc/postfix/exchange_recipients
ssh autoscript@$REMOTESERVER sudo postmap/etc/postfix/exchange_recipients


Now you’ll need to set permissions and ownership of the script files so they can be executed.

  1. chown autoscript:autoscript /scripts/
  2. chmod 755 /scripts/‘.
  3. chown root:autoscript /scripts/
  4. chown autoscript:autoscript /scripts/exchange_recipients
  5. chmod 774 /scripts/exchange_recipients

That should set ownership and permissions correctly. Switch to the autoscript user and run ‘/scripts/‘. Look for any errors. If it asks you for a password, make sure you didn’t misspell either of the authorized_keys files on either server. Otherwise, you should just see some quick text about copying the file.

Two things left: Set the Postfix server to use this file, and add this job to the cron to run automatically.

On the Postfix server, edit and add another or edit your relay_recipient_maps to use the new file like this: relay_recipient_maps = /etc/postfix/exchange_recipients. Make sure to reload Postfix to use the new configuration.

On the management server, you’ll need to edit the cron for the autoscript user (NOT root’s cron!). Do this by changing the the autoscript user and running ‘crontab -e‘. Then add in the following line to the crontab: ‘0 * * * * /scripts/‘ This runs the script at the start of every hour, every day. You can adjust the timing to fit your needs.

Congratulations, you’re done!

(System commands based on Ubuntu 8.04. Thanks to Chris Covington for writing the original script.)

(Note this guide was originally published 2010-02-11 and has not been updated)

This article was developed using an Ubuntu 8.10 box, but since it uses mostly compiled from source applications, it should be easily portable to the distribution of your choice.

So, you want to offer some web services, but your company is standardized on MS-SQL for their backend? I can already hear the Linux die-hards yelling about Postgres and MySQL already, but fact of life is, a lot of companies use MS-SQL. But there’s no reason to not use a nice lightweight, fast, open-source front end for your web apps against this database.

Three major applications we’ll use to provide access in this guide are: FreeTDS, an interface application that allows direct SQL access, Apache 2.2 for web server duties, and PHP 5.2.6 for scripting/DB access/dynamic HTML generation.

First off, you’ll need to load up your Linux box with the regular tools used for building applications (gcc, etc). This can be done under Ubuntu by getting the ‘build-essential’ package.

Now, in your home directory, you’ll want to pull down the source packages for the applications we are going to build. You can pull down Apache and PHP source packages in Ubuntu with ‘apt-get source packagename‘. For FreeTDS, you can get the latest package from the FreeTDS website (link here). Use either FTP or wget to download the tar.gz file and extract it.

First off, lets build Apache. It’s a relatively simple configure, since we really only need the dynamic module loading capability compiled in. Switch the the source directory for Apache, and execute the following configure script:

./configure –prefix=/usr/local/apache2_2 –enable-so

You’ll see a bunch of text fly by. Congratulations, you’re compiling code! Once that is done, it will have created what is called the ‘makefile’. Guess which command you run next… yup.


More text flies by! Once it’s done, run ‘make install‘. This will put the compiled libraries in their spots.

Alright, so Apache is now installed under /usr/local/apache2_2.

Now lets build and install FreeTDS. This needs to be built before we can compile PHP.

Change to the extracted source directory of FreeTDS (you did unpack the tar.gz archive right?). Again, we’ll go thru the same steps as Apache for building.

./configure –prefix=/usr/local/freetds  –disable-odbc
make install

In this setup, we’ve disabled building the ODBC driver for FreeTDS, as we are not using a Linux ODBC driver manager, so we don’t need it. We use FreeTDS as a direct interface to MS-SQL.

Now we can compile PHP. I used version 5.2.6 and we’ll be enabling some common features used by PHP scripts. Switch to the source directory for PHP and execute:

./configure –prefix=/usr/local/php5 –with-apxs2=/usr/local/apache2_2/bin/apxs –with-gd –with-mssql=/usr/local/freetds –enable-calendar

What this does is sets the build location to /usr/local/php, the ‘with-apxs2‘ is the apache module support for dynamic loading, ‘–with-gd‘ enables graphic support, ‘–with-mssql‘ enables mssql functions within PHP, and ‘–enable-calendar‘ builds the calendar manipulation features.

Note: the ‘–with-gd‘ option usually requires PNG support. You’ll need the development libraries for it. Under Ubunutu, you can get these with ‘apt-get install libpng12-dev‘.

Once you’ve got a successful configure, execute the make and make install commands.

Congratulations, you’re almost done.

Check the /usr/local/apache2_2/conf/httpd.conf and make sure there is a line in the LoadModule section as follows:

LoadModule php5_module    modules/

This enables PHP5 support for Apache. You will need to restart Apache if you modify the httpd.conf (or recompile PHP for additional options later).

You’ll also need to edit the freetds.conf file to put in your SQL server settings. This is a dynamic file that is checked on each call, so you don’t need to recompile or restart anything if you make changes. The TDS version line value is generally set at 8 for MS-SQL (works on SQL 2000 and above). Check the FreeTDS site for the value mapping if you are unsure.

Note: Make sure your SQL server is listening on the port you assign (default is 1433). On SQL 2005 and above, you may need to turn off dynamic ports.

You can test accessing the SQL server with the ‘tsql‘ executable in the freetds/bin directory. This is basically a telnet test to the SQL server. Once you have tested this successfully, you should be able to use the PHP mssql_* commands in your scripts!

Now, one final thing. If you reboot the system, you’ll notice Apache doesn’t auto start. That’s because compiling this way didn’t add any startup scripts.  You’ll need to create one to fit your distribution as necessary.

Hope this helps someone out there! I know it’s been extremely useful for me.