Clean up temporary directories and files
Temporary directories and files are created in various parts of the system, but are not tidied up.

diff --git a/bin/cylc-dbviewer b/bin/cylc-dbviewer
index 43fe42f47..dad5a5173 100755
--- a/bin/cylc-dbviewer
+++ b/bin/cylc-dbviewer
@@ -27,7 +27,7 @@ from cylc.CylcOptionParsers import cop
 sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../lib')
 sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../')
 
-from cylc.global_config import globalcfg
+from cylc.global_config import gcfg
 from cylc.suite_logging import suite_log
 
 parser = cop( """cylc [db] dbviewer [OPTIONS]
@@ -59,8 +59,7 @@ from cylc.gui.db_viewer import MainApp
 # they attempt to determine their CWD.
 os.chdir(os.environ['HOME'])
 
-globals = globalcfg()
-tmpdir = globals.cfg['temporary directory']
+tmpdir = gcfg.get_tmpdir()
 
 app = MainApp( options.db, options.db_owner, tmpdir, options.pyro_timeout )
 gtk.main()
diff --git a/bin/cylc-documentation b/bin/cylc-documentation
index 9872a37a7..9515dcfc9 100755
--- a/bin/cylc-documentation
+++ b/bin/cylc-documentation
@@ -19,7 +19,7 @@
 import sys, os, re
 import subprocess
 from optparse import OptionParser
-from cylc.global_config import globalcfg
+from cylc.global_config import gcfg
 from OrderedDict import OrderedDict
 
 parser = OptionParser( """cylc [info] documentation|browse [OPTIONS]
@@ -32,7 +32,7 @@ documentation URL. The browser and PDF reader to use, and the intranet
 URL, is determined by cylc site/user configuration - for details see
   $ cylc get-global-config --help""" )
 
-globals = globalcfg()
+globals = gcfg
 
 intranet = globals.cfg['documentation']['urls']['local index']
 internet = globals.cfg['documentation']['urls']['internet homepage']
diff --git a/bin/cylc-edit b/bin/cylc-edit
index cca1837dd..b2bc690a1 100755
--- a/bin/cylc-edit
+++ b/bin/cylc-edit
@@ -25,7 +25,7 @@ import os, re
 import datetime
 from shutil import copy
 import subprocess
-from cylc.global_config import globalcfg
+from cylc.global_config import gcfg
 from cylc.CylcOptionParsers import cop
 from cylc.include_files import inline_for_editing, \
     split_file, backup, backups, newfiles, cleanup, modtimes
@@ -88,7 +88,7 @@ parser.add_option( "--gui", "-g",
 
 suite, suiterc = prep_file( args[0], options ).execute()
 
-globals = globalcfg()
+globals = gcfg
 if options.geditor:
     editor = globals.cfg['editors']['gui']
 else:
diff --git a/bin/cylc-get-global-config b/bin/cylc-get-global-config
index 5b73e7778..a41988e05 100755
--- a/bin/cylc-get-global-config
+++ b/bin/cylc-get-global-config
@@ -18,7 +18,7 @@
 
 import sys
 from optparse import OptionParser
-from cylc.global_config import globalcfg
+from cylc.global_config import gcfg
 
 parser = OptionParser( usage = """cylc [admin] get-global-config [OPTIONS] 
 
@@ -45,7 +45,7 @@ parser.add_option( "-u", "--write-user", help="Write a user "
 if len(args) != 0:
     parser.error( "ERROR: wrong number of arguments" )
 
-globals = globalcfg()
+globals = gcfg
 
 if options.writesite:
     globals.write_rc( 'site' )
diff --git a/bin/cylc-gui b/bin/cylc-gui
index 2d5031675..209427580 100755
--- a/bin/cylc-gui
+++ b/bin/cylc-gui
@@ -30,7 +30,7 @@ from copy import copy
 sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../lib')
 sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../')
 
-from cylc.global_config import globalcfg
+from cylc.global_config import gcfg
 from cylc.suite_host import is_remote_host
 from cylc.suite_logging import suite_log
 from cylc.owner import user, is_remote_user
@@ -81,8 +81,7 @@ warnings.filterwarnings('ignore', 'use the new', Warning)
 from cylc.gui.SuiteControl import ControlApp
 from cylc.gui.gcylc_config import config
 
-globals = globalcfg()
-cylc_tmpdir = globals.cfg['temporary directory']
+cylc_tmpdir = gcfg.get_tmpdir()
 
 # Make current working directory be $HOME. Otherwise (1) if the user
 # attempts to start gcylc from a CWD that has been removed, Pyro will
diff --git a/bin/cylc-lockserver b/bin/cylc-lockserver
index 860ba8a0a..96180d989 100755
--- a/bin/cylc-lockserver
+++ b/bin/cylc-lockserver
@@ -26,7 +26,7 @@ import Pyro.core, Pyro.errors, Pyro.naming
 from Pyro.ext.daemonizer import Daemonizer
 from ConfigParser import SafeConfigParser
 from cylc.suite_id import identifier
-from cylc.global_config import globalcfg
+from cylc.global_config import gcfg
 import logging
 
 class ServerDaemon( Daemonizer ):
@@ -75,7 +75,7 @@ class LServer( object ):
         #Pyro.config.PYRO_STDLOGGING = True
         #Pyro.config.PYRO_TRACELEVEL = 3
 
-        globals = globalcfg()
+        globals = gcfg
         base = globals.cfg['pyro']['base port']
         range = globals.cfg['pyro']['maximum number of ports']
 
diff --git a/bin/cylc-ping b/bin/cylc-ping
index f0b3325a2..efdb1d118 100755
--- a/bin/cylc-ping
+++ b/bin/cylc-ping
@@ -27,7 +27,7 @@ from cylc.CylcOptionParsers import cop
 from cylc.TaskID import TaskID, TaskIDError
 from cylc import cylc_pyro_client
 from cylc.command_prep import prep_pyro
-from cylc.global_config import globalcfg
+from cylc.global_config import gcfg
 
 parser = cop( """cylc [discover] ping [OPTIONS] ARGS
     
@@ -47,7 +47,7 @@ parser.add_option( "--print-ports",
 ( options, args ) = parser.parse_args()
 
 if options.print_ports:
-    globals = globalcfg()
+    globals = gcfg
     base = globals.cfg['pyro']['base port']
     range = globals.cfg['pyro']['maximum number of ports']
     print base, '<= port <=', base + range
diff --git a/bin/cylc-scan b/bin/cylc-scan
index c91f16f38..74afbda59 100755
--- a/bin/cylc-scan
+++ b/bin/cylc-scan
@@ -26,7 +26,7 @@ if '--use-ssh' in sys.argv[1:]:
 from cylc.port_scan import scan
 from cylc.suite_host import hostname
 from cylc.CylcOptionParsers import cop
-from cylc.global_config import globalcfg
+from cylc.global_config import gcfg
 
 parser = cop( """cylc [discover] scan [OPTIONS]
     
@@ -57,13 +57,13 @@ parser.remove_option("-f" ) # don't need force
 
 parser.add_option( "--print-ports",
         help="Print the port range from the site config file "
-        "($CYLC_DIR/conf/globals/cylc.rc).",
+        "($CYLC_DIR/conf/siterc/site.rc).",
         action="store_true",default=False, dest="print_ports" )
 
 ( options, args ) = parser.parse_args()
 
 if options.print_ports:
-    globals = globalcfg()
+    globals = gcfg
     base = globals.cfg['pyro']['base port']
     range = globals.cfg['pyro']['maximum number of ports']
     print base, '<= port <=', base + range
diff --git a/bin/cylc-view b/bin/cylc-view
index 97ff8cc0a..91e6c5f1d 100755
--- a/bin/cylc-view
+++ b/bin/cylc-view
@@ -24,7 +24,7 @@ if remrun().execute():
 import os, re
 import tempfile
 import subprocess
-from cylc.global_config import globalcfg
+from cylc.global_config import gcfg
 from cylc.CylcOptionParsers import cop
 from cylc.continuation_lines import join
 from cylc.Jinja2Support import Jinja2Process
@@ -39,7 +39,7 @@ editor, after optional include-file inlining and Jinja2 preprocessing.
 The edit process is spawned in the foreground as follows:
   % <editor> suite.rc
 Where <editor> is defined in the cylc site and user config files
-($CYLC_DIR/conf/globals/cylc.rc and $HOME/.cylc/cylc.rc).
+($CYLC_DIR/conf/siterc/site.rc and $HOME/.cylc/user.rc).
 
 For remote host or owner, the suite will be printed to stdout unless
 the '-g,--gui' flag is used to spawn a remote GUI edit session.
@@ -87,12 +87,11 @@ parser.add_option( "--stdout",
 
 suite, suiterc = prep_file( args[0], options ).execute()
 
-globals = globalcfg()
-cylc_tmpdir = globals.cfg['temporary directory']
+cylc_tmpdir = gcfg.get_tmpdir()
 if options.geditor:
-    editor = globals.cfg['editors']['gui']
+    editor = gcfg.cfg['editors']['gui']
 else:
-    editor = globals.cfg['editors']['in-terminal']
+    editor = gcfg.cfg['editors']['in-terminal']
 
 global suitedir
 suitedir = os.path.dirname(suiterc)
diff --git a/bin/gcapture b/bin/gcapture
index a9c6339f1..693394823 100755
--- a/bin/gcapture
+++ b/bin/gcapture
@@ -21,7 +21,7 @@ from optparse import OptionParser
 
 sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../lib')
 
-from cylc.global_config import globalcfg
+from cylc.global_config import gcfg
 
 # This is a unit test for $CYLC_DIR/lib/cylc/gui/gcapture.py
 # but it may be more generally useful.
@@ -69,8 +69,7 @@ if options.other:
 else:
     ignore_command = False
 
-globals = globalcfg()
-cylc_tmpdir = globals.cfg['temporary directory']
+cylc_tmpdir = gcfg.get_tmpdir()
 
 gobject.threads_init()
 if options.filep:
diff --git a/conf/siterc/cfgspec b/conf/siterc/cfgspec
index ac75c8b9b..20498d000 100644
--- a/conf/siterc/cfgspec
+++ b/conf/siterc/cfgspec
@@ -25,6 +25,7 @@
 
 # A temporary directory is needed by a few cylc commands. Leave it unset
 # to get the default system temporary directory (usually $TMPDIR).
+# Cylc temporary directories are automatically cleaned up on exit.
 temporary directory = string( default=None )
 
 # A rolling archive of suite state dumps is maintained for restart use.
diff --git a/lib/cylc/cylc_pyro_client.py b/lib/cylc/cylc_pyro_client.py
index cf8d50873..eb261ca84 100644
--- a/lib/cylc/cylc_pyro_client.py
+++ b/lib/cylc/cylc_pyro_client.py
@@ -28,7 +28,7 @@ from time import sleep
 from passphrase import passphrase
 from owner import user
 from port_file import port_retriever
-from global_config import globalcfg
+from global_config import gcfg
 
 class client( object ):
     def __init__( self, suite, pphrase=None, owner=user, host=hostname,
@@ -45,7 +45,7 @@ class client( object ):
 
         self.pphrase = pphrase
 
-        self.globals = globalcfg()
+        self.globals = gcfg
 
     def get_proxy( self, target ):
         if self.port:
diff --git a/lib/cylc/global_config.py b/lib/cylc/global_config.py
index d38ddedf1..95515db5a 100644
--- a/lib/cylc/global_config.py
+++ b/lib/cylc/global_config.py
@@ -6,7 +6,7 @@ from validate import Validator
 from print_cfg import print_cfg
 from mkdir_p import mkdir_p
 from copy import deepcopy
-##import atexit
+import atexit
 import shutil
 from tempfile import mkdtemp
 
@@ -152,20 +152,26 @@ class globalcfg( object ):
         print "File written:", target
         print "See in-file comments for customization information."
 
-    def process( self ):
-        # process temporary directory
-        cylc_tmpdir = self.cfg['temporary directory']
-        if not cylc_tmpdir:
-            # use tempfile.mkdtemp() to create a new temp directory
-            cylc_tmpdir = mkdtemp(prefix="cylc-")
-            ## can't use atexit in daemon mode!
-            ## self-cleanup
-            ##atexit.register(lambda: shutil.rmtree(cylc_tmpdir))
-            # now replace the original item
-            self.cfg['temporary directory'] = cylc_tmpdir
+    def get_tmpdir( self ):
+        # NOTE: if the cylc server program ever needs a temporary
+        # directory, this must be called AFTER forking to daemon mode,
+        # otherwise atexit() will delete the tmpdir when the initial
+        # process exits immediately after forking.
+
+        tdir = self.cfg['temporary directory']
+        if tdir:
+            tdir = self.proc_dir( tdir )
+            cylc_tmpdir = mkdtemp(prefix="cylc-", dir=tdir )
         else:
-            self.cfg['temporary directory'] = self.proc_dir( self.cfg['temporary directory'] )
+            cylc_tmpdir = mkdtemp(prefix="cylc-")
 
+        # self-cleanup
+        atexit.register(lambda: shutil.rmtree(cylc_tmpdir))
+
+        # now replace the original item to allow direct access
+        self.cfg['temporary directory'] = cylc_tmpdir
+
+    def process( self ):
         # expand environment variables and ~user in file paths
         for key,val in self.cfg['documentation']['files'].items():
             self.cfg['documentation']['files'][key] = os.path.expanduser( os.path.expandvars( val ))
@@ -298,3 +304,5 @@ class globalcfg( object ):
             self.proc_dir( path )
         return path
 
+gcfg = globalcfg()
+
diff --git a/lib/cylc/port_scan.py b/lib/cylc/port_scan.py
index c148645f4..0e2ac0b25 100644
--- a/lib/cylc/port_scan.py
+++ b/lib/cylc/port_scan.py
@@ -23,7 +23,7 @@ from passphrase import passphrase
 from registration import localdb
 import datetime
 import Pyro.errors, Pyro.core
-from global_config import globalcfg
+from global_config import gcfg
 
 class SuiteIdentificationError( Exception ):
     """
@@ -127,7 +127,7 @@ def cylcid_uri( host, port ):
 def get_port( suite, owner=user, host=hostname, pphrase=None, pyro_timeout=None, verbose=False ):
     # Scan ports until a particular suite is found.
 
-    globals = globalcfg()
+    globals = gcfg
     pyro_base_port = globals.cfg['pyro']['base port']
     pyro_port_range = globals.cfg['pyro']['maximum number of ports']
 
@@ -217,7 +217,7 @@ def scan( host=hostname, db=None, pyro_timeout=None, verbose=False, silent=False
     #print 'SCANNING PORTS'
     # scan all cylc Pyro ports for cylc suites
 
-    globals = globalcfg()
+    globals = gcfg
     pyro_base_port = globals.cfg['pyro']['base port']
     pyro_port_range = globals.cfg['pyro']['maximum number of ports']
 
diff --git a/lib/cylc/scheduler.py b/lib/cylc/scheduler.py
index 55d07fcda..775bda6c7 100644
--- a/lib/cylc/scheduler.py
+++ b/lib/cylc/scheduler.py
@@ -36,7 +36,7 @@ from locking.lockserver import lockserver
 from locking.suite_lock import suite_lock
 from suite_id import identifier
 from config import config, SuiteConfigError, TaskNotDefinedError
-from global_config import globalcfg
+from global_config import gcfg
 from port_file import port_file, PortFileExistsError, PortFileError
 from broker import broker
 from Pyro.errors import NamingError, ProtocolError
@@ -158,7 +158,7 @@ class scheduler(object):
         self.parse_commandline()
 
         # global config
-        self.globals = globalcfg()
+        self.globals = gcfg
 
         # create task log directory
         self.globals.get_task_log_dir( self.suite, create=True )
diff --git a/lib/cylc/suite_host.py b/lib/cylc/suite_host.py
index 6c78cd320..57575b7b4 100644
--- a/lib/cylc/suite_host.py
+++ b/lib/cylc/suite_host.py
@@ -17,7 +17,7 @@
 #C: along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import re, sys, socket
-from global_config import globalcfg
+from global_config import gcfg
 
 def get_local_ip_address( target ):
     """
@@ -67,7 +67,7 @@ returning the IP address associated with this socket.
     return ipaddr
 
 
-globals = globalcfg()
+globals = gcfg
 method = globals.cfg['suite host self-identification']['method']
 target = globals.cfg['suite host self-identification']['target']
 hardwired = globals.cfg['suite host self-identification']['host']
diff --git a/lib/cylc/suite_logging.py b/lib/cylc/suite_logging.py
index 13fc0bc8b..e5ebb4f37 100644
--- a/lib/cylc/suite_logging.py
+++ b/lib/cylc/suite_logging.py
@@ -18,7 +18,7 @@
 
 import os, sys, re
 import logging, logging.handlers
-from global_config import globalcfg
+from global_config import gcfg
 from mkdir_p import mkdir_p
 
 """Configure suite logging with the Python logging module, 'main'
@@ -38,7 +38,7 @@ class LogFilter(logging.Filter):
 
 class suite_log( object ):
     def __init__( self, suite, ext='suite' ):
-        globals = globalcfg()
+        globals = gcfg
         self.dir = globals.get_suite_log_dir( suite, ext )
         self.path = os.path.join( self.dir, 'log' ) 
         self.roll_at_startup = globals.cfg['suite logging']['roll over at start-up']
diff --git a/lib/cylc/suite_output.py b/lib/cylc/suite_output.py
index ba4d7f98d..f3094edbe 100644
--- a/lib/cylc/suite_output.py
+++ b/lib/cylc/suite_output.py
@@ -18,7 +18,7 @@
 
 import os, sys
 import logging, logging.handlers
-from global_config import globalcfg
+from global_config import gcfg
 from rolling_archive import rolling_archive
 
 """Configure suite stdout and stderr logs, as rolling archives, in a
@@ -27,7 +27,7 @@ get the configure log locations."""
 
 class suite_output( object ):
     def __init__( self, suite ):
-        globals = globalcfg()
+        globals = gcfg
         self.dir = os.path.join( globals.cfg['task hosts']['local']['run directory'], suite, 'log', 'suite' ) 
         self.opath = os.path.join( self.dir, 'out' ) 
         self.epath = os.path.join( self.dir, 'err' ) 
diff --git a/lib/cylc/suite_state_dumping.py b/lib/cylc/suite_state_dumping.py
index ccbf9c554..2d900bacb 100644
--- a/lib/cylc/suite_state_dumping.py
+++ b/lib/cylc/suite_state_dumping.py
@@ -17,7 +17,7 @@
 #C: along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import os
-from global_config import globalcfg
+from global_config import gcfg
 from rolling_archive import rolling_archive
 from mkdir_p import mkdir_p
 
@@ -28,7 +28,7 @@ class dumper( object ):
         self.clock = clock
         self.start_tag = start_tag
         self.stop_tag = stop_tag
-        globals = globalcfg()
+        globals = gcfg
         self.dir = os.path.join( globals.cfg['task hosts']['local']['run directory'], suite, 'state' ) 
         self.path = os.path.join( self.dir, 'state' ) 
         try:
diff --git a/lib/cylc/task_message.py b/lib/cylc/task_message.py
index b597b7c5f..b5d84271e 100644
--- a/lib/cylc/task_message.py
+++ b/lib/cylc/task_message.py
@@ -26,12 +26,12 @@ from time import sleep
 from remote import remrun
 from cylc.passphrase import passphrase
 from cylc.strftime import strftime
-from cylc.global_config import globalcfg
+from cylc.global_config import gcfg
 
 class message(object):
     def __init__( self, msg=None, priority='NORMAL', verbose=False ):
 
-        globals = globalcfg()
+        globals = gcfg
         self.retry_seconds = globals.cfg['task messaging']['retry interval in seconds']
         self.max_tries = globals.cfg['task messaging']['maximum number of tries']
         self.try_timeout = globals.cfg['task messaging']['connection timeout in seconds']
diff --git a/lib/cylc/task_types/task.py b/lib/cylc/task_types/task.py
index c33f942ac..cc4b21fac 100644
--- a/lib/cylc/task_types/task.py
+++ b/lib/cylc/task_types/task.py
@@ -34,7 +34,7 @@ from random import randrange
 from collections import deque
 from cylc import task_state
 from cylc.strftime import strftime
-from cylc.global_config import globalcfg
+from cylc.global_config import gcfg
 from cylc.owner import user
 from cylc.suite_host import hostname as suite_hostname
 import logging
@@ -146,7 +146,6 @@ class task( object ):
         self.message_queue = msgqueue()
         self.db_queue = []
 
-        gcfg = globalcfg()
         self.suite_name = os.environ['CYLC_SUITE_REG_NAME']
         self.db_path = os.path.join(gcfg.cfg['task hosts']['local']['run directory'], self.suite_name)
         self.db = cylc.rundb.CylcRuntimeDAO(suite_dir=self.db_path)
@@ -520,8 +519,6 @@ class task( object ):
             precommand = rtconfig['pre-command scripting'] 
             postcommand = rtconfig['post-command scripting'] 
 
-        gcfg = globalcfg()
-
         # Determine task host settings now, just before job submission,
         # because dynamic host selection may be used.
 
['bin/cylc-documentation', 'conf/siterc/cfgspec', 'bin/cylc-scan', 'lib/cylc/scheduler.py', 'lib/cylc/cylc_pyro_client.py', 'bin/cylc-gui', 'lib/cylc/suite_host.py', 'lib/cylc/suite_state_dumping.py', 'bin/cylc-edit', 'lib/cylc/task_types/task.py', 'lib/cylc/global_config.py', 'lib/cylc/task_message.py', 'lib/cylc/port_scan.py', 'lib/cylc/suite_logging.py', 'bin/cylc-view', 'bin/cylc-get-global-config', 'bin/gcapture', 'bin/cylc-lockserver', 'lib/cylc/suite_output.py', 'bin/cylc-dbviewer', 'bin/cylc-ping']