supervisorctl.py 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341
  1. #!/usr/bin/env python -u
  2. """supervisorctl -- control applications run by supervisord from the cmd line.
  3. Usage: %s [options] [action [arguments]]
  4. Options:
  5. -c/--configuration FILENAME -- configuration file path (default /etc/supervisord.conf)
  6. -h/--help -- print usage message and exit
  7. -i/--interactive -- start an interactive shell after executing commands
  8. -s/--serverurl URL -- URL on which supervisord server is listening
  9. (default "http://localhost:9001").
  10. -u/--username USERNAME -- username to use for authentication with server
  11. -p/--password PASSWORD -- password to use for authentication with server
  12. -r/--history-file -- keep a readline history (if readline is available)
  13. action [arguments] -- see below
  14. Actions are commands like "tail" or "stop". If -i is specified or no action is
  15. specified on the command line, a "shell" interpreting actions typed
  16. interactively is started. Use the action "help" to find out about available
  17. actions.
  18. """
  19. import cmd
  20. import sys
  21. import getpass
  22. import socket
  23. import errno
  24. import threading
  25. from supervisor.compat import xmlrpclib
  26. from supervisor.compat import urlparse
  27. from supervisor.compat import unicode
  28. from supervisor.compat import raw_input
  29. from supervisor.medusa import asyncore_25 as asyncore
  30. from supervisor.options import ClientOptions
  31. from supervisor.options import make_namespec
  32. from supervisor.options import split_namespec
  33. from supervisor import xmlrpc
  34. from supervisor import states
  35. from supervisor import http_client
  36. class LSBInitErrorCode:
  37. GENERIC = 1
  38. INVALID_ARGS = 2
  39. UNIMPLEMENTED_FEATURE = 3
  40. INSUFFICIENT_PRIVLEDGES = 4
  41. NOT_INSTALLED = 5
  42. NOT_CONFIGURED = 6
  43. NOT_RUNNING = 7
  44. class LSBStatusErrorCode:
  45. DEAD_WITH_PID = 1
  46. DEAD_WITH_LOCK = 2
  47. NOT_RUNNING = 3
  48. UNKNOWN = 4
  49. class fgthread(threading.Thread):
  50. """ A subclass of threading.Thread, with a kill() method.
  51. To be used for foreground output/error streaming.
  52. http://mail.python.org/pipermail/python-list/2004-May/260937.html
  53. """
  54. def __init__(self, program, ctl):
  55. threading.Thread.__init__(self)
  56. self.killed = False
  57. self.program = program
  58. self.ctl = ctl
  59. self.listener = http_client.Listener()
  60. self.output_handler = http_client.HTTPHandler(self.listener,
  61. self.ctl.options.username,
  62. self.ctl.options.password)
  63. self.error_handler = http_client.HTTPHandler(self.listener,
  64. self.ctl.options.username,
  65. self.ctl.options.password)
  66. def start(self): # pragma: no cover
  67. # Start the thread
  68. self.__run_backup = self.run
  69. self.run = self.__run
  70. threading.Thread.start(self)
  71. def run(self): # pragma: no cover
  72. self.output_handler.get(self.ctl.options.serverurl,
  73. '/logtail/%s/stdout'%self.program)
  74. self.error_handler.get(self.ctl.options.serverurl,
  75. '/logtail/%s/stderr'%self.program)
  76. asyncore.loop()
  77. def __run(self): # pragma: no cover
  78. # Hacked run function, which installs the trace
  79. sys.settrace(self.globaltrace)
  80. self.__run_backup()
  81. self.run = self.__run_backup
  82. def globaltrace(self, frame, why, arg):
  83. if why == 'call':
  84. return self.localtrace
  85. else:
  86. return None
  87. def localtrace(self, frame, why, arg):
  88. if self.killed:
  89. if why == 'line':
  90. sys.exit(0)
  91. return self.localtrace
  92. def kill(self):
  93. self.output_handler.close()
  94. self.error_handler.close()
  95. self.killed = True
  96. class Controller(cmd.Cmd):
  97. def __init__(self, options, completekey='tab', stdin=None,
  98. stdout=None):
  99. self.options = options
  100. self.prompt = self.options.prompt + '> '
  101. self.options.plugins = []
  102. self.vocab = ['help']
  103. self._complete_info = None
  104. self.exit_status = None
  105. cmd.Cmd.__init__(self, completekey, stdin, stdout)
  106. for name, factory, kwargs in self.options.plugin_factories:
  107. plugin = factory(self, **kwargs)
  108. for a in dir(plugin):
  109. if a.startswith('do_') and callable(getattr(plugin, a)):
  110. self.vocab.append(a[3:])
  111. self.options.plugins.append(plugin)
  112. plugin.name = name
  113. def emptyline(self):
  114. # We don't want a blank line to repeat the last command.
  115. return
  116. def default(self, line):
  117. super(Controller, self).default(line)
  118. self.handle_error()
  119. def exec_cmdloop(self, args, options):
  120. try:
  121. import readline
  122. delims = readline.get_completer_delims()
  123. delims = delims.replace(':', '') # "group:process" as one word
  124. delims = delims.replace('*', '') # "group:*" as one word
  125. delims = delims.replace('-', '') # names with "-" as one word
  126. readline.set_completer_delims(delims)
  127. if options.history_file:
  128. try:
  129. readline.read_history_file(options.history_file)
  130. except IOError:
  131. pass
  132. def save():
  133. try:
  134. readline.write_history_file(options.history_file)
  135. except IOError:
  136. pass
  137. import atexit
  138. atexit.register(save)
  139. except ImportError:
  140. pass
  141. try:
  142. self.cmdqueue.append('status')
  143. self.cmdloop()
  144. except KeyboardInterrupt:
  145. self.output('')
  146. pass
  147. def handle_xmlrpc_fault_state(self, state_handler, result, ignore_state=None):
  148. code = result['status']
  149. result = state_handler(result)
  150. if code == ignore_state or code == xmlrpc.Faults.SUCCESS:
  151. self.output(result)
  152. elif code in xmlrpc.DEAD_PROGRAM_FAULTS:
  153. self.handle_error(message=result, code=LSBInitErrorCode.NOT_RUNNING)
  154. else:
  155. self.handle_error(message=result)
  156. def handle_error(self, message=None, fatal=False, code=None):
  157. if code is None:
  158. code = LSBInitErrorCode.GENERIC
  159. if message:
  160. self.output(message)
  161. if self.exit_status is None:
  162. self.exit_status = code
  163. if fatal:
  164. raise
  165. def onecmd(self, line):
  166. """ Override the onecmd method to:
  167. - catch and print all exceptions
  168. - allow for composite commands in interactive mode (foo; bar)
  169. - call 'do_foo' on plugins rather than ourself
  170. """
  171. result = self.onecmd_run(line)
  172. if self.options.exit_on_error and self.exit_status is not None:
  173. raise SystemExit(self.exit_status)
  174. return result
  175. def onecmd_run(self, line):
  176. origline = line
  177. lines = line.split(';') # don't filter(None, line.split), as we pop
  178. line = lines.pop(0)
  179. # stuffing the remainder into cmdqueue will cause cmdloop to
  180. # call us again for each command.
  181. self.cmdqueue.extend(lines)
  182. cmd, arg, line = self.parseline(line)
  183. if not line:
  184. return self.emptyline()
  185. if cmd is None:
  186. return self.default(line)
  187. self._complete_info = None
  188. self.lastcmd = line
  189. if cmd == '':
  190. return self.default(line)
  191. else:
  192. do_func = self._get_do_func(cmd)
  193. if do_func is None:
  194. return self.default(line)
  195. try:
  196. try:
  197. return do_func(arg)
  198. except xmlrpclib.ProtocolError as e:
  199. if e.errcode == 401:
  200. if self.options.interactive:
  201. self.output('Server requires authentication')
  202. username = raw_input('Username:')
  203. password = getpass.getpass(prompt='Password:')
  204. self.output('')
  205. self.options.username = username
  206. self.options.password = password
  207. return self.onecmd_run(origline)
  208. else:
  209. self.handle_error('Server requires authentication')
  210. else:
  211. self.handle_error(fatal=True)
  212. do_func(arg)
  213. except Exception:
  214. (file, fun, line), t, v, tbinfo = asyncore.compact_traceback()
  215. error = 'error: %s, %s: file: %s line: %s' % (t, v, file, line)
  216. self.handle_error(error)
  217. def _get_do_func(self, cmd):
  218. func_name = 'do_' + cmd
  219. func = getattr(self, func_name, None)
  220. if not func:
  221. for plugin in self.options.plugins:
  222. func = getattr(plugin, func_name, None)
  223. if func is not None:
  224. break
  225. return func
  226. def output(self, stuff):
  227. if stuff is not None:
  228. if isinstance(stuff, unicode):
  229. stuff = stuff.encode('utf-8')
  230. self.stdout.write(stuff + '\n')
  231. def get_supervisor(self):
  232. return self.get_server_proxy('supervisor')
  233. def get_server_proxy(self, namespace=None):
  234. proxy = self.options.getServerProxy()
  235. if namespace is None:
  236. return proxy
  237. else:
  238. return getattr(proxy, namespace)
  239. def upcheck(self):
  240. try:
  241. supervisor = self.get_supervisor()
  242. api = supervisor.getVersion() # deprecated
  243. from supervisor import rpcinterface
  244. if api != rpcinterface.API_VERSION:
  245. self.handle_error(
  246. 'Sorry, this version of supervisorctl expects to '
  247. 'talk to a server with API version %s, but the '
  248. 'remote version is %s.' % (rpcinterface.API_VERSION, api), code=LSBInitErrorCode.NOT_INSTALLED)
  249. return False
  250. except xmlrpclib.Fault as e:
  251. if e.faultCode == xmlrpc.Faults.UNKNOWN_METHOD:
  252. self.handle_error(
  253. 'Sorry, supervisord responded but did not recognize '
  254. 'the supervisor namespace commands that supervisorctl '
  255. 'uses to control it. Please check that the '
  256. '[rpcinterface:supervisor] section is enabled in the '
  257. 'configuration file (see sample.conf).', code=LSBInitErrorCode.UNIMPLEMENTED_FEATURE)
  258. return False
  259. self.handle_error(fatal=True)
  260. except socket.error as e:
  261. if e.args[0] == errno.ECONNREFUSED:
  262. self.handle_error(message='%s refused connection' % self.options.serverurl, code=LSBInitErrorCode.INSUFFICIENT_PRIVLEDGES)
  263. return False
  264. elif e.args[0] == errno.ENOENT:
  265. self.handle_error(message='%s no such file' % self.options.serverurl, code=LSBInitErrorCode.NOT_RUNNING)
  266. return False
  267. self.handle_error(fatal=True)
  268. return True
  269. def complete(self, text, state, line=None):
  270. """Completer function that Cmd will register with readline using
  271. readline.set_completer(). This function will be called by readline
  272. as complete(text, state) where text is a fragment to complete and
  273. state is an integer (0..n). Each call returns a string with a new
  274. completion. When no more are available, None is returned."""
  275. if line is None: # line is only set in tests
  276. import readline
  277. line = readline.get_line_buffer()
  278. # take the last phrase from a line like "stop foo; start bar"
  279. phrase = line.split(';')[-1]
  280. matches = []
  281. # blank phrase completes to action list
  282. if not phrase.strip():
  283. matches = self._complete_actions(text)
  284. else:
  285. words = phrase.split()
  286. action = words[0]
  287. # incomplete action completes to action list
  288. if len(words) == 1 and not phrase.endswith(' '):
  289. matches = self._complete_actions(text)
  290. # actions that accept an action name
  291. elif action in ('help'):
  292. matches = self._complete_actions(text)
  293. # actions that accept a group name
  294. elif action in ('add', 'remove', 'update'):
  295. matches = self._complete_groups(text)
  296. # actions that accept a process name
  297. elif action in ('clear', 'fg', 'pid', 'restart', 'signal',
  298. 'start', 'status', 'stop', 'tail'):
  299. matches = self._complete_processes(text)
  300. if len(matches) > state:
  301. return matches[state]
  302. def _complete_actions(self, text):
  303. """Build a completion list of action names matching text"""
  304. return [ a + ' ' for a in self.vocab if a.startswith(text)]
  305. def _complete_groups(self, text):
  306. """Build a completion list of group names matching text"""
  307. groups = []
  308. for info in self._get_complete_info():
  309. if info['group'] not in groups:
  310. groups.append(info['group'])
  311. return [ g + ' ' for g in groups if g.startswith(text) ]
  312. def _complete_processes(self, text):
  313. """Build a completion list of process names matching text"""
  314. processes = []
  315. for info in self._get_complete_info():
  316. if ':' in text or info['name'] != info['group']:
  317. processes.append('%s:%s' % (info['group'], info['name']))
  318. if '%s:*' % info['group'] not in processes:
  319. processes.append('%s:*' % info['group'])
  320. else:
  321. processes.append(info['name'])
  322. return [ p + ' ' for p in processes if p.startswith(text) ]
  323. def _get_complete_info(self):
  324. """Get all process info used for completion. We cache this between
  325. commands to reduce XML-RPC calls because readline may call
  326. complete() many times if the user hits tab only once."""
  327. if self._complete_info is None:
  328. self._complete_info = self.get_supervisor().getAllProcessInfo()
  329. return self._complete_info
  330. def do_help(self, arg):
  331. if arg.strip() == 'help':
  332. self.help_help()
  333. else:
  334. for plugin in self.options.plugins:
  335. plugin.do_help(arg)
  336. def help_help(self):
  337. self.output("help\t\tPrint a list of available actions")
  338. self.output("help <action>\tPrint help for <action>")
  339. def do_EOF(self, arg):
  340. self.output('')
  341. return 1
  342. def help_EOF(self):
  343. self.output("To quit, type ^D or use the quit command")
  344. def get_names(inst):
  345. names = []
  346. classes = [inst.__class__]
  347. while classes:
  348. aclass = classes.pop(0)
  349. if aclass.__bases__:
  350. classes = classes + list(aclass.__bases__)
  351. names = names + dir(aclass)
  352. return names
  353. class ControllerPluginBase:
  354. name = 'unnamed'
  355. def __init__(self, controller):
  356. self.ctl = controller
  357. def _doc_header(self):
  358. return "%s commands (type help <topic>):" % self.name
  359. doc_header = property(_doc_header)
  360. def do_help(self, arg):
  361. if arg:
  362. # XXX check arg syntax
  363. try:
  364. func = getattr(self, 'help_' + arg)
  365. except AttributeError:
  366. try:
  367. doc = getattr(self, 'do_' + arg).__doc__
  368. if doc:
  369. self.ctl.output(doc)
  370. return
  371. except AttributeError:
  372. pass
  373. self.ctl.output(self.ctl.nohelp % (arg,))
  374. return
  375. func()
  376. else:
  377. names = get_names(self)
  378. cmds_doc = []
  379. cmds_undoc = []
  380. help = {}
  381. for name in names:
  382. if name[:5] == 'help_':
  383. help[name[5:]]=1
  384. names.sort()
  385. # There can be duplicates if routines overridden
  386. prevname = ''
  387. for name in names:
  388. if name[:3] == 'do_':
  389. if name == prevname:
  390. continue
  391. prevname = name
  392. cmd=name[3:]
  393. if cmd in help:
  394. cmds_doc.append(cmd)
  395. del help[cmd]
  396. elif getattr(self, name).__doc__:
  397. cmds_doc.append(cmd)
  398. else:
  399. cmds_undoc.append(cmd)
  400. self.ctl.output('')
  401. self.ctl.print_topics(self.doc_header, cmds_doc, 15, 80)
  402. class DefaultControllerPlugin(ControllerPluginBase):
  403. name = 'default'
  404. listener = None # for unit tests
  405. def _tailf(self, path):
  406. self.ctl.output('==> Press Ctrl-C to exit <==')
  407. username = self.ctl.options.username
  408. password = self.ctl.options.password
  409. handler = None
  410. try:
  411. # Python's urllib2 (at least as of Python 2.4.2) isn't up
  412. # to this task; it doesn't actually implement a proper
  413. # HTTP/1.1 client that deals with chunked responses (it
  414. # always sends a Connection: close header). We use a
  415. # homegrown client based on asyncore instead. This makes
  416. # me sad.
  417. if self.listener is None:
  418. listener = http_client.Listener()
  419. else:
  420. listener = self.listener # for unit tests
  421. handler = http_client.HTTPHandler(listener, username, password)
  422. handler.get(self.ctl.options.serverurl, path)
  423. asyncore.loop()
  424. except KeyboardInterrupt:
  425. if handler:
  426. handler.close()
  427. self.ctl.output('')
  428. return
  429. def do_tail(self, arg):
  430. if not self.ctl.upcheck():
  431. return
  432. args = arg.split()
  433. if len(args) < 1:
  434. self.handle_error('Error: too few arguments')
  435. self.help_tail()
  436. return
  437. elif len(args) > 3:
  438. self.handle_error('Error: too many arguments')
  439. self.help_tail()
  440. return
  441. modifier = None
  442. if args[0].startswith('-'):
  443. modifier = args.pop(0)
  444. if len(args) == 1:
  445. name = args[-1]
  446. channel = 'stdout'
  447. else:
  448. if args:
  449. name = args[0]
  450. channel = args[-1].lower()
  451. if channel not in ('stderr', 'stdout'):
  452. self.handle_error('Error: bad channel %r' % channel)
  453. return
  454. else:
  455. self.handle_error('Error: tail requires process name')
  456. return
  457. bytes = 1600
  458. if modifier is not None:
  459. what = modifier[1:]
  460. if what == 'f':
  461. bytes = None
  462. else:
  463. try:
  464. bytes = int(what)
  465. except:
  466. self.handle_error('Error: bad argument %s' % modifier)
  467. return
  468. supervisor = self.ctl.get_supervisor()
  469. if bytes is None:
  470. return self._tailf('/logtail/%s/%s' % (name, channel))
  471. else:
  472. try:
  473. if channel is 'stdout':
  474. output = supervisor.readProcessStdoutLog(name,
  475. -bytes, 0)
  476. else: # if channel is 'stderr'
  477. output = supervisor.readProcessStderrLog(name,
  478. -bytes, 0)
  479. except xmlrpclib.Fault as e:
  480. template = '%s: ERROR (%s)'
  481. if e.faultCode == xmlrpc.Faults.NO_FILE:
  482. self.handle_error(template % (name, 'no log file'))
  483. elif e.faultCode == xmlrpc.Faults.FAILED:
  484. self.handle_error(template % (name,
  485. 'unknown error reading log'))
  486. elif e.faultCode == xmlrpc.Faults.BAD_NAME:
  487. self.handle_error(template % (name,
  488. 'no such process name'))
  489. else:
  490. self.handle_error(fatal=True)
  491. else:
  492. self.ctl.output(output)
  493. def help_tail(self):
  494. self.ctl.output(
  495. "tail [-f] <name> [stdout|stderr] (default stdout)\n"
  496. "Ex:\n"
  497. "tail -f <name>\t\tContinuous tail of named process stdout\n"
  498. "\t\t\tCtrl-C to exit.\n"
  499. "tail -100 <name>\tlast 100 *bytes* of process stdout\n"
  500. "tail <name> stderr\tlast 1600 *bytes* of process stderr"
  501. )
  502. def do_maintail(self, arg):
  503. if not self.ctl.upcheck():
  504. return
  505. args = arg.split()
  506. if len(args) > 1:
  507. self.handle_error('Error: too many arguments')
  508. self.help_maintail()
  509. return
  510. elif len(args) == 1:
  511. if args[0].startswith('-'):
  512. what = args[0][1:]
  513. if what == 'f':
  514. path = '/mainlogtail'
  515. return self._tailf(path)
  516. try:
  517. what = int(what)
  518. except:
  519. self.handle_error('Error: bad argument %s' % args[0])
  520. return
  521. else:
  522. bytes = what
  523. else:
  524. self.handle_error('Error: bad argument %s' % args[0])
  525. return
  526. else:
  527. bytes = 1600
  528. supervisor = self.ctl.get_supervisor()
  529. try:
  530. output = supervisor.readLog(-bytes, 0)
  531. except xmlrpclib.Fault as e:
  532. template = '%s: ERROR (%s)'
  533. if e.faultCode == xmlrpc.Faults.NO_FILE:
  534. self.handle_error(template % ('supervisord', 'no log file'))
  535. elif e.faultCode == xmlrpc.Faults.FAILED:
  536. self.handle_error(template % ('supervisord',
  537. 'unknown error reading log'))
  538. else:
  539. self.handle_error(fatal=True)
  540. else:
  541. self.ctl.output(output)
  542. def help_maintail(self):
  543. self.ctl.output(
  544. "maintail -f \tContinuous tail of supervisor main log file"
  545. " (Ctrl-C to exit)\n"
  546. "maintail -100\tlast 100 *bytes* of supervisord main log file\n"
  547. "maintail\tlast 1600 *bytes* of supervisor main log file\n"
  548. )
  549. def do_quit(self, arg):
  550. sys.exit(0)
  551. def help_quit(self):
  552. self.ctl.output("quit\tExit the supervisor shell.")
  553. do_exit = do_quit
  554. def help_exit(self):
  555. self.ctl.output("exit\tExit the supervisor shell.")
  556. def _show_statuses(self, process_infos):
  557. namespecs, maxlen = [], 30
  558. for i, info in enumerate(process_infos):
  559. namespecs.append(make_namespec(info['group'], info['name']))
  560. if len(namespecs[i]) > maxlen:
  561. maxlen = len(namespecs[i])
  562. template = '%(namespec)-' + str(maxlen+3) + 's%(state)-10s%(desc)s'
  563. for i, info in enumerate(process_infos):
  564. line = template % {'namespec': namespecs[i],
  565. 'state': info['statename'],
  566. 'desc': info['description']}
  567. self.ctl.output(line)
  568. def do_status(self, arg, supress_exit_status=False):
  569. """In case upcheck sets an error_status we sanitize it for do_status call which should only return 4
  570. for this case."""
  571. exit_status = self.ctl.exit_status
  572. if not self.ctl.upcheck():
  573. if exit_status is not None:
  574. self.ctl.exit_status = LSBStatusErrorCode.UNKNOWN
  575. return
  576. supervisor = self.ctl.get_supervisor()
  577. all_infos = supervisor.getAllProcessInfo()
  578. names = arg.split()
  579. if not names or "all" in names:
  580. matching_infos = all_infos
  581. else:
  582. matching_infos = []
  583. for name in names:
  584. bad_name = True
  585. group_name, process_name = split_namespec(name)
  586. for info in all_infos:
  587. matched = info['group'] == group_name
  588. if process_name is not None:
  589. matched = matched and info['name'] == process_name
  590. if matched:
  591. bad_name = False
  592. matching_infos.append(info)
  593. if bad_name:
  594. if process_name is None:
  595. msg = "%s: ERROR (no such group)" % group_name
  596. else:
  597. msg = "%s: ERROR (no such process)" % name
  598. self.ctl.handle_error(msg, code=LSBStatusErrorCode.UNKNOWN)
  599. self._show_statuses(matching_infos)
  600. # Special case where we consider a status call that contains a stopped status to be an error.
  601. if not supress_exit_status:
  602. for info in matching_infos:
  603. if info['state'] in states.STOPPED_STATES:
  604. self.ctl.handle_error(code=LSBStatusErrorCode.NOT_RUNNING)
  605. def help_status(self):
  606. self.ctl.output("status <name>\t\tGet status for a single process")
  607. self.ctl.output("status <gname>:*\tGet status for all "
  608. "processes in a group")
  609. self.ctl.output("status <name> <name>\tGet status for multiple named "
  610. "processes")
  611. self.ctl.output("status\t\t\tGet all process status info")
  612. def do_pid(self, arg):
  613. supervisor = self.ctl.get_supervisor()
  614. if not self.ctl.upcheck():
  615. return
  616. names = arg.split()
  617. if not names:
  618. pid = supervisor.getPID()
  619. self.ctl.output(str(pid))
  620. elif 'all' in names:
  621. for info in supervisor.getAllProcessInfo():
  622. self.ctl.output(str(info['pid']))
  623. else:
  624. for name in names:
  625. try:
  626. info = supervisor.getProcessInfo(name)
  627. except xmlrpclib.Fault as e:
  628. if e.faultCode == xmlrpc.Faults.BAD_NAME:
  629. self.ctl.handle_error('No such process %s' % name)
  630. else:
  631. self.ctl.handle_error(fatal=True)
  632. else:
  633. self.ctl.output(str(info['pid']))
  634. def help_pid(self):
  635. self.ctl.output("pid\t\t\tGet the PID of supervisord.")
  636. self.ctl.output("pid <name>\t\tGet the PID of a single "
  637. "child process by name.")
  638. self.ctl.output("pid all\t\t\tGet the PID of every child "
  639. "process, one per line.")
  640. def _startresult(self, result):
  641. name = make_namespec(result['group'], result['name'])
  642. code = result['status']
  643. template = '%s: ERROR (%s)'
  644. if code == xmlrpc.Faults.BAD_NAME:
  645. return template % (name, 'no such process')
  646. elif code == xmlrpc.Faults.NO_FILE:
  647. return template % (name, 'no such file')
  648. elif code == xmlrpc.Faults.NOT_EXECUTABLE:
  649. return template % (name, 'file is not executable')
  650. elif code == xmlrpc.Faults.ALREADY_STARTED:
  651. return template % (name, 'already started')
  652. elif code == xmlrpc.Faults.SPAWN_ERROR:
  653. return template % (name, 'spawn error')
  654. elif code == xmlrpc.Faults.ABNORMAL_TERMINATION:
  655. return template % (name, 'abnormal termination')
  656. elif code == xmlrpc.Faults.SUCCESS:
  657. return '%s: started' % name
  658. # assertion
  659. raise ValueError('Unknown result code %s for %s' % (code, name))
  660. def do_start(self, arg):
  661. if not self.ctl.upcheck():
  662. return
  663. names = arg.split()
  664. supervisor = self.ctl.get_supervisor()
  665. if not names:
  666. self.handle_error("Error: start requires a process name", code=LSBInitErrorCode.INVALID_ARGS)
  667. self.help_start()
  668. return
  669. if 'all' in names:
  670. results = supervisor.startAllProcesses()
  671. for result in results:
  672. self.ctl.handle_xmlrpc_fault_state(self._startresult, result, xmlrpc.Faults.ALREADY_STARTED)
  673. else:
  674. for name in names:
  675. group_name, process_name = split_namespec(name)
  676. if process_name is None:
  677. try:
  678. results = supervisor.startProcessGroup(group_name)
  679. for result in results:
  680. self.ctl.handle_xmlrpc_fault_state(self._startresult, result, xmlrpc.Faults.ALREADY_STARTED)
  681. except xmlrpclib.Fault as e:
  682. if e.faultCode == xmlrpc.Faults.BAD_NAME:
  683. error = "%s: ERROR (no such group)" % group_name
  684. self.handle_error(error, code=LSBInitErrorCode.INVALID_ARGS)
  685. else:
  686. self.handle_error(fatal=True)
  687. else:
  688. try:
  689. result = supervisor.startProcess(name)
  690. except xmlrpclib.Fault as e:
  691. error = {'status': e.faultCode,
  692. 'name': process_name,
  693. 'group': group_name,
  694. 'description': e.faultString}
  695. self.ctl.handle_xmlrpc_fault_state(self._startresult, error, xmlrpc.Faults.ALREADY_STARTED)
  696. else:
  697. name = make_namespec(group_name, process_name)
  698. self.ctl.output('%s: started' % name)
  699. def help_start(self):
  700. self.ctl.output("start <name>\t\tStart a process")
  701. self.ctl.output("start <gname>:*\t\tStart all processes in a group")
  702. self.ctl.output(
  703. "start <name> <name>\tStart multiple processes or groups")
  704. self.ctl.output("start all\t\tStart all processes")
  705. def _signalresult(self, result, success='signalled'):
  706. name = make_namespec(result['group'], result['name'])
  707. code = result['status']
  708. fault_string = result['description']
  709. template = '%s: ERROR (%s)'
  710. if code == xmlrpc.Faults.BAD_NAME:
  711. return template % (name, 'no such process')
  712. elif code == xmlrpc.Faults.BAD_SIGNAL:
  713. return template % (name, 'bad signal name')
  714. elif code == xmlrpc.Faults.NOT_RUNNING:
  715. return template % (name, 'not running')
  716. elif code == xmlrpc.Faults.SUCCESS:
  717. return '%s: %s' % (name, success)
  718. elif code == xmlrpc.Faults.FAILED:
  719. return fault_string
  720. # assertion
  721. raise ValueError('Unknown result code %s for %s' % (code, name))
  722. def _stopresult(self, result):
  723. return self._signalresult(result, success='stopped')
  724. def do_stop(self, arg):
  725. if not self.ctl.upcheck():
  726. return
  727. names = arg.split()
  728. supervisor = self.ctl.get_supervisor()
  729. if not names:
  730. self.handle_error('Error: stop requires a process name')
  731. self.help_stop()
  732. return
  733. if 'all' in names:
  734. results = supervisor.stopAllProcesses()
  735. for result in results:
  736. self.ctl.handle_xmlrpc_fault_state(self._stopresult, result, xmlrpc.Faults.NOT_RUNNING)
  737. else:
  738. for name in names:
  739. group_name, process_name = split_namespec(name)
  740. if process_name is None:
  741. try:
  742. results = supervisor.stopProcessGroup(group_name)
  743. for result in results:
  744. self.ctl.handle_xmlrpc_fault_state(self._stopresult, result, xmlrpc.Faults.NOT_RUNNING)
  745. except xmlrpclib.Fault as e:
  746. if e.faultCode == xmlrpc.Faults.BAD_NAME:
  747. error = "%s: ERROR (no such group)" % group_name
  748. self.ctl.handle_error(message=error)
  749. else:
  750. self.ctl.handle_error(fatal=True)
  751. else:
  752. try:
  753. supervisor.stopProcess(name)
  754. except xmlrpclib.Fault as e:
  755. error = {'status': e.faultCode,
  756. 'name': process_name,
  757. 'group': group_name,
  758. 'description':e.faultString}
  759. self.ctl.handle_xmlrpc_fault_state(self._stopresult, error, xmlrpc.Faults.NOT_RUNNING)
  760. else:
  761. name = make_namespec(group_name, process_name)
  762. self.ctl.output('%s: stopped' % name)
  763. def help_stop(self):
  764. self.ctl.output("stop <name>\t\tStop a process")
  765. self.ctl.output("stop <gname>:*\t\tStop all processes in a group")
  766. self.ctl.output("stop <name> <name>\tStop multiple processes or groups")
  767. self.ctl.output("stop all\t\tStop all processes")
  768. def do_signal(self, arg):
  769. if not self.ctl.upcheck():
  770. return
  771. args = arg.split()
  772. if len(args) < 2:
  773. self.ctl.output(
  774. 'Error: signal requires a signal name and a process name')
  775. self.help_signal()
  776. self.handle_error()
  777. return
  778. sig = args[0]
  779. names = args[1:]
  780. supervisor = self.ctl.get_supervisor()
  781. if 'all' in names:
  782. results = supervisor.signalAllProcesses(sig)
  783. for result in results:
  784. self.ctl.handle_xmlrpc_fault_state(self._signalresult, result)
  785. else:
  786. for name in names:
  787. group_name, process_name = split_namespec(name)
  788. if process_name is None:
  789. try:
  790. results = supervisor.signalProcessGroup(
  791. group_name, sig
  792. )
  793. for result in results:
  794. self.ctl.handle_xmlrpc_fault_state(self._signalresult, result)
  795. except xmlrpclib.Fault as e:
  796. if e.faultCode == xmlrpc.Faults.BAD_NAME:
  797. error = "%s: ERROR (no such group)" % group_name
  798. self.ctl.handle_error(error)
  799. else:
  800. raise
  801. else:
  802. try:
  803. supervisor.signalProcess(name, sig)
  804. except xmlrpclib.Fault as e:
  805. error = {'status': e.faultCode,
  806. 'name': process_name,
  807. 'group': group_name,
  808. 'description':e.faultString}
  809. self.ctl.handle_xmlrpc_fault_state(self._signalresult, error)
  810. else:
  811. name = make_namespec(group_name, process_name)
  812. self.ctl.output('%s: signalled' % name)
  813. def help_signal(self):
  814. self.ctl.output("signal <signal name> <name>\t\tSignal a process")
  815. self.ctl.output("signal <signal name> <gname>:*\t\tSignal all processes in a group")
  816. self.ctl.output("signal <signal name> <name> <name>\tSignal multiple processes or groups")
  817. self.ctl.output("signal <signal name> all\t\tSignal all processes")
  818. def do_restart(self, arg):
  819. if not self.ctl.upcheck():
  820. return
  821. names = arg.split()
  822. if not names:
  823. self.handle_error('Error: restart requires a process name')
  824. self.help_restart()
  825. return
  826. self.do_stop(arg)
  827. self.do_start(arg)
  828. def help_restart(self):
  829. self.ctl.output("restart <name>\t\tRestart a process")
  830. self.ctl.output("restart <gname>:*\tRestart all processes in a group")
  831. self.ctl.output("restart <name> <name>\tRestart multiple processes or "
  832. "groups")
  833. self.ctl.output("restart all\t\tRestart all processes")
  834. self.ctl.output("Note: restart does not reread config files. For that,"
  835. " see reread and update.")
  836. def do_shutdown(self, arg):
  837. if self.ctl.options.interactive:
  838. yesno = raw_input('Really shut the remote supervisord process '
  839. 'down y/N? ')
  840. really = yesno.lower().startswith('y')
  841. else:
  842. really = 1
  843. if really:
  844. supervisor = self.ctl.get_supervisor()
  845. try:
  846. supervisor.shutdown()
  847. except xmlrpclib.Fault as e:
  848. if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
  849. self.ctl.output('ERROR: already shutting down')
  850. else:
  851. self.handle_error(fatal=True)
  852. except socket.error as e:
  853. if e.args[0] == errno.ECONNREFUSED:
  854. msg = 'ERROR: %s refused connection (already shut down?)'
  855. self.handle_error(msg % self.ctl.options.serverurl)
  856. elif e.args[0] == errno.ENOENT:
  857. msg = 'ERROR: %s no such file (already shut down?)'
  858. self.handle_error(msg % self.ctl.options.serverurl)
  859. else:
  860. self.handle_error(fatal=True)
  861. else:
  862. self.ctl.output('Shut down')
  863. def help_shutdown(self):
  864. self.ctl.output("shutdown \tShut the remote supervisord down.")
  865. def do_reload(self, arg):
  866. if self.ctl.options.interactive:
  867. yesno = raw_input('Really restart the remote supervisord process '
  868. 'y/N? ')
  869. really = yesno.lower().startswith('y')
  870. else:
  871. really = 1
  872. if really:
  873. supervisor = self.ctl.get_supervisor()
  874. try:
  875. supervisor.restart()
  876. except xmlrpclib.Fault as e:
  877. if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
  878. self.handle_error('ERROR: already shutting down')
  879. else:
  880. self.handle_error(fatal=True)
  881. else:
  882. self.ctl.output('Restarted supervisord')
  883. def help_reload(self):
  884. self.ctl.output("reload \t\tRestart the remote supervisord.")
  885. def _formatChanges(self, added_changed_dropped_tuple):
  886. added, changed, dropped = added_changed_dropped_tuple
  887. changedict = {}
  888. for n, t in [(added, 'available'),
  889. (changed, 'changed'),
  890. (dropped, 'disappeared')]:
  891. changedict.update(dict(zip(n, [t] * len(n))))
  892. if changedict:
  893. names = list(changedict.keys())
  894. names.sort()
  895. for name in names:
  896. self.ctl.output("%s: %s" % (name, changedict[name]))
  897. else:
  898. self.ctl.output("No config updates to processes")
  899. def _formatConfigInfo(self, configinfo):
  900. name = make_namespec(configinfo['group'], configinfo['name'])
  901. formatted = { 'name': name }
  902. if configinfo['inuse']:
  903. formatted['inuse'] = 'in use'
  904. else:
  905. formatted['inuse'] = 'avail'
  906. if configinfo['autostart']:
  907. formatted['autostart'] = 'auto'
  908. else:
  909. formatted['autostart'] = 'manual'
  910. formatted['priority'] = "%s:%s" % (configinfo['group_prio'],
  911. configinfo['process_prio'])
  912. template = '%(name)-32s %(inuse)-9s %(autostart)-9s %(priority)s'
  913. return template % formatted
  914. def do_avail(self, arg):
  915. supervisor = self.ctl.get_supervisor()
  916. try:
  917. configinfo = supervisor.getAllConfigInfo()
  918. except xmlrpclib.Fault as e:
  919. if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
  920. self.handle_error('ERROR: supervisor shutting down')
  921. else:
  922. self.handle_error(fatal=True)
  923. else:
  924. for pinfo in configinfo:
  925. self.ctl.output(self._formatConfigInfo(pinfo))
  926. def help_avail(self):
  927. self.ctl.output("avail\t\t\tDisplay all configured processes")
  928. def do_reread(self, arg):
  929. supervisor = self.ctl.get_supervisor()
  930. try:
  931. result = supervisor.reloadConfig()
  932. except xmlrpclib.Fault as e:
  933. if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
  934. self.handle_error('ERROR: supervisor shutting down')
  935. elif e.faultCode == xmlrpc.Faults.CANT_REREAD:
  936. self.handle_error("ERROR: %s" % e.faultString)
  937. else:
  938. self.handle_error(fatal=True)
  939. else:
  940. self._formatChanges(result[0])
  941. def handle_error(self, message=None, fatal=False, code=None):
  942. self.ctl.handle_error(message=message, fatal=fatal, code=code)
  943. def help_reread(self):
  944. self.ctl.output("reread \t\t\tReload the daemon's configuration files")
  945. def do_add(self, arg):
  946. names = arg.split()
  947. supervisor = self.ctl.get_supervisor()
  948. for name in names:
  949. try:
  950. supervisor.addProcessGroup(name)
  951. except xmlrpclib.Fault as e:
  952. if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
  953. self.handle_error('ERROR: shutting down')
  954. elif e.faultCode == xmlrpc.Faults.ALREADY_ADDED:
  955. self.ctl.output('ERROR: process group already active')
  956. elif e.faultCode == xmlrpc.Faults.BAD_NAME:
  957. self.handle_error("ERROR: no such process/group: %s" % name)
  958. else:
  959. self.handle_error(fatal=True)
  960. else:
  961. self.ctl.output("%s: added process group" % name)
  962. def help_add(self):
  963. self.ctl.output("add <name> [...]\tActivates any updates in config "
  964. "for process/group")
  965. def do_remove(self, arg):
  966. names = arg.split()
  967. supervisor = self.ctl.get_supervisor()
  968. for name in names:
  969. try:
  970. supervisor.removeProcessGroup(name)
  971. except xmlrpclib.Fault as e:
  972. if e.faultCode == xmlrpc.Faults.STILL_RUNNING:
  973. self.handle_error('ERROR: process/group still running: %s'
  974. % name)
  975. elif e.faultCode == xmlrpc.Faults.BAD_NAME:
  976. self.handle_error("ERROR: no such process/group: %s" % name)
  977. else:
  978. self.handle_error(fatal=True)
  979. else:
  980. self.ctl.output("%s: removed process group" % name)
  981. def help_remove(self):
  982. self.ctl.output("remove <name> [...]\tRemoves process/group from "
  983. "active config")
  984. def do_update(self, arg):
  985. def log(name, message):
  986. self.ctl.output("%s: %s" % (name, message))
  987. supervisor = self.ctl.get_supervisor()
  988. try:
  989. result = supervisor.reloadConfig()
  990. except xmlrpclib.Fault as e:
  991. if e.faultCode == xmlrpc.Faults.SHUTDOWN_STATE:
  992. self.handle_error('ERROR: already shutting down')
  993. return
  994. else:
  995. self.handle_error(fatal=True)
  996. added, changed, removed = result[0]
  997. valid_gnames = set(arg.split())
  998. # If all is specified treat it as if nothing was specified.
  999. if "all" in valid_gnames:
  1000. valid_gnames = set()
  1001. # If any gnames are specified we need to verify that they are
  1002. # valid in order to print a useful error message.
  1003. if valid_gnames:
  1004. groups = set()
  1005. for info in supervisor.getAllProcessInfo():
  1006. groups.add(info['group'])
  1007. # New gnames would not currently exist in this set so
  1008. # add those as well.
  1009. groups.update(added)
  1010. for gname in valid_gnames:
  1011. if gname not in groups:
  1012. self.ctl.handle_error('ERROR: no such group: %s' % gname)
  1013. for gname in removed:
  1014. if valid_gnames and gname not in valid_gnames:
  1015. continue
  1016. results = supervisor.stopProcessGroup(gname)
  1017. log(gname, "stopped")
  1018. fails = [res for res in results
  1019. if res['status'] == xmlrpc.Faults.FAILED]
  1020. if fails:
  1021. self.ctl.handle_error("%s: %s" % (gname, "has problems; not removing"))
  1022. continue
  1023. supervisor.removeProcessGroup(gname)
  1024. log(gname, "removed process group")
  1025. for gname in changed:
  1026. if valid_gnames and gname not in valid_gnames:
  1027. continue
  1028. supervisor.stopProcessGroup(gname)
  1029. log(gname, "stopped")
  1030. supervisor.removeProcessGroup(gname)
  1031. supervisor.addProcessGroup(gname)
  1032. log(gname, "updated process group")
  1033. for gname in added:
  1034. if valid_gnames and gname not in valid_gnames:
  1035. continue
  1036. supervisor.addProcessGroup(gname)
  1037. log(gname, "added process group")
  1038. def help_update(self):
  1039. self.ctl.output("update\t\t\tReload config and add/remove as necessary")
  1040. self.ctl.output("update all\t\tReload config and add/remove as necessary")
  1041. self.ctl.output("update <gname> [...]\tUpdate specific groups")
  1042. def _clearresult(self, result):
  1043. name = make_namespec(result['group'], result['name'])
  1044. code = result['status']
  1045. template = '%s: ERROR (%s)'
  1046. if code == xmlrpc.Faults.BAD_NAME:
  1047. return template % (name, 'no such process')
  1048. elif code == xmlrpc.Faults.FAILED:
  1049. return template % (name, 'failed')
  1050. elif code == xmlrpc.Faults.SUCCESS:
  1051. return '%s: cleared' % name
  1052. raise ValueError('Unknown result code %s for %s' % (code, name))
  1053. def do_clear(self, arg):
  1054. if not self.ctl.upcheck():
  1055. return
  1056. names = arg.split()
  1057. if not names:
  1058. self.handle_error('Error: clear requires a process name')
  1059. self.help_clear()
  1060. return
  1061. supervisor = self.ctl.get_supervisor()
  1062. if 'all' in names:
  1063. results = supervisor.clearAllProcessLogs()
  1064. for result in results:
  1065. self.ctl.handle_xmlrpc_fault_state(self._clearresult, result)
  1066. else:
  1067. for name in names:
  1068. group_name, process_name = split_namespec(name)
  1069. try:
  1070. supervisor.clearProcessLogs(name)
  1071. except xmlrpclib.Fault as e:
  1072. error = {'status': e.faultCode,
  1073. 'name': process_name,
  1074. 'group': group_name,
  1075. 'description': e.faultString}
  1076. self.ctl.handle_xmlrpc_fault_state(self._clearresult, error)
  1077. else:
  1078. name = make_namespec(group_name, process_name)
  1079. self.ctl.output('%s: cleared' % name)
  1080. def help_clear(self):
  1081. self.ctl.output("clear <name>\t\tClear a process' log files.")
  1082. self.ctl.output(
  1083. "clear <name> <name>\tClear multiple process' log files")
  1084. self.ctl.output("clear all\t\tClear all process' log files")
  1085. def do_open(self, arg):
  1086. url = arg.strip()
  1087. parts = urlparse.urlparse(url)
  1088. if parts[0] not in ('unix', 'http'):
  1089. self.handle_error('ERROR: url must be http:// or unix://')
  1090. return
  1091. self.ctl.options.serverurl = url
  1092. self.do_status('', True)
  1093. def help_open(self):
  1094. self.ctl.output("open <url>\tConnect to a remote supervisord process.")
  1095. self.ctl.output("\t\t(for UNIX domain socket, use unix:///socket/path)")
  1096. def do_version(self, arg):
  1097. if not self.ctl.upcheck():
  1098. return
  1099. supervisor = self.ctl.get_supervisor()
  1100. self.ctl.output(supervisor.getSupervisorVersion())
  1101. def help_version(self):
  1102. self.ctl.output(
  1103. "version\t\t\tShow the version of the remote supervisord "
  1104. "process")
  1105. def do_fg(self,args=None):
  1106. if not self.ctl.upcheck():
  1107. return
  1108. if not args:
  1109. self.handle_error('Error: no process name supplied')
  1110. self.help_fg()
  1111. return
  1112. args = args.split()
  1113. if len(args) > 1:
  1114. self.handle_error('Error: too many process names supplied')
  1115. return
  1116. program = args[0]
  1117. supervisor = self.ctl.get_supervisor()
  1118. try:
  1119. info = supervisor.getProcessInfo(program)
  1120. except xmlrpclib.Fault as msg:
  1121. if msg.faultCode == xmlrpc.Faults.BAD_NAME:
  1122. self.handle_error('Error: bad process name supplied')
  1123. return
  1124. # for any other fault
  1125. self.ctl.output(str(msg))
  1126. return
  1127. if not info['state'] == states.ProcessStates.RUNNING:
  1128. self.handle_error('Error: process not running')
  1129. return
  1130. # everything good; continue
  1131. a = None
  1132. try:
  1133. a = fgthread(program,self.ctl)
  1134. # this thread takes care of
  1135. # the output/error messages
  1136. a.start()
  1137. while True:
  1138. # this takes care of the user input
  1139. inp = raw_input() + '\n'
  1140. try:
  1141. supervisor.sendProcessStdin(program, inp)
  1142. except xmlrpclib.Fault as msg:
  1143. if msg.faultCode == xmlrpc.Faults.NOT_RUNNING:
  1144. self.ctl.output('Process got killed')
  1145. self.ctl.output('Exiting foreground')
  1146. a.kill()
  1147. return
  1148. info = supervisor.getProcessInfo(program)
  1149. if not info['state'] == states.ProcessStates.RUNNING:
  1150. self.ctl.output('Process got killed')
  1151. self.ctl.output('Exiting foreground')
  1152. a.kill()
  1153. return
  1154. continue
  1155. except (KeyboardInterrupt, EOFError):
  1156. if a:
  1157. a.kill()
  1158. self.ctl.output('Exiting foreground')
  1159. return
  1160. def help_fg(self,args=None):
  1161. self.ctl.output('fg <process>\tConnect to a process in foreground mode')
  1162. self.ctl.output('Press Ctrl+C to exit foreground')
  1163. def main(args=None, options=None):
  1164. if options is None:
  1165. options = ClientOptions()
  1166. options.realize(args, doc=__doc__)
  1167. c = Controller(options)
  1168. if options.args:
  1169. c.onecmd(" ".join(options.args))
  1170. if options.interactive:
  1171. c.exec_cmdloop(args, options)
  1172. if __name__ == "__main__":
  1173. main()