Zach Burlingame
Programming, Computers, and Other Notes on Technology

HOWTO: Setup a Locally Authoritative DNS Server on Ubuntu 9.10

May 18th, 2011

How to setup a locally authoritative DNS server on a LAN with Ubuntu 9.10. These instructions should work almost entirely on Ubuntu 10.x without modification.

# File:	HOWTO Setup a DNS Server on Ubuntu 9.10.notes
# Date:	2010/03/25
# Refs: https://help.ubuntu.com/8.04/serverguide/C/dns-installation.html
#       https://help.ubuntu.com/community/BIND9ServerHowto
# Desc:	Setting up a locally authoritative DNS server on a LAN with Ubuntu 9.10

# Install bind9 (the actual DNS server) and the dnsutils package
# (useful for testing and troubleshooting DNS issues). We 
# also install resolvconf so that we can manage the static
# IP DNS settings in /etc/network/interfaces since NetworkManager
# will stomp on any changes we make to /etc/resolv.conf
sudo apt-get install bind9 dnsutils resolvconf

# Establish a static IP for the nameserver by editing
# /etc/network/interfaces. In our setup, we have a multihomed
# machine with eth0 static on the trusted LAN and eth1 DHCP
# to the internet
auto eth0
iface eth0 inet static
    address 192.168.72.1
    netmask 255.255.255.0
    network 192.168.72.0
    gateway 192.168.72.254
    broadcast 192.168.72.255
    # dns-options are implemented by the resolvconf package
    dns-nameservers 192.168.72.1
    dns-search test.com

# Make the static IP changes take affect
# NOTE: You should be able to use 
# ifconfig <interface> down/up, but I found rebooting to be
# the only reliable way. Also, when these changes take effect,
# you'll have NO internet DNS yet, so beware.
sudo shutdown -r now

# For our setup here, I want to be an authoritative nameserver 
# on the local LAN, so I need at least 1 Forward Zone and
# 1 Reverse Zone for it. Our domain is test.com and our 
# subnet is 192.168.72.0. First we add the zones to
# /etc/bind/named.conf.local
zone "test.com" {
        type master;
        file "/etc/bind/db.test.com";
};

zone "72.168.192.in-addr.arpa" {
        type master
        notify no;
        file "/etc/bind/db.192";
};

# Use an existing zone file as a template
sudo cp /etc/bind/db.local /etc/bind/db.test.com

# Edit the /etc/bind/db.test.com file to fit our zone requirements
;
; BIND data file for test.com
;
$TTL    604800
@       IN      SOA     ns.test.com. root.test.com. (
                       20100325         ; Serial (YYYYMMDD)
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                         604800 )       ; Negative Cache TTL
;
@       IN      NS      ns.test.com.
@       IN      A       192.168.72.1
ns      IN      A       192.168.72.1

; Hostname entries
hydrogen    IN  A       192.168.72.1
helium      IN  A       192.168.72.2
lithium     IN  A       192.168.72.3
beryllium   IN  A       192.168.72.4
boron       IN  A       192.168.72.5

# Use an existing reverse zone file as a template
sudo cp /etc/bind/db.127 /etc/bind/db.192

# Edit the Reverse Zone file for our reqs
;
; BIND reverse data file for test.com
;
$TTL    604800
@       IN      SOA     ns.test.com. root.test.com. (
                       20100325         ; Serial (YYYYMMDD)
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                         604800 )       ; Negative Cache TTL
;
@       IN      NS      ns.
1       IN      PTR     ns.test.com.

; Hostname RNL pointers
2       IN      PTR     helium.test.com.
3       IN      PTR     lithium.test.com.
4       IN      PTR     beryllium.test.com.
5       IN      PTR     boron.test.com.


# Start the bind9 daemon
sudo /etc/init.d/bind9 start

# Check the status of the server. You should get a big 
# printout from the local nameserver
dig @localhost

Resolving Redefinition Errors Betwen ws2def.h and winsock.h

May 17th, 2011

The Problem

This particular issue I run into much more frequently than I would like (full pathnames removed for brevity):

ws2def.h(91): warning C4005: ‘AF_IPX’ : macro redefinition
winsock.h(460) : see previous definition of ‘AF_IPX’
ws2def.h(131): warning C4005: ‘AF_MAX’ : macro redefinition
winsock.h(479) : see previous definition of ‘AF_MAX’
ws2def.h(168): warning C4005: ‘SO_DONTLINGER’ : macro redefinition
winsock.h(402) : see previous definition of ‘SO_DONTLINGER’
ws2def.h(212): error C2011: ‘sockaddr’ : ‘struct’ type redefinition
winsock.h(485) : see declaration of ‘sockaddr’
ws2def.h(390): error C2059: syntax error : ‘constant’
ws2def.h(524): warning C4005: ‘IN_CLASSA’ : macro redefinition
winsock.h(287) : see previous definition of ‘IN_CLASSA’

The Cause

The problem is due to an incompatibility between winsock.h and WinSock2.h. This usually occurs when something has included Windows.h (which includes winsock.h) before WinSock2.h, like this:

#include <Windows.h>
#include <WinSock2.h>

int main( int argc, char* argv[] )
{
  return 0;
}

Of course, finding where this include ordering has occurred is often more complex than this example. Turn on the “Show Includes” compiler option and look in the output window of your build to trace the occurrence to your source files.

The Solution

You basically have three options to fix this:

Option 1: WIN32_LEAN_AND_MEAN

Defining WIN32_LEAN_AND_MEAN will reduce the size of the Windows headers by excluding several APIs, including Windows Sockets.

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <WinSock2.h>

int main( int argc, char* argv[] )
{
  return 0;
}

Option 2: Explicitly include WinSock2.h before Windows.h

By explicitly including WinSock2.h before every place that you Windows.h, you prevent the collision. However, this can be quite cumbersome at times.
UPDATE 2011-11-12:This method can cause issues with struct packing and alignment if WIN32 isn’t defined before including WinSock2.h (see Oren’s comment below). To resolve this issue, either define WIN32 as a preprocessor flag in your project or explicitly define #WIN32 prior to the WinSock2.h include as I’ve done below.

#ifndef WIN32
  #define WIN32
#endif
#include <WinSock2.h>
#include <Windows.h>

int main( int argc, char* argv[] )
{
  return 0;
}

Option 3: Create a WinSock Wrapper Header

This option creates a header file that prevents the collision in the first place and then you include this at (or nearest as possible) to the top of every file that needs WinSock2.h.

The following is based on code from this stackoverflow answer.
#ifndef _WINSOCK_WRAPPER_H_
#define _WINSOCK_WRAPPER_H_

#if _MSC_VER > 1000
#pragma once
#endif

#ifndef _WINDOWS_
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#endif

#include <winsock2.h>

#pragma comment(lib, "ws2_32.lib")

#endif
If the #pragma option above is unfamiliar to you, the compiler passes it along to the linker telling it to include the lib rather than having to set it in the project settings. Theres more on #pragma comment options over on MSDN.

HOWTO: Configure a Local NTP Server on Ubuntu 9.10

May 16th, 2011

Setting up a Network Time Protocol (NTP) server and configuring NTP clients on a LAN with Ubuntu 9.10. These instructions should also work for Ubuntu 10.x but may require slight tweaking for use with upstart.

# File:	HOWTO Configure a Local NTP server on Ubuntu.notes
# Date:	2010/03/16
# Refs: http://www.ubuntugeek.com/network-time-protocol-ntp-server-and-clients-setup-in-ubuntu.html
#       http://www.cameratim.com/computing/linux/time-serving
#       http://en.gentoo-wiki.com/wiki/NTP#Be_a_Time_Server
# Desc:	Setting up an NTP server and configuring NTP clients
#       on a LAN with Ubuntu 9.10

# -------------------------------------
#           Configure the Server
# -------------------------------------
# We can't uninstall ntpdate like the ubuntugeek reference 
# suggest because it will remove ubuntu-minimal along with it,
# which has a bunch of stuff we want. So, we just have to
# disable the ntpdate service
sudo update-rc.d -f ntpdate remove

# Install NTP
sudo apt-get install ntp

# Edit the ntp configuration file: /etc/ntp.conf
# Change the server lines to point to the pool servers
# you want to use. 
# In my case, we are on a LAN with no Internet access, 
# so I will be the master time keeper, so I commented
# out all server lines and added the following:
server 127.127.1.0

# Fudge your local server's local clock to a lowish stratum 
# so that other local computers will still use it as a time 
# source, but will resync to a better time source when
# they're able to.
# Using a stratum of 9 means it's considered better than the
# computer's own time (usually stratum 16 for unsynced, often 
# stratum 12 for stand-alone time servers), but will be
# considered a poor source of time compared to most other 
# servers (usually stratums ranging from 1 to 4), and will 
# be ignored in those cases (when better sources are available).
fudge 127.127.1.0 stratum 9

# Restart the ntp service
sudo service ntp restart

# -------------------------------------
#           Configure the Clients
# -------------------------------------
# Install NTP
sudo apt-get install ntp

# Edit the ntp configuration file: /etc/ntp.conf
# Change the server lines to point to the IP (or DNS record
# if you created one) of the LAN server created above
# Bonus Points if you have a DNS entry for the NTP server
server ns.test.com

# Restart the ntp service
sudo service ntp restart

HOWTO: Using cpio to Copy a Partition in Linux

May 15th, 2011

Using cpio to copy everything from one partition to another in Linux, maintaining all file permissions, symlinks, and timestamps.

# File: copy_partition.notes
# Auth: burly
# Date: 03/20/05
# Desc: A method of copying everything from one 
#      partition to another

# Change directories to the top level of the 
# partition you want to copy
cd /part

# Use find to locate all objects and cpio to copy them
find . -mount -print0 | cpio -0dump /path/to/new/part

HOWTO: Rebuild a Software RAID 5 Array After Replacing a Disk

May 15th, 2011

How to rebuild a software RAID 5 array after replacing a failed hard disk on CentOS linux.

# File: rebuild_RAID5.notes
# Auth: burly
# Date: 2005/08/09
# Ref: 
# Desc: Rebuild a degraded RAID 5 array w/ a new HDD

# Assumptions: 
	Failed drive is /dev/sda
	Good drives are /dev/sdb, /dev/sdc
	RAID array(s) are /dev/md3
	
# Copy the partition table from one of the existing
# drives over to the new drive
sfdisk -d /dev/sdb | sfdisk /dev/sda

# Rebuild the array
mdadm --manage --add /dev/md3 /dev/sda1

# Check mdstat for progress
watch -n 60 cat /proc/mdstat

	md3 : active raid5 sda[3] sdc1[1] sdb1[0]
	      490223104 blocks level 5, 128k chunk, algorithm 2 [3/2] [UU_]
	      [>....................]  recovery =  0.1% (380160/245111552) finish=64.3min speed=63360K/sec

# That's it!

HOWTO: Extract Directory from Mercurial Repo with Full History

May 15th, 2011

Extract a directory out of an existing Mercurial repo into a new, top level repo with full revision history. For example, you have:
<Project A>/<sub project B>
and you want to make <sub project B> its own repo.

# File: HOWTOExtractDirectoryIntoStandaloneRepoMercurial.notes
# Auth: burly
# Date: 03/01/2011
# Refs: http://mercurial.selenic.com/wiki/ConvertExtension
#       http://www.jmedved.com/2010/09/extracting-part-of-mercurial-repository/
# Desc: Extract directory out of an existing Mercurial repo into
#       a new, top level repo with full revision history.
#       e.g. You have <Project A>/<sub project B> and 
#       you want to make <sub project B> it's own repo.

#Enable hg convert
vim ~/.hgrc

######## BEGIN COPY BELOW THIS LINE ###########
[extensions]
convert =
########## END COPY ABOVE THIS LINE ############

# Clone the repo you want to extract from
mkdir ~/tmp
cd ~/tmp
hg clone /path/to/yer/repo-to-clone

# Make a filemap of the items you want to keep. 
vim filemap.txt

########## BEGIN COPY BELOW THIS LINE #########
include WindowsPowerEvents
rename WindowsPowerEvents .
############ END COPY ABOVE THIS LINE ########

# Extract the desired folder with the convert extension
hg convert --filemap filemap.txt repo-to-clone new-repo-name

# (Optionally) Import this repo into the primary location you want it
cd /path/to/primary/repo/location
hg pull ~/tmp/new-repo-name

HOWTO: VMWare Server on CentOS 5.4

May 13th, 2011

I have a habit of creating .notes files whenever I’m doing system admin type work. I’ve collected a number of these over the years and I refer back to them fairly regularly whether I’m doing something similar or just looking for a specific command. I’ll be placing a bunch of these up here for easier access for me as well as public consumption in case anyone else finds them useful. They will be posted pretty much unedited, so they won’t be in the same “format” as I’ve used in the past, but hopefully they are sufficiently legible :-).

Installation and Configuration of VMWare Server 2.x on CentOS 5.4 and 5.5. These instructions should mostly work on 5.0-5.6, note however that the glibc workaround is only necessary on 5.4 and 5.5. VMWare Server is no longer supported by VMWare but I continue to use it until I can upgrade my hardware to be ESXi compatible.

# File: HOWTO_VMwareServer_on_CentOS_5.4.notes
# Auth: burly
# Date: 02/28/2010
# Refs: http://www.cyberciti.biz/tips/vmware-on-centos5-rhel5-64-bit-version.html
#       http://sanbarrow.com/vmx/vmx-config-ini.html
#       http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=844
#       http://pubs.vmware.com/vi301/resmgmt/wwhelp/wwhimpl/common/html/wwhelp.htm?context=resmgmt&file=vc_advanced_mgmt.11.32.html
# Desc: Installation of VMware Server 2.0.2 on CentOS 5.4 x86-64

# Download VMware Server 2.x

# Install dependencies
yum install gcc gcc-c++ kernel-headers kernel-devel libXtst-devel libXrender-devel xinetd

# Install VMware server
rpm -ivh VMware-server-2.x.x-XXXXX.<arch>.rpm

# Configure VMware server
vmware-config.pl

# Answer the series of questions. My answers are below:
Networking: yes
Network Type: Bridge
Network Name: Bridged
. vmnet0 is bridged to eth0
NAT: no
Host-only: no
remote connectiosn port: 902
http connections: 8222
https connections: 8333
Different Admin: yes
Admin user: <my user account>
VM File Location: /vwmare/vms
VMware VIX API files: Default locations

# ##########################################################
# Deal with the hostd/glibc compatilibity issues of VMware 
# Server 2.x w/ CentOS 5.4 - 5.5 (no issues with CentOS 5.3 
# and earlier or CentOS 5.6. VMware Server had not addressed
# this as of VMware Server 2.0.2-203138

# Get the necessary glibc file from 5.3
mkdir ~/vmwareglibc
cd ~/vmwareglibc
wget http://vault.centos.org/5.3/os/x86_64/CentOS/glibc-2.5-34.x86_64.rpm
rpm2cpio glibc-2.5-34.x86_64.rpm | cpio -ivd

# Stop the vmware service and kill any instances of hostd
service vmware stop
killall vmware-hostd

# Move the libc file 
mkdir /usr/lib/vmware/lib/libc.so.6
mv lib64/libc-2.5.so /usr/lib/vmware/lib/libc.so.6/libc.so.6

# Edit the VMware hostd process script
vim /usr/sbin/vmware-hostd

# At line 372, before the program is called, insert an
# empty line and the following
export LD_LIBRARY_PATH=/usr/lib/vmware/lib/libc.so.6:$LD_LIBRARY_PATH

# Start the vmware service
service vmware start

# Set the service to run on startup
chkconfig vmware on

# -----------------------------------------------------------------------------
#                           Optional Performance Tunings
# -----------------------------------------------------------------------------

# -------------------------------------
#    Server-wide Host VMware Settings
# -------------------------------------

# The following changes are made in /etc/vmware/config

# Fit memory into RAM whenever possible and don't ballon
# and shrink memory as needed.
prefvmx.useRecommendedLockedMemSize="TRUE"
prefvmx.minVmMemPct = "100"

# By default, VMware will back the guest's main memory with
# a file the size of  the guest's nominal RAM in the working
# directory (next to the vmdk). If we turn this off, then in
# Linux the memory backed file will be created in the 
# temporary directory while on Windows it will be back by the 
# host's swap file. On Linux hosts, if we turn off named file
# backing AND use a shared memory file system in RAM for the 
# temporary directory, we will miss the disk completely
# unless we are out of RAM on the host system.
mainMem.useNamedFile = "FALSE"
tmpDirectory = "/dev/shm"

# The following changse are made in /etc/sysctl.conf
# Disabling the kernel from over committing memory and only
# using swap when physical memory has been exhausted helps
# overall performance (vm.swapiness). The maximum user 
# frequency covers how fast a virtual machine can set 
# it's tick count to. The vm.dirty options tune how the
# VM subsystem commits I/O operations to disk, you may 
# not want to tune these values if you do not have a
# stable power source.
# http://peterkieser.com/technical/vmware-server-issues/
vm.swappiness = 0
vm.overcommit_memory = 1
vm.dirty_background_ratio = 5
vm.dirty_ratio = 10
vm.dirty_expire_centisecs = 1000
dev.rtc.max-user-freq = 1024


# -------------------------------------
#            Host OS Settings
# -------------------------------------

# In order for the VMWare configuration to work properly 
# with shared memory, you'll need to increase the default
# shared memory size for tmpfs to match the amount of
# memory in your system. This can be done by
# editing /etc/fstab
tmpfs                   /dev/shm                tmpfs   size=8G                    0 0

# In order for the tmpfs changes to take effect, 
# remount the tmpfs
mount -o remount /dev/shm

# The following changes are made in /etc/rc.d/rc.local

# Read ahead on the hard drive should be set to an
# optimal value I have found an optimal value is
# between 16384 and 32768.
# http://peterkieser.com/technical/vmware-server-issues/
blockdev --setra 32768 /dev/md1

# The following items are added as boot-time options
# to the kernel for the host. To enable these values,
# add them to /boot/grub/menu.lst at the end of the
# kernel line.

# On the host operating system, consider using deadline 
# I/O scheduler (enabled by adding elevator=deadline to
# kernel boot parameters), and noop I/O scheduler in
# the guest if it is running Linux 2.6; using the noop 
# scheduler enables the host operating system to better 
# optimize I/O resource usage between different virtual machines.
# http://peterkieser.com/technical/vmware-server-issues/
elevator=deadline

# -------------------------------------
#            Per VM Settings
# -------------------------------------

# The following changes are made to the guest's vmx file

# If we have enough RAM for all the guests to have their
# memory in physical RAM all the time, then we can avoid 
# the ballooning (grow/shrinking) to save CPU cycles. 
# Note this will force the VMware hypervisor to swap
# rather than balloon if it's in need of memory. 
# Swapping is less desirable than ballooning.
sched.mem.maxmemctl = 0

# Disable memory sharing for the VM. This prevents the
# hypervisor from scanning the memory pages for places
# to de-dup memory across VMs and save space. This scanning
# doesn't come free however, and if we have enough physical
# RAM to support all of our VMs, then we don't really need
# the savings.
sched.mem.pshare.enable = "FALSE"
mem.ShareScanTotal = 0
mem.ShareScanVM = 0
mem.ShareScanThreshold = 4096


# The VMware clock synchronization features are a bit
# problematic. If the guest clock gets behind,then VMware
# will catch it up by trying to issue all of the missed
# ticks until it is caught up. However, if the guest gets
# ahead, then the VMware clock will not bring it back. So,
# I am going to use ntp on the guest machines. If you have
# a large number of guests, it's best to setup a local ntpd
# server to offload some of the traffic from the root pools.
tools.syncTime = "FALSE"

# When I reboot the host, I want to gracefully stop each
# VM instead of just powering it off:
autostop = "softpoweroff"

# -------------------------------------
#            Guest OS Settings
# -------------------------------------

# The following items are added as boot-time options to 
# the kernel for the host. To enable these values, add
# them to /boot/grub/menu.lst at the end of the kernel line.

# On the host operating system, consider using deadline I/O
# scheduler (enabled by adding elevator=deadline to kernel
# boot parameters), and noop I/O scheduler in the guest if it 
# is running Linux 2.6; using the noop scheduler enables the 
# host operating system to better optimize I/O resource usage
# between different virtual machines.
# http://peterkieser.com/technical/vmware-server-issues/
elevator=noop

# The following kernel boot parameters will help performance 
# and stability using Linux 2.6 as a guest. APCI/APIC support
# must be enabled if you plan on using SMP virtualization in
# the guest.Setting the clock to PIT has shown to have better
# time keeping than other clock sources, your mileage may vary. 
# Setting elevator to noop will enable the host operating 
# system to better schedule I/O as it has an overview of the
# whole system as opposed to just one virtual machine.
# http://peterkieser.com/technical/vmware-server-issues/

# The current (March 3, 2010) guidance from VMware is that 
# clocksource is no longer required in CentOS 5.4 Use this 
# guide to determine what time keeping settings you need
# for your Guest OS
# http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1006427

# CentOS 5.4 x86_64 Guest
divider=10 elevator=noop

TortoiseSVN Cache (TSVNCache.exe) and Disk I/O

May 12th, 2011

I’ve had a long standing fight with TortoiseSVN‘s TSVNCache.exe both in terms of disk I/O performance and it holding locks to files/folders I want to work with. Turns out that you can set which directories it watches with an include/exclude list under Settings->Icon Overlays.

Thanks to Travis Illig for the blog post on this issue: http://www.paraesthesia.com/archive/2007/09/26/optimize-tortoise-svn-cache-tsvncache.exe-disk-io.aspx

For what it’s worth, my patterns are:

Exclude: C:\*
Include:
C:\Users\<username>\Documents\Visual Studio 2008\Projects\*
C:\Users\<username>\Documents\Visual Studio 2010\Projects\*

Note the trailing ‘\*’, which seemed necessary to get it working for me. I killed TSVNCache.exe after saving the preference changes and then went to some SVN working copy folders. I watched TSVNCache.exe briefly in procmon and it seems that it’s only accessing the specified directories now.

Signal a Windows Application to Terminate from Another Process

April 12th, 2011

In usual fashion, I’ve written a complete sample application. The source code is available here.

Sometimes you want detect if a specific application is running and signal it to terminate in a clean manner. It may be your upgrade installer making sure that the current version of the application is not running before performing it’s upgrade. It could be a long running helper process that needs to be signaled when it is no longer needed. Whatever it is, one method to accomplish this is to use a uniquely named shared event.

NOTE: The method I’m about describe only works for processes who’s source code is under your control. If you want a way to generically signal any running process (e.g. search for a list of running OS and 3rd-party processes that might interfere with your installer and signal them to terminate) then this is not what you want.

A Bit of Background

A similar problem to the one we are discussing here is signaling all running threads to terminate. The idea is that there could be multiple places in the code where an application might need to initiate a process termination, but you need to synchronize that across all threads and allow them to perform their own cleanup. One way to do this is have long running threads periodically check to see if they should shutdown by checking to see if an event is signaled.

Windows Events

On the Windows platform when an event object is created it is done so in an object namespace. In addition to the ability to create your own private namespaces, there are also two special kernel object namespaces – Global and Local. There is a Local namespace associated with each client session on the machine. By default the Local namespace is used for any object created by a process that was started under a client session. As the name implies, there is a single Global namespace system-wide. The Global namespace is used primarily by system services but can also be used by client session processes by prefixing the event name with “Global\”.

The CreateEvent function is used to (surprise!) create an event. It can create either a named or unnamed event. If you use a named event and the named event already exists before the call to CreateEvent then the function returns a handle to the existing object and GetLastError returns ERROR_ALREADY_EXISTS. By creating a named event, the OS enforces that only a single instance of the object exists in that namespace at any one time and that all processes referring to that event will receive a handle to the same instance, creating a form of interprocess communication. Thus if the Local namespace is used, then the event object is shared across all processes that refer to it in that client session. Likewise if it is created in the Global namespace, it is shared across all processes that refer to it on the entire system.

There are two reset mechanisms used by event objects: AutoReset and ManualReset. An AutoReset event will automatically be reset to a non-signaled state as soon as single waiting thread is released. A ManualReset event requires a call to ResetEvent in order to be returned to a non-signaled state.

Lastly, an event can be set to either the signaled or non-signaled state when it is initially created.

Signal Terminate via Named Event Object

By combining the concept of checking for a signaled event to determine when to shutdown and using a named event object, it is possible to signal one process to shutdown via another process. By using an event object created in the Local namespace you can signal processes across a single client session. Conversely by using an event object created in the Global namespace you can signal processes across the entire system.

When creating the terminate event object you want to use a ManualReset event created in the non-signaled state initially. If it were instead an AutoReset event, then as soon as one of the waiting threads from any of the processes was released, the event would return to the non-signaled state. This would result in only a single thread receiving the terminate message, which is not what we want. As for the state, if it were instead initially signaled then the threads would begin terminating as soon as they started running and began checking the event.

Below is an example of creating a named ManualReset event in the Local object namespace that is intially non-signaled. I’m using a GUID for the object name to avoid the potential for unintentional naming collisions with other applications. While a GUID is probably overkill, using a name like “shutdown_event” probably isn’t a good idea.

static const LPCSTR fp_terminate_event_name =
   "Local\\0BAF85D0-0786-4cbf-AF3B-E36322382DBF";

// Create a manual-reset event in the non-signaled state
fh_terminate_event =
  CreateEvent( NULL,                              // default security attributes
               TRUE,                              // manual-reset event.
               FALSE,                             // initial state is non-signaled
               TEXT( fp_terminate_event_name ) ); // object name

Silently Terminate on Abort/Unhandled Exception in Windows

April 4th, 2011

When an application has an unhandled exception or a system routine calls abort (e.g. strcpy_s with a buffer size too small) the system is left to deal with the mess. Usually part of the process is displaying a crash dialog to the user notifying them that the application has unexpectedly terminated, creating a crash dump file, and possibly checking with Microsoft for a solution if it’s a known problem. Sometimes however, you’d prefer that your application not crash in such an “in your face” manner, such as when you spawn child or helper processes as part of a larger system which can manage the process termination on its own. The first thing you should do is focus on making your application NOT crash in the first place!

However, there are times when things may be beyond your control (e.g. making calls to a third-party library that sometimes wigs out on you) or you just want it as a fail safe in case. You can use you own unhandled-exception handler for instance, to perform custom logging. There are several manners in which the system and the C-runtime can notify the user of an abnormal application termination. In order to suppress all of them, most of the time, I use the following routines:

You’ll note that I said most of the time because this method doesn’t guarantee that all of them will be suppressed. Other libraries you call could overwrite your handlers for one. Secondly, the /GS (Buffer Security Check) compiler flags causes the CRT to directly invoke Dr. Watson in the case of a buffer-overrun for security purposes. To prevent and detect other libraries from overwriting your exception filter, you can use API hooking. ¬†When it comes to the CRT directly calling Dr. Watson, this is by design and Microsoft has no plans of changing it.

Here’s the important parts of the source. The entire project is available here.

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>

// Function Declarations
void suppress_crash_handlers( void );
long WINAPI unhandled_exception_handler( EXCEPTION_POINTERS* p_exceptions );

int main( int argc, char* argv[] )
{
  // Suppress C4100 Warnings for unused parameters
  (void*)argc;
  (void*)argv;

  suppress_crash_handlers( );

  abort( );

  return -1;
}

void suppress_crash_handlers( )
{
  // Register our own unhandled exception handler
  // http://msdn.microsoft.com/en-us/library/ms680634(v=vs.85).aspx
  SetUnhandledExceptionFilter( unhandled_exception_handler );

  // Minimize what notifications are made when an error occurs
  // http://msdn.microsoft.com/en-us/library/ms680621(v=vs.85).aspx
  SetErrorMode( SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX ); 

  // When the app crashes, don't print the abort message and don't call Dr. Watson to make a crash dump.
  // http://msdn.microsoft.com/en-us/library/e631wekh(v=VS.100).aspx
  _set_abort_behavior( 0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT );
}

long WINAPI unhandled_exception_handler( EXCEPTION_POINTERS* p_exceptions )
{
  // Suppress C4100 Warnings unused parameters required to match the 
  // function signature of the API call.
  (void*)p_exceptions;

  // Throw any and all exceptions to the ground.
  return EXCEPTION_EXECUTE_HANDLER;
}