From 520cbb0165ba631b670ab79ac70596657f40c051 Mon Sep 17 00:00:00 2001 From: robk-tahoe Date: Wed, 9 Jan 2008 19:01:56 -0700 Subject: [PATCH] first stab at windows build details. there are many and various fiddly details that were involved in this process on mountain view. This is a stripped down version of the build process used there. there's hence a good chance that one or two necessary details got stripped down through the cracks. this provides a py2exe setup.py to build a tahoe.exe and a tahoesvc.exe the former is equivalent to bin/tahoe, but without the start/stop commands. the latter is a windows service that instantiates a client whose basedir is found in the registry. --- windows/registry.py | 77 +++++++++++++++++++++ windows/setup.py | 41 ++++++++++++ windows/tahoe.py | 2 + windows/tahoesvc.py | 158 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 278 insertions(+) create mode 100644 windows/registry.py create mode 100644 windows/setup.py create mode 100644 windows/tahoe.py create mode 100644 windows/tahoesvc.py diff --git a/windows/registry.py b/windows/registry.py new file mode 100644 index 00000000..2b87689b --- /dev/null +++ b/windows/registry.py @@ -0,0 +1,77 @@ +import sys +import _winreg + +_AMD_KEY = r"Software\Allmydata" +_BDIR_KEY = 'Base Dir Path' + +if sys.platform not in ('win32'): + raise ImportError, "registry cannot be used on non-windows systems" + class WindowsError(Exception): # stupid voodoo to appease pyflakes + pass + +def get_registry_setting(key, name, _topkey=None): + """ + This function iterates through _topkey (if not None), + HKEY_CURRENT_USER, and HKEY_LOCAL_MACHINE before giving up. + + @note: Only supports string values. + + @param key: The key we are searching. + @type key: String + + @param name: The name of the setting we are querying. + @type name: String + """ + topkeys = [_winreg.HKEY_CURRENT_USER, _winreg.HKEY_LOCAL_MACHINE] + + if _topkey: + topkeys.insert(0, _topkey) + + for topkey in topkeys: + try: + regkey = _winreg.OpenKey(topkey, key) + + sublen, vallen, timestamp = _winreg.QueryInfoKey(regkey) + for validx in xrange(vallen): + keyname, value, keytype = _winreg.EnumValue(regkey, validx) + if keyname == name and keytype == _winreg.REG_SZ: + return value + + except WindowsError: + continue + # We didn't find the key: + raise KeyError, (key, name, "registry setting not found") + +def set_registry_setting(key, name, data, reg_type=_winreg.REG_SZ, + _topkey=_winreg.HKEY_LOCAL_MACHINE, create_key_if_missing=True): + """ + Sets a registry setting. + + defaults to string values (REG_SZ) - overridable with reg_type. + """ + try: + regkey = _winreg.OpenKey(_topkey, key, 0, _winreg.KEY_SET_VALUE) + except WindowsError: + if create_key_if_missing: + regkey = _winreg.CreateKey(_topkey, key) + else: + raise KeyError, (key, "registry key not found") + + try: + _winreg.DeleteValue(regkey, name) + except: + pass + + _winreg.SetValueEx(regkey, name, 0, reg_type, data) + +def get_registry_value(keyname): + """ + retrieves a registry key value from within the Software/Allmydata Inc key + """ + try: + return get_registry_setting(_AMD_KEY, keyname) + except KeyError: + return None + +def get_base_dir_path(): + return get_registry_value(_BDIR_KEY) diff --git a/windows/setup.py b/windows/setup.py new file mode 100644 index 00000000..e93e1b20 --- /dev/null +++ b/windows/setup.py @@ -0,0 +1,41 @@ +from distutils.core import setup +import py2exe + +setup_args = { + 'name': 'Tahoe', + 'description': 'Allmydata Tahoe distributated storage', + 'author': 'Allmydata, Inc.', + 'windows': [ + ], + 'console': [ + 'tahoe.py', + ], + 'service': [ + 'tahoesvc', + ], + 'data_files': [ + ('.', [ + ],), + ], + 'zipfile' : 'library.zip', + 'options': { + "py2exe": { + "excludes": [ + ], + "includes": [ + ], + "packages": [ + "encodings", + "_xmlplus", + ], + #"optimize" : 2, + }, + }, +} + +if __name__ == '__main__': + setup(**setup_args) + + +_junk = py2exe # appease pyflakes +del _junk diff --git a/windows/tahoe.py b/windows/tahoe.py new file mode 100644 index 00000000..7c5d2d0b --- /dev/null +++ b/windows/tahoe.py @@ -0,0 +1,2 @@ +from allmydata.scripts import runner +runner.run(install_node_control=False) diff --git a/windows/tahoesvc.py b/windows/tahoesvc.py new file mode 100644 index 00000000..11eebb41 --- /dev/null +++ b/windows/tahoesvc.py @@ -0,0 +1,158 @@ +import sys +reload(sys) +sys.setdefaultencoding("utf-8") + +import win32serviceutil +import win32service +import win32event +import win32evtlogutil + +import os +import thread +import time +import traceback + +# this logging should go away once service startup is considered debugged. +logfilehandle = file('c:\\tahoe_service.log', 'ab+') +def logmsg(msg): + logfilehandle.write("%s: %s\r\n" % (time.strftime('%Y%m%d_%H%M%S'), msg)) + logfilehandle.flush() +logmsg('service loaded') + +# +# Now with some bootstrap util functions in place, let's try and init things: +try: + logmsg('loading base dir') + import registry + basedir = registry.get_base_dir_path() + logmsg("got base dir (%s)" % (basedir,)) + if not basedir: + regpth = "%s : %s " % (registry._AMD_KEY, registry._BDIR_KEY) + raise RuntimeError('"%s" not set in registry' % (regpth,)) + os.chdir(basedir) + logmsg("chdir(%s)" % (basedir,)) +except: + logmsg("exception") + traceback.print_exc(None, logfilehandle) + logfilehandle.flush() + logfilehandle.close() + raise + +class Tahoe(win32serviceutil.ServiceFramework): + _svc_name_ = "Tahoe" + _svc_display_name_ = "Allmydata Tahoe Node" + def __init__(self, args): + logmsg("init") + try: + # The exe-file has messages for the Event Log Viewer. + # Register the exe-file as event source. + # + # Probably it would be better if this is done at installation time, + # so that it also could be removed if the service is uninstalled. + # Unfortunately it cannot be done in the 'if __name__ == "__main__"' + # block below, because the 'frozen' exe-file does not run this code. + # + logmsg("service start") + win32evtlogutil.AddSourceToRegistry(self._svc_display_name_, + sys.executable, + "Application") + win32serviceutil.ServiceFramework.__init__(self, args) + self.hWaitStop = win32event.CreateEvent(None, 0, 0, None) + except: + try: + logmsg("exception") + traceback.print_exc(None, logfilehandle) + logfilehandle.flush() + logfilehandle.close() + except: + os.abort() + + def SvcStop(self): + logmsg("service stop") + self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) + win32event.SetEvent(self.hWaitStop) + + def SvcDoRun(self): + try: + logmsg("service run") + import servicemanager + # Write a 'started' event to the event log... + win32evtlogutil.ReportEvent(self._svc_display_name_, + servicemanager.PYS_SERVICE_STARTED, + 0, # category + servicemanager.EVENTLOG_INFORMATION_TYPE, + (self._svc_name_, '')) + + reactor_type = registry.get_registry_value('reactor') + if reactor_type == 'iocp': + from twisted.internet import iocpreactor + iocpreactor.install() + else: + from twisted.internet import selectreactor + selectreactor.install() + from twisted.internet import reactor + + if os.path.exists('DISABLE_STARTUP'): + logmsg("DISABLE_STARTUP exists: exiting") + else: + logmsg("runing reactorthread") + + # launch main thread... + thread.start_new_thread(self.launch_node, ()) + + # ...and block until service stop request + win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE) + + logmsg("wake up") + + reactor.callFromThread(self.app.shutdown) + reactor.callFromThread(reactor.stop) + + time.sleep(2) # give the node/reactor a chance to cleanup + + # and write a 'stopped' event to the event log. + win32evtlogutil.ReportEvent(self._svc_display_name_, + servicemanager.PYS_SERVICE_STOPPED, + 0, # category + servicemanager.EVENTLOG_INFORMATION_TYPE, + (self._svc_name_, '')) + except: + try: + logmsg("exception") + traceback.print_exc(None, logfilehandle) + logfilehandle.flush() + logfilehandle.close() + except: + os.abort() + + def launch_node(self): + try: + logmsg("main thread startup") + + from twisted.internet import reactor + from twisted.python import log, logfile + from allmydata import client + + # set up twisted logging. this will become part of the node rsn. + logdir = os.path.join(basedir, 'logs') + if not os.path.exists(logdir): + os.makedirs(logdir) + lf = logfile.LogFile('tahoesvc.log', logdir) + log.startLogging(lf) + + # run the node itself + c = client.Client(basedir) + reactor.callLater(c.startService) # after reactor startup + reactor.run(dont_bind_signals=True) # does a reactor.run() + + logmsg("main thread shutdown") + except: + logmsg("exception") + traceback.print_exc(None, logfilehandle) + logfilehandle.flush() + os.abort() + +if __name__ == '__main__': + logmsg("service main") + win32serviceutil.HandleCommandLine(Tahoe) + -- 2.45.2