1
# Copyright (c) 2012 OpenStack Foundation.
2
# Administrator of the National Aeronautics and Space Administration.
5
# Licensed under the Apache License, Version 2.0 (the "License"); you may
6
# not use this file except in compliance with the License. You may obtain
7
# a copy of the License at
9
# http://www.apache.org/licenses/LICENSE-2.0
11
# Unless required by applicable law or agreed to in writing, software
12
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
# License for the specific language governing permissions and limitations
17
from __future__ import print_function
29
import eventlet.backdoor
31
from oslo_config import cfg
33
from heat.openstack.common._i18n import _LI
35
help_for_backdoor_port = (
36
"Acceptable values are 0, <port>, and <start>:<end>, where 0 results "
37
"in listening on a random tcp port number; <port> results in listening "
38
"on the specified port number (and not enabling backdoor if that port "
39
"is in use); and <start>:<end> results in listening on the smallest "
40
"unused port number within the specified range of port numbers. The "
41
"chosen port is displayed in the service's log file.")
42
eventlet_backdoor_opts = [
43
cfg.StrOpt('backdoor_port',
44
help="Enable eventlet backdoor. %s" % help_for_backdoor_port)
48
CONF.register_opts(eventlet_backdoor_opts)
49
LOG = logging.getLogger(__name__)
53
"""Entry point for oslo-config-generator.
55
return [(None, copy.deepcopy(eventlet_backdoor_opts))]
58
class EventletBackdoorConfigValueError(Exception):
59
def __init__(self, port_range, help_msg, ex):
60
msg = ('Invalid backdoor_port configuration %(range)s: %(ex)s. '
62
{'range': port_range, 'ex': ex, 'help': help_msg})
63
super(EventletBackdoorConfigValueError, self).__init__(msg)
64
self.port_range = port_range
68
print("Don't use this, just disconnect instead")
72
return [o for o in gc.get_objects() if isinstance(o, t)]
75
def _print_greenthreads():
76
for i, gt in enumerate(_find_objects(greenlet.greenlet)):
78
traceback.print_stack(gt.gr_frame)
82
def _print_nativethreads():
83
for threadId, stack in sys._current_frames().items():
85
traceback.print_stack(stack)
89
def _parse_port_range(port_range):
90
if ':' not in port_range:
91
start, end = port_range, port_range
93
start, end = port_range.split(':', 1)
95
start, end = int(start), int(end)
99
except ValueError as ex:
100
raise EventletBackdoorConfigValueError(port_range, ex,
101
help_for_backdoor_port)
104
def _listen(host, start_port, end_port, listen_func):
105
try_port = start_port
108
return listen_func((host, try_port))
109
except socket.error as exc:
110
if (exc.errno != errno.EADDRINUSE or
111
try_port >= end_port):
116
def initialize_if_enabled():
118
'exit': _dont_use_this, # So we don't exit the entire process
119
'quit': _dont_use_this, # So we don't exit the entire process
121
'pgt': _print_greenthreads,
122
'pnt': _print_nativethreads,
125
if CONF.backdoor_port is None:
128
start_port, end_port = _parse_port_range(str(CONF.backdoor_port))
130
# NOTE(johannes): The standard sys.displayhook will print the value of
131
# the last expression and set it to __builtin__._, which overwrites
132
# the __builtin__._ that gettext sets. Let's switch to using pprint
133
# since it won't interact poorly with gettext, and it's easier to
134
# read the output too.
135
def displayhook(val):
138
sys.displayhook = displayhook
140
sock = _listen('localhost', start_port, end_port, eventlet.listen)
142
# In the case of backdoor port being zero, a port number is assigned by
143
# listen(). In any case, pull the port number out here.
144
port = sock.getsockname()[1]
146
_LI('Eventlet backdoor listening on %(port)s for process %(pid)d') %
147
{'port': port, 'pid': os.getpid()}
149
eventlet.spawn_n(eventlet.backdoor.backdoor_server, sock,
150
locals=backdoor_locals)