10 | | optFlags = [ |
11 | | ["profile", "p", "Run under the Python profiler, putting results in 'profiling_results.prof'."], |
12 | | ["syslog", None, "Tell the node to log to syslog, not a file."], |
13 | | ] |
| 11 | def parseArgs(self, basedir=None, *twistd_args): |
| 12 | # this can't handle e.g. 'tahoe start --nodaemon', since then |
| 13 | # --nodaemon looks like a basedir. So you can either use 'tahoe |
| 14 | # start' or 'tahoe start BASEDIR --TWISTD-OPTIONS'. |
| 15 | BasedirMixin.parseArgs(self, basedir) |
| 16 | if not os.path.isdir(self['basedir']): |
| 17 | raise usage.UsageError("--basedir '%s' doesn't exist" % |
| 18 | quote_output(self['basedir'])) |
| 19 | self.twistd_args = twistd_args |
45 | | def start(opts, out=sys.stdout, err=sys.stderr): |
46 | | basedir = opts['basedir'] |
47 | | print >>out, "STARTING", quote_output(basedir) |
48 | | if not os.path.isdir(basedir): |
49 | | print >>err, "%s does not look like a directory at all" % quote_output(basedir) |
50 | | return 1 |
| 39 | class MyTwistdConfig(twistd.ServerOptions): |
| 40 | subCommands = [("XYZ", None, usage.Options, "node")] |
| 41 | |
| 42 | class NodeStartingPlugin: |
| 43 | tapname = "xyznode" |
| 44 | def __init__(self, nodetype, basedir): |
| 45 | self.nodetype = nodetype |
| 46 | self.basedir = basedir |
| 47 | def makeService(self, so): |
| 48 | # delay this import as late as possible, to allow twistd's code to |
| 49 | # accept --reactor= selection. N.B.: this can't actually work until |
| 50 | # this file, and all the __init__.py files above it, also respect the |
| 51 | # prohibition on importing anything that transitively imports |
| 52 | # twisted.internet.reactor . That will take a lot of work. |
| 53 | if self.nodetype == "client": |
| 54 | from allmydata.client import Client |
| 55 | return Client(self.basedir) |
| 56 | if self.nodetype == "introducer": |
| 57 | from allmydata.introducer.server import IntroducerNode |
| 58 | return IntroducerNode(self.basedir) |
| 59 | if self.nodetype == "key-generator": |
| 60 | from allmydata.key_generator import KeyGeneratorService |
| 61 | return KeyGeneratorService(default_key_size=2048) |
| 62 | if self.nodetype == "stats-gatherer": |
| 63 | from allmydata.stats import StatsGathererService |
| 64 | return StatsGathererService(verbose=True) |
| 65 | raise ValueError("unknown nodetype %s" % self.nodetype) |
| 66 | |
| 67 | def identify_node_type(basedir): |
65 | | args = ["twistd", "-y", tac] |
66 | | if opts["syslog"]: |
67 | | args.append("--syslog") |
68 | | elif nodetype in ("client", "introducer"): |
69 | | fileutil.make_dirs(os.path.join(basedir, "logs")) |
70 | | args.extend(["--logfile", os.path.join("logs", "twistd.log")]) |
71 | | if opts["profile"]: |
72 | | args.extend(["--profile=profiling_results.prof", "--savestats",]) |
73 | | # now we're committed |
| 85 | def start(config, out=sys.stdout, err=sys.stderr): |
| 86 | basedir = config['basedir'] |
| 87 | print >>out, "STARTING", quote_output(basedir) |
| 88 | if not os.path.isdir(basedir): |
| 89 | print >>err, "%s does not look like a directory at all" % quote_output(basedir) |
| 90 | return 1 |
| 91 | nodetype = identify_node_type(basedir) |
| 92 | if not nodetype: |
| 93 | print >>err, "%s is not a recognizable node directory" % quote_output(basedir) |
| 94 | return 1 |
| 95 | # Now prepare to turn into a twistd process. This os.chdir is the point |
| 96 | # of no return. |
75 | | from twisted.scripts import twistd |
76 | | sys.argv = args |
77 | | twistd.run() |
78 | | # run() doesn't return: the parent does os._exit(0) in daemonize(), so |
79 | | # we'll never get here. If application setup fails (e.g. ImportError), |
80 | | # run() will raise an exception. |
| 98 | twistd_args = [] |
| 99 | if (nodetype in ("client", "introducer") |
| 100 | and "--nodaemon" not in config.twistd_args |
| 101 | and "--syslog" not in config.twistd_args |
| 102 | and "--logfile" not in config.twistd_args): |
| 103 | fileutil.make_dirs(os.path.join(basedir, "logs")) |
| 104 | twistd_args.extend(["--logfile", os.path.join("logs", "twistd.log")]) |
| 105 | twistd_args.extend(config.twistd_args) |
| 106 | twistd_args.append("XYZ") # point at our NodeStartingPlugin |
| 107 | |
| 108 | twistd_config = MyTwistdConfig() |
| 109 | try: |
| 110 | twistd_config.parseOptions(twistd_args) |
| 111 | except usage.error, ue: |
| 112 | # these arguments were unsuitable for 'twistd' |
| 113 | print >>err, twistd_config |
| 114 | print >>err, "tahoe start: %s" % (config.subCommand, ue) |
| 115 | return 1 |
| 116 | twistd_config.loadedPlugins = {"XYZ": NodeStartingPlugin(nodetype, basedir)} |
| 117 | |
| 118 | # Unless --nodaemon was provided, the twistd.runApp() below spawns off a |
| 119 | # child process, and the parent calls os._exit(0), so there's no way for |
| 120 | # us to get control afterwards, even with 'except SystemExit'. If |
| 121 | # application setup fails (e.g. ImportError), runApp() will raise an |
| 122 | # exception. |
| 123 | # |
| 124 | # So if we wanted to do anything with the running child, we'd have two |
| 125 | # options: |
| 126 | # |
| 127 | # * fork first, and have our child wait for the runApp() child to get |
| 128 | # running. (note: just fork(). This is easier than fork+exec, since we |
| 129 | # don't have to get PATH and PYTHONPATH set up, since we're not |
| 130 | # starting a *different* process, just cloning a new instance of the |
| 131 | # current process) |
| 132 | # * or have the user run a separate command some time after this one |
| 133 | # exits. |
| 134 | # |
| 135 | # For Tahoe, we don't need to do anything with the child, so we can just |
| 136 | # let it exit. |
| 137 | |
| 138 | verb = "starting" |
| 139 | if "--nodaemon" in twistd_args: |
| 140 | verb = "running" |
| 141 | print >>out, "%s node in %s" % (verb, basedir) |
| 142 | twistd.runApp(twistd_config) |
| 143 | # we should only reach here if --nodaemon was used |
| 144 | return 0 |
150 | | from twisted.internet import reactor |
151 | | from twisted.python import log, logfile |
152 | | from allmydata import client |
153 | | |
154 | | basedir = config['basedir'] |
155 | | precondition(isinstance(basedir, unicode), basedir) |
156 | | |
157 | | if not os.path.isdir(basedir): |
158 | | print >>stderr, "%s does not look like a directory at all" % quote_output(basedir) |
159 | | return 1 |
160 | | for fn in listdir_unicode(basedir): |
161 | | if fn.endswith(u".tac"): |
162 | | tac = str(fn) |
163 | | break |
164 | | else: |
165 | | print >>stderr, "%s does not look like a node directory (no .tac file)" % quote_output(basedir) |
166 | | return 1 |
167 | | if "client" not in tac: |
168 | | print >>stderr, ("%s looks like it contains a non-client node (%s).\n" |
169 | | "Use 'tahoe start' instead of 'tahoe run'." |
170 | | % (quote_output(basedir), tac)) |
171 | | return 1 |
172 | | |
173 | | os.chdir(basedir) |
174 | | |
175 | | # set up twisted logging. this will become part of the node rsn. |
176 | | logdir = os.path.join(basedir, 'logs') |
177 | | if not os.path.exists(logdir): |
178 | | os.makedirs(logdir) |
179 | | lf = logfile.LogFile('tahoesvc.log', logdir) |
180 | | log.startLogging(lf) |
181 | | |
182 | | # run the node itself |
183 | | c = client.Client(basedir) |
184 | | reactor.callLater(0, c.startService) # after reactor startup |
185 | | reactor.run() |
186 | | |
187 | | return 0 |
| 214 | config.twistd_args = config.twistd_args + ("--nodaemon",) |
| 215 | # also ("--logfile", "tahoesvc.log") |
| 216 | return start(config, stdout, stderr) |