options.py 79 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123
  1. import socket
  2. import getopt
  3. import os
  4. import sys
  5. import tempfile
  6. import errno
  7. import signal
  8. import re
  9. import pwd
  10. import grp
  11. import resource
  12. import stat
  13. import pkg_resources
  14. import glob
  15. import platform
  16. import warnings
  17. import fcntl
  18. from supervisor.compat import PY3
  19. from supervisor.compat import ConfigParser
  20. from supervisor.compat import as_bytes, as_string
  21. from supervisor.compat import xmlrpclib
  22. from supervisor.compat import StringIO
  23. from supervisor.compat import basestring
  24. from supervisor.medusa import asyncore_25 as asyncore
  25. from supervisor.datatypes import process_or_group_name
  26. from supervisor.datatypes import boolean
  27. from supervisor.datatypes import integer
  28. from supervisor.datatypes import name_to_uid
  29. from supervisor.datatypes import gid_for_uid
  30. from supervisor.datatypes import existing_dirpath
  31. from supervisor.datatypes import byte_size
  32. from supervisor.datatypes import signal_number
  33. from supervisor.datatypes import list_of_exitcodes
  34. from supervisor.datatypes import dict_of_key_value_pairs
  35. from supervisor.datatypes import logfile_name
  36. from supervisor.datatypes import list_of_strings
  37. from supervisor.datatypes import octal_type
  38. from supervisor.datatypes import existing_directory
  39. from supervisor.datatypes import logging_level
  40. from supervisor.datatypes import colon_separated_user_group
  41. from supervisor.datatypes import inet_address
  42. from supervisor.datatypes import InetStreamSocketConfig
  43. from supervisor.datatypes import UnixStreamSocketConfig
  44. from supervisor.datatypes import url
  45. from supervisor.datatypes import Automatic
  46. from supervisor.datatypes import auto_restart
  47. from supervisor.datatypes import profile_options
  48. from supervisor.datatypes import set_here
  49. from supervisor import loggers
  50. from supervisor import states
  51. from supervisor import xmlrpc
  52. from supervisor import poller
  53. def _read_version_txt():
  54. mydir = os.path.abspath(os.path.dirname(__file__))
  55. version_txt = os.path.join(mydir, 'version.txt')
  56. with open(version_txt, 'r') as f:
  57. return f.read().strip()
  58. VERSION = _read_version_txt()
  59. def normalize_path(v):
  60. return os.path.normpath(os.path.abspath(os.path.expanduser(v)))
  61. class Dummy:
  62. pass
  63. class Options:
  64. stderr = sys.stderr
  65. stdout = sys.stdout
  66. exit = sys.exit
  67. warnings = warnings
  68. uid = gid = None
  69. progname = sys.argv[0]
  70. configfile = None
  71. schemadir = None
  72. configroot = None
  73. here = None
  74. # Class variable deciding whether positional arguments are allowed.
  75. # If you want positional arguments, set this to 1 in your subclass.
  76. positional_args_allowed = 0
  77. def __init__(self, require_configfile=True):
  78. """Constructor.
  79. Params:
  80. require_configfile -- whether we should fail on no config file.
  81. """
  82. self.names_list = []
  83. self.short_options = []
  84. self.long_options = []
  85. self.options_map = {}
  86. self.default_map = {}
  87. self.required_map = {}
  88. self.environ_map = {}
  89. self.attr_priorities = {}
  90. self.require_configfile = require_configfile
  91. self.add(None, None, "h", "help", self.help)
  92. self.add("configfile", None, "c:", "configuration=")
  93. here = os.path.dirname(os.path.dirname(sys.argv[0]))
  94. searchpaths = [os.path.join(here, 'etc', 'supervisord.conf'),
  95. os.path.join(here, 'supervisord.conf'),
  96. 'supervisord.conf',
  97. 'etc/supervisord.conf',
  98. '/etc/supervisord.conf']
  99. self.searchpaths = searchpaths
  100. def default_configfile(self):
  101. """Return the name of the found config file or print usage/exit."""
  102. config = None
  103. for path in self.searchpaths:
  104. if os.path.exists(path):
  105. config = path
  106. break
  107. if config is None and self.require_configfile:
  108. self.usage('No config file found at default paths (%s); '
  109. 'use the -c option to specify a config file '
  110. 'at a different path' % ', '.join(self.searchpaths))
  111. return config
  112. def help(self, dummy):
  113. """Print a long help message to stdout and exit(0).
  114. Occurrences of "%s" in are replaced by self.progname.
  115. """
  116. help = self.doc + "\n"
  117. if help.find("%s") > 0:
  118. help = help.replace("%s", self.progname)
  119. self.stdout.write(help)
  120. self.exit(0)
  121. def usage(self, msg):
  122. """Print a brief error message to stderr and exit(2)."""
  123. self.stderr.write("Error: %s\n" % str(msg))
  124. self.stderr.write("For help, use %s -h\n" % self.progname)
  125. self.exit(2)
  126. def add(self,
  127. name=None, # attribute name on self
  128. confname=None, # dotted config path name
  129. short=None, # short option name
  130. long=None, # long option name
  131. handler=None, # handler (defaults to string)
  132. default=None, # default value
  133. required=None, # message if not provided
  134. flag=None, # if not None, flag value
  135. env=None, # if not None, environment variable
  136. ):
  137. """Add information about a configuration option.
  138. This can take several forms:
  139. add(name, confname)
  140. Configuration option 'confname' maps to attribute 'name'
  141. add(name, None, short, long)
  142. Command line option '-short' or '--long' maps to 'name'
  143. add(None, None, short, long, handler)
  144. Command line option calls handler
  145. add(name, None, short, long, handler)
  146. Assign handler return value to attribute 'name'
  147. In addition, one of the following keyword arguments may be given:
  148. default=... -- if not None, the default value
  149. required=... -- if nonempty, an error message if no value provided
  150. flag=... -- if not None, flag value for command line option
  151. env=... -- if not None, name of environment variable that
  152. overrides the configuration file or default
  153. """
  154. if flag is not None:
  155. if handler is not None:
  156. raise ValueError("use at most one of flag= and handler=")
  157. if not long and not short:
  158. raise ValueError("flag= requires a command line flag")
  159. if short and short.endswith(":"):
  160. raise ValueError("flag= requires a command line flag")
  161. if long and long.endswith("="):
  162. raise ValueError("flag= requires a command line flag")
  163. handler = lambda arg, flag=flag: flag
  164. if short and long:
  165. if short.endswith(":") != long.endswith("="):
  166. raise ValueError("inconsistent short/long options: %r %r" % (
  167. short, long))
  168. if short:
  169. if short[0] == "-":
  170. raise ValueError("short option should not start with '-'")
  171. key, rest = short[:1], short[1:]
  172. if rest not in ("", ":"):
  173. raise ValueError("short option should be 'x' or 'x:'")
  174. key = "-" + key
  175. if key in self.options_map:
  176. raise ValueError("duplicate short option key '%s'" % key)
  177. self.options_map[key] = (name, handler)
  178. self.short_options.append(short)
  179. if long:
  180. if long[0] == "-":
  181. raise ValueError("long option should not start with '-'")
  182. key = long
  183. if key[-1] == "=":
  184. key = key[:-1]
  185. key = "--" + key
  186. if key in self.options_map:
  187. raise ValueError("duplicate long option key '%s'" % key)
  188. self.options_map[key] = (name, handler)
  189. self.long_options.append(long)
  190. if env:
  191. self.environ_map[env] = (name, handler)
  192. if name:
  193. if not hasattr(self, name):
  194. setattr(self, name, None)
  195. self.names_list.append((name, confname))
  196. if default is not None:
  197. self.default_map[name] = default
  198. if required:
  199. self.required_map[name] = required
  200. def _set(self, attr, value, prio):
  201. current = self.attr_priorities.get(attr, -1)
  202. if prio >= current:
  203. setattr(self, attr, value)
  204. self.attr_priorities[attr] = prio
  205. def realize(self, args=None, doc=None,
  206. progname=None, raise_getopt_errs=True):
  207. """Realize a configuration.
  208. Optional arguments:
  209. args -- the command line arguments, less the program name
  210. (default is sys.argv[1:])
  211. doc -- usage message (default is __main__.__doc__)
  212. """
  213. # Provide dynamic default method arguments
  214. if args is None:
  215. args = sys.argv[1:]
  216. if progname is None:
  217. progname = sys.argv[0]
  218. if doc is None:
  219. try:
  220. import __main__
  221. doc = __main__.__doc__
  222. except Exception:
  223. pass
  224. self.progname = progname
  225. self.doc = doc
  226. self.options = []
  227. self.args = []
  228. # Call getopt
  229. try:
  230. self.options, self.args = getopt.getopt(
  231. args, "".join(self.short_options), self.long_options)
  232. except getopt.error as msg:
  233. if raise_getopt_errs:
  234. self.usage(msg)
  235. # Check for positional args
  236. if self.args and not self.positional_args_allowed:
  237. self.usage("positional arguments are not supported: %s" % (str(self.args)))
  238. # Process options returned by getopt
  239. for opt, arg in self.options:
  240. name, handler = self.options_map[opt]
  241. if handler is not None:
  242. try:
  243. arg = handler(arg)
  244. except ValueError as msg:
  245. self.usage("invalid value for %s %r: %s" % (opt, arg, msg))
  246. if name and arg is not None:
  247. if getattr(self, name) is not None:
  248. self.usage("conflicting command line option %r" % opt)
  249. self._set(name, arg, 2)
  250. # Process environment variables
  251. for envvar in self.environ_map.keys():
  252. name, handler = self.environ_map[envvar]
  253. if envvar in os.environ:
  254. value = os.environ[envvar]
  255. if handler is not None:
  256. try:
  257. value = handler(value)
  258. except ValueError as msg:
  259. self.usage("invalid environment value for %s %r: %s"
  260. % (envvar, value, msg))
  261. if name and value is not None:
  262. self._set(name, value, 1)
  263. if self.configfile is None:
  264. uid = os.getuid()
  265. if uid == 0 and "supervisord" in self.progname: # pragma: no cover
  266. self.warnings.warn(
  267. 'Supervisord is running as root and it is searching '
  268. 'for its configuration file in default locations '
  269. '(including its current working directory); you '
  270. 'probably want to specify a "-c" argument specifying an '
  271. 'absolute path to a configuration file for improved '
  272. 'security.'
  273. )
  274. self.configfile = self.default_configfile()
  275. self.process_config()
  276. def process_config(self, do_usage=True):
  277. """Process configuration data structure.
  278. This includes reading config file if necessary, setting defaults etc.
  279. """
  280. if self.configfile:
  281. self.process_config_file(do_usage)
  282. # Copy config options to attributes of self. This only fills
  283. # in options that aren't already set from the command line.
  284. for name, confname in self.names_list:
  285. if confname:
  286. parts = confname.split(".")
  287. obj = self.configroot
  288. for part in parts:
  289. if obj is None:
  290. break
  291. # Here AttributeError is not a user error!
  292. obj = getattr(obj, part)
  293. self._set(name, obj, 0)
  294. # Process defaults
  295. for name, value in self.default_map.items():
  296. if getattr(self, name) is None:
  297. setattr(self, name, value)
  298. # Process required options
  299. for name, message in self.required_map.items():
  300. if getattr(self, name) is None:
  301. self.usage(message)
  302. def process_config_file(self, do_usage):
  303. # Process config file
  304. if not hasattr(self.configfile, 'read'):
  305. self.here = os.path.abspath(os.path.dirname(self.configfile))
  306. set_here(self.here)
  307. try:
  308. self.read_config(self.configfile)
  309. except ValueError as msg:
  310. if do_usage:
  311. # if this is not called from an RPC method, run usage and exit.
  312. self.usage(str(msg))
  313. else:
  314. # if this is called from an RPC method, raise an error
  315. raise ValueError(msg)
  316. def exists(self, path):
  317. return os.path.exists(path)
  318. def open(self, fn, mode='r'):
  319. return open(fn, mode)
  320. def get_plugins(self, parser, factory_key, section_prefix):
  321. factories = []
  322. for section in parser.sections():
  323. if not section.startswith(section_prefix):
  324. continue
  325. name = section.split(':', 1)[1]
  326. factory_spec = parser.saneget(section, factory_key, None)
  327. if factory_spec is None:
  328. raise ValueError('section [%s] does not specify a %s' %
  329. (section, factory_key))
  330. try:
  331. factory = self.import_spec(factory_spec)
  332. except ImportError:
  333. raise ValueError('%s cannot be resolved within [%s]' % (
  334. factory_spec, section))
  335. items_tmp = parser.items(section)
  336. items = []
  337. for ikv in items_tmp:
  338. ik, iv_tmp = ikv
  339. iexpansions = {}
  340. iexpansions.update(environ_expansions())
  341. iv = expand(iv_tmp, iexpansions, ik)
  342. items.append((ik, iv))
  343. items.remove((factory_key, factory_spec))
  344. factories.append((name, factory, dict(items)))
  345. return factories
  346. def import_spec(self, spec):
  347. return pkg_resources.EntryPoint.parse("x="+spec).load(False)
  348. class ServerOptions(Options):
  349. user = None
  350. sockchown = None
  351. sockchmod = None
  352. logfile = None
  353. loglevel = None
  354. pidfile = None
  355. passwdfile = None
  356. nodaemon = None
  357. environment = None
  358. httpservers = ()
  359. unlink_socketfiles = True
  360. mood = states.SupervisorStates.RUNNING
  361. def __init__(self):
  362. Options.__init__(self)
  363. self.configroot = Dummy()
  364. self.configroot.supervisord = Dummy()
  365. self.add(None, None, "v", "version", self.version)
  366. self.add("nodaemon", "supervisord.nodaemon", "n", "nodaemon", flag=1,
  367. default=0)
  368. self.add("user", "supervisord.user", "u:", "user=")
  369. self.add("umask", "supervisord.umask", "m:", "umask=",
  370. octal_type, default='022')
  371. self.add("directory", "supervisord.directory", "d:", "directory=",
  372. existing_directory)
  373. self.add("logfile", "supervisord.logfile", "l:", "logfile=",
  374. existing_dirpath, default="supervisord.log")
  375. self.add("logfile_maxbytes", "supervisord.logfile_maxbytes",
  376. "y:", "logfile_maxbytes=", byte_size,
  377. default=50 * 1024 * 1024) # 50MB
  378. self.add("logfile_backups", "supervisord.logfile_backups",
  379. "z:", "logfile_backups=", integer, default=10)
  380. self.add("loglevel", "supervisord.loglevel", "e:", "loglevel=",
  381. logging_level, default="info")
  382. self.add("pidfile", "supervisord.pidfile", "j:", "pidfile=",
  383. existing_dirpath, default="supervisord.pid")
  384. self.add("identifier", "supervisord.identifier", "i:", "identifier=",
  385. str, default="supervisor")
  386. self.add("childlogdir", "supervisord.childlogdir", "q:", "childlogdir=",
  387. existing_directory, default=tempfile.gettempdir())
  388. self.add("minfds", "supervisord.minfds",
  389. "a:", "minfds=", int, default=1024)
  390. self.add("minprocs", "supervisord.minprocs",
  391. "", "minprocs=", int, default=200)
  392. self.add("nocleanup", "supervisord.nocleanup",
  393. "k", "nocleanup", flag=1, default=0)
  394. self.add("strip_ansi", "supervisord.strip_ansi",
  395. "t", "strip_ansi", flag=1, default=0)
  396. self.add("profile_options", "supervisord.profile_options",
  397. "", "profile_options=", profile_options, default=None)
  398. self.pidhistory = {}
  399. self.process_group_configs = []
  400. self.parse_warnings = []
  401. self.signal_receiver = SignalReceiver()
  402. self.poller = poller.Poller(self)
  403. def version(self, dummy):
  404. """Print version to stdout and exit(0).
  405. """
  406. self.stdout.write('%s\n' % VERSION)
  407. self.exit(0)
  408. # TODO: not covered by any test, but used by dispatchers
  409. def getLogger(self, *args, **kwargs):
  410. return loggers.getLogger(*args, **kwargs)
  411. def realize(self, *arg, **kw):
  412. Options.realize(self, *arg, **kw)
  413. section = self.configroot.supervisord
  414. # Additional checking of user option; set uid and gid
  415. if self.user is not None:
  416. try:
  417. uid = name_to_uid(self.user)
  418. except ValueError as msg:
  419. self.usage(msg) # invalid user
  420. self.uid = uid
  421. self.gid = gid_for_uid(uid)
  422. if not self.loglevel:
  423. self.loglevel = section.loglevel
  424. if self.logfile:
  425. logfile = self.logfile
  426. else:
  427. logfile = section.logfile
  428. self.logfile = normalize_path(logfile)
  429. if self.pidfile:
  430. pidfile = self.pidfile
  431. else:
  432. pidfile = section.pidfile
  433. self.pidfile = normalize_path(pidfile)
  434. self.rpcinterface_factories = section.rpcinterface_factories
  435. self.serverurl = None
  436. self.server_configs = sconfigs = section.server_configs
  437. # we need to set a fallback serverurl that process.spawn can use
  438. # prefer a unix domain socket
  439. for config in [ config for config in sconfigs if
  440. config['family'] is socket.AF_UNIX ]:
  441. path = config['file']
  442. self.serverurl = 'unix://%s' % path
  443. break
  444. # fall back to an inet socket
  445. if self.serverurl is None:
  446. for config in [ config for config in sconfigs if
  447. config['family'] is socket.AF_INET]:
  448. host = config['host']
  449. port = config['port']
  450. if not host:
  451. host = 'localhost'
  452. self.serverurl = 'http://%s:%s' % (host, port)
  453. # self.serverurl may still be None if no servers at all are
  454. # configured in the config file
  455. self.identifier = section.identifier
  456. def process_config(self, do_usage=True):
  457. Options.process_config(self, do_usage=do_usage)
  458. new = self.configroot.supervisord.process_group_configs
  459. self.process_group_configs = new
  460. def read_config(self, fp):
  461. # Clear parse warnings, since we may be re-reading the
  462. # config a second time after a reload.
  463. self.parse_warnings = []
  464. section = self.configroot.supervisord
  465. need_close = False
  466. if not hasattr(fp, 'read'):
  467. if not self.exists(fp):
  468. raise ValueError("could not find config file %s" % fp)
  469. try:
  470. fp = self.open(fp, 'r')
  471. need_close = True
  472. except (IOError, OSError):
  473. raise ValueError("could not read config file %s" % fp)
  474. kwargs = {}
  475. if PY3:
  476. kwargs['inline_comment_prefixes'] = (';','#')
  477. parser = UnhosedConfigParser(**kwargs)
  478. try:
  479. try:
  480. parser.read_file(fp)
  481. except AttributeError:
  482. parser.readfp(fp)
  483. except ConfigParser.ParsingError as why:
  484. raise ValueError(str(why))
  485. finally:
  486. if need_close:
  487. fp.close()
  488. expansions = {'here':self.here}
  489. expansions.update(environ_expansions())
  490. if parser.has_section('include'):
  491. if not parser.has_option('include', 'files'):
  492. raise ValueError(".ini file has [include] section, but no "
  493. "files setting")
  494. files = parser.get('include', 'files')
  495. files = expand(files, expansions, 'include.files')
  496. files = files.split()
  497. if hasattr(fp, 'name'):
  498. base = os.path.dirname(os.path.abspath(fp.name))
  499. else:
  500. base = '.'
  501. for pattern in files:
  502. pattern = os.path.join(base, pattern)
  503. filenames = glob.glob(pattern)
  504. if not filenames:
  505. self.parse_warnings.append(
  506. 'No file matches via include "%s"' % pattern)
  507. continue
  508. for filename in filenames:
  509. self.parse_warnings.append(
  510. 'Included extra file "%s" during parsing' % filename)
  511. try:
  512. parser.read(filename)
  513. except ConfigParser.ParsingError as why:
  514. raise ValueError(str(why))
  515. sections = parser.sections()
  516. if not 'supervisord' in sections:
  517. raise ValueError('.ini file does not include supervisord section')
  518. common_expansions = {'here':self.here}
  519. def get(opt, default, **kwargs):
  520. expansions = kwargs.get('expansions', {})
  521. expansions.update(common_expansions)
  522. kwargs['expansions'] = expansions
  523. return parser.getdefault(opt, default, **kwargs)
  524. section.minfds = integer(get('minfds', 1024))
  525. section.minprocs = integer(get('minprocs', 200))
  526. directory = get('directory', None)
  527. if directory is None:
  528. section.directory = None
  529. else:
  530. section.directory = existing_directory(directory)
  531. section.user = get('user', None)
  532. section.umask = octal_type(get('umask', '022'))
  533. section.logfile = existing_dirpath(get('logfile', 'supervisord.log'))
  534. section.logfile_maxbytes = byte_size(get('logfile_maxbytes', '50MB'))
  535. section.logfile_backups = integer(get('logfile_backups', 10))
  536. section.loglevel = logging_level(get('loglevel', 'info'))
  537. section.pidfile = existing_dirpath(get('pidfile', 'supervisord.pid'))
  538. section.identifier = get('identifier', 'supervisor')
  539. section.nodaemon = boolean(get('nodaemon', 'false'))
  540. tempdir = tempfile.gettempdir()
  541. section.childlogdir = existing_directory(get('childlogdir', tempdir))
  542. section.nocleanup = boolean(get('nocleanup', 'false'))
  543. section.strip_ansi = boolean(get('strip_ansi', 'false'))
  544. environ_str = get('environment', '')
  545. environ_str = expand(environ_str, expansions, 'environment')
  546. section.environment = dict_of_key_value_pairs(environ_str)
  547. # Process rpcinterface plugins before groups to allow custom events to
  548. # be registered.
  549. section.rpcinterface_factories = self.get_plugins(
  550. parser,
  551. 'supervisor.rpcinterface_factory',
  552. 'rpcinterface:'
  553. )
  554. section.process_group_configs = self.process_groups_from_parser(parser)
  555. for group in section.process_group_configs:
  556. for proc in group.process_configs:
  557. env = section.environment.copy()
  558. env.update(proc.environment)
  559. proc.environment = env
  560. section.server_configs = self.server_configs_from_parser(parser)
  561. section.profile_options = None
  562. return section
  563. def process_groups_from_parser(self, parser):
  564. groups = []
  565. all_sections = parser.sections()
  566. homogeneous_exclude = []
  567. common_expansions = {'here':self.here}
  568. def get(section, opt, default, **kwargs):
  569. expansions = kwargs.get('expansions', {})
  570. expansions.update(common_expansions)
  571. kwargs['expansions'] = expansions
  572. return parser.saneget(section, opt, default, **kwargs)
  573. # process heterogeneous groups
  574. for section in all_sections:
  575. if not section.startswith('group:'):
  576. continue
  577. group_name = process_or_group_name(section.split(':', 1)[1])
  578. programs = list_of_strings(get(section, 'programs', None))
  579. priority = integer(get(section, 'priority', 999))
  580. group_processes = []
  581. for program in programs:
  582. program_section = "program:%s" % program
  583. if not program_section in all_sections:
  584. raise ValueError(
  585. '[%s] names unknown program %s' % (section, program))
  586. homogeneous_exclude.append(program_section)
  587. processes = self.processes_from_section(parser, program_section,
  588. group_name,
  589. ProcessConfig)
  590. group_processes.extend(processes)
  591. groups.append(
  592. ProcessGroupConfig(self, group_name, priority, group_processes)
  593. )
  594. # process "normal" homogeneous groups
  595. for section in all_sections:
  596. if ( (not section.startswith('program:') )
  597. or section in homogeneous_exclude ):
  598. continue
  599. program_name = process_or_group_name(section.split(':', 1)[1])
  600. priority = integer(get(section, 'priority', 999))
  601. processes=self.processes_from_section(parser, section, program_name,
  602. ProcessConfig)
  603. groups.append(
  604. ProcessGroupConfig(self, program_name, priority, processes)
  605. )
  606. # process "event listener" homogeneous groups
  607. for section in all_sections:
  608. if not section.startswith('eventlistener:'):
  609. continue
  610. pool_name = section.split(':', 1)[1]
  611. # give listeners a "high" default priority so they are started first
  612. # and stopped last at mainloop exit
  613. priority = integer(get(section, 'priority', -1))
  614. buffer_size = integer(get(section, 'buffer_size', 10))
  615. result_handler = get(section, 'result_handler',
  616. 'supervisor.dispatchers:default_handler')
  617. try:
  618. result_handler = self.import_spec(result_handler)
  619. except ImportError:
  620. raise ValueError('%s cannot be resolved within [%s]' % (
  621. result_handler, section))
  622. pool_event_names = [x.upper() for x in
  623. list_of_strings(get(section, 'events', ''))]
  624. pool_event_names = set(pool_event_names)
  625. if not pool_event_names:
  626. raise ValueError('[%s] section requires an "events" line' %
  627. section)
  628. from supervisor.events import EventTypes
  629. pool_events = []
  630. for pool_event_name in pool_event_names:
  631. pool_event = getattr(EventTypes, pool_event_name, None)
  632. if pool_event is None:
  633. raise ValueError('Unknown event type %s in [%s] events' %
  634. (pool_event_name, section))
  635. pool_events.append(pool_event)
  636. redirect_stderr = boolean(get(section, 'redirect_stderr', 'false'))
  637. if redirect_stderr:
  638. raise ValueError('[%s] section sets redirect_stderr=true '
  639. 'but this is not allowed because it will interfere '
  640. 'with the eventlistener protocol' % section)
  641. processes=self.processes_from_section(parser, section, pool_name,
  642. EventListenerConfig)
  643. groups.append(
  644. EventListenerPoolConfig(self, pool_name, priority, processes,
  645. buffer_size, pool_events,
  646. result_handler)
  647. )
  648. # process fastcgi homogeneous groups
  649. for section in all_sections:
  650. if ( (not section.startswith('fcgi-program:') )
  651. or section in homogeneous_exclude ):
  652. continue
  653. program_name = process_or_group_name(section.split(':', 1)[1])
  654. priority = integer(get(section, 'priority', 999))
  655. fcgi_expansions = {'program_name': program_name}
  656. # find proc_uid from "user" option
  657. proc_user = get(section, 'user', None)
  658. if proc_user is None:
  659. proc_uid = None
  660. else:
  661. proc_uid = name_to_uid(proc_user)
  662. socket_owner = get(section, 'socket_owner', None)
  663. if socket_owner is not None:
  664. try:
  665. socket_owner = colon_separated_user_group(socket_owner)
  666. except ValueError:
  667. raise ValueError('Invalid socket_owner value %s'
  668. % socket_owner)
  669. socket_mode = get(section, 'socket_mode', None)
  670. if socket_mode is not None:
  671. try:
  672. socket_mode = octal_type(socket_mode)
  673. except (TypeError, ValueError):
  674. raise ValueError('Invalid socket_mode value %s'
  675. % socket_mode)
  676. socket = get(section, 'socket', None, expansions=fcgi_expansions)
  677. if not socket:
  678. raise ValueError('[%s] section requires a "socket" line' %
  679. section)
  680. try:
  681. socket_config = self.parse_fcgi_socket(socket, proc_uid,
  682. socket_owner, socket_mode)
  683. except ValueError as e:
  684. raise ValueError('%s in [%s] socket' % (str(e), section))
  685. processes=self.processes_from_section(parser, section, program_name,
  686. FastCGIProcessConfig)
  687. groups.append(
  688. FastCGIGroupConfig(self, program_name, priority, processes,
  689. socket_config)
  690. )
  691. groups.sort()
  692. return groups
  693. def parse_fcgi_socket(self, sock, proc_uid, socket_owner, socket_mode):
  694. if sock.startswith('unix://'):
  695. path = sock[7:]
  696. #Check it's an absolute path
  697. if not os.path.isabs(path):
  698. raise ValueError("Unix socket path %s is not an absolute path",
  699. path)
  700. path = normalize_path(path)
  701. if socket_owner is None:
  702. uid = os.getuid()
  703. if proc_uid is not None and proc_uid != uid:
  704. socket_owner = (proc_uid, gid_for_uid(proc_uid))
  705. if socket_mode is None:
  706. socket_mode = 448 # 0700 in Py2, 0o700 Py3
  707. return UnixStreamSocketConfig(path, owner=socket_owner,
  708. mode=socket_mode)
  709. if socket_owner is not None or socket_mode is not None:
  710. raise ValueError("socket_owner and socket_mode params should"
  711. + " only be used with a Unix domain socket")
  712. m = re.match(r'tcp://([^\s:]+):(\d+)$', sock)
  713. if m:
  714. host = m.group(1)
  715. port = int(m.group(2))
  716. return InetStreamSocketConfig(host, port)
  717. raise ValueError("Bad socket format %s", sock)
  718. def processes_from_section(self, parser, section, group_name,
  719. klass=None):
  720. try:
  721. return self._processes_from_section(
  722. parser, section, group_name, klass)
  723. except ValueError as e:
  724. raise ValueError('%s in section %r' % (e, section))
  725. def _processes_from_section(self, parser, section, group_name,
  726. klass=None):
  727. if klass is None:
  728. klass = ProcessConfig
  729. programs = []
  730. program_name = process_or_group_name(section.split(':', 1)[1])
  731. host_node_name = platform.node()
  732. common_expansions = {'here':self.here,
  733. 'program_name':program_name,
  734. 'host_node_name':host_node_name,
  735. 'group_name':group_name}
  736. def get(section, opt, *args, **kwargs):
  737. expansions = kwargs.get('expansions', {})
  738. expansions.update(common_expansions)
  739. kwargs['expansions'] = expansions
  740. return parser.saneget(section, opt, *args, **kwargs)
  741. priority = integer(get(section, 'priority', 999))
  742. autostart = boolean(get(section, 'autostart', 'true'))
  743. autorestart = auto_restart(get(section, 'autorestart', 'unexpected'))
  744. startsecs = integer(get(section, 'startsecs', 1))
  745. startretries = integer(get(section, 'startretries', 3))
  746. stopsignal = signal_number(get(section, 'stopsignal', 'TERM'))
  747. stopwaitsecs = integer(get(section, 'stopwaitsecs', 10))
  748. stopasgroup = boolean(get(section, 'stopasgroup', 'false'))
  749. killasgroup = boolean(get(section, 'killasgroup', stopasgroup))
  750. exitcodes = list_of_exitcodes(get(section, 'exitcodes', '0,2'))
  751. # see also redirect_stderr check in process_groups_from_parser()
  752. redirect_stderr = boolean(get(section, 'redirect_stderr','false'))
  753. numprocs = integer(get(section, 'numprocs', 1))
  754. numprocs_start = integer(get(section, 'numprocs_start', 0))
  755. environment_str = get(section, 'environment', '', do_expand=False)
  756. stdout_cmaxbytes = byte_size(get(section,'stdout_capture_maxbytes','0'))
  757. stdout_events = boolean(get(section, 'stdout_events_enabled','false'))
  758. stderr_cmaxbytes = byte_size(get(section,'stderr_capture_maxbytes','0'))
  759. stderr_events = boolean(get(section, 'stderr_events_enabled','false'))
  760. serverurl = get(section, 'serverurl', None)
  761. if serverurl and serverurl.strip().upper() == 'AUTO':
  762. serverurl = None
  763. # find uid from "user" option
  764. user = get(section, 'user', None)
  765. if user is None:
  766. uid = None
  767. else:
  768. uid = name_to_uid(user)
  769. umask = get(section, 'umask', None)
  770. if umask is not None:
  771. umask = octal_type(umask)
  772. process_name = process_or_group_name(
  773. get(section, 'process_name', '%(program_name)s', do_expand=False))
  774. if numprocs > 1:
  775. if not '%(process_num)' in process_name:
  776. # process_name needs to include process_num when we
  777. # represent a group of processes
  778. raise ValueError(
  779. '%(process_num) must be present within process_name when '
  780. 'numprocs > 1')
  781. if stopasgroup and not killasgroup:
  782. raise ValueError(
  783. "Cannot set stopasgroup=true and killasgroup=false"
  784. )
  785. for process_num in range(numprocs_start, numprocs + numprocs_start):
  786. expansions = common_expansions
  787. expansions.update({'process_num': process_num})
  788. expansions.update(environ_expansions())
  789. environment = dict_of_key_value_pairs(
  790. expand(environment_str, expansions, 'environment'))
  791. directory = get(section, 'directory', None)
  792. if directory:
  793. directory = expand(directory, expansions, 'directory')
  794. logfiles = {}
  795. for k in ('stdout', 'stderr'):
  796. n = '%s_logfile' % k
  797. lf_val = get(section, n, Automatic)
  798. if isinstance(lf_val, basestring):
  799. lf_val = expand(lf_val, expansions, n)
  800. lf_val = logfile_name(lf_val)
  801. logfiles[n] = lf_val
  802. bu_key = '%s_logfile_backups' % k
  803. backups = integer(get(section, bu_key, 10))
  804. logfiles[bu_key] = backups
  805. mb_key = '%s_logfile_maxbytes' % k
  806. maxbytes = byte_size(get(section, mb_key, '50MB'))
  807. logfiles[mb_key] = maxbytes
  808. sy_key = '%s_syslog' % k
  809. syslog = boolean(get(section, sy_key, False))
  810. logfiles[sy_key] = syslog
  811. if lf_val is Automatic and not maxbytes:
  812. self.parse_warnings.append(
  813. 'For [%s], AUTO logging used for %s without '
  814. 'rollover, set maxbytes > 0 to avoid filling up '
  815. 'filesystem unintentionally' % (section, n))
  816. command = get(section, 'command', None, expansions=expansions)
  817. if command is None:
  818. raise ValueError(
  819. 'program section %s does not specify a command' % section)
  820. pconfig = klass(
  821. self,
  822. name=expand(process_name, expansions, 'process_name'),
  823. command=command,
  824. directory=directory,
  825. umask=umask,
  826. priority=priority,
  827. autostart=autostart,
  828. autorestart=autorestart,
  829. startsecs=startsecs,
  830. startretries=startretries,
  831. uid=uid,
  832. stdout_logfile=logfiles['stdout_logfile'],
  833. stdout_capture_maxbytes = stdout_cmaxbytes,
  834. stdout_events_enabled = stdout_events,
  835. stdout_logfile_backups=logfiles['stdout_logfile_backups'],
  836. stdout_logfile_maxbytes=logfiles['stdout_logfile_maxbytes'],
  837. stdout_syslog=logfiles['stdout_syslog'],
  838. stderr_logfile=logfiles['stderr_logfile'],
  839. stderr_capture_maxbytes = stderr_cmaxbytes,
  840. stderr_events_enabled = stderr_events,
  841. stderr_logfile_backups=logfiles['stderr_logfile_backups'],
  842. stderr_logfile_maxbytes=logfiles['stderr_logfile_maxbytes'],
  843. stderr_syslog=logfiles['stderr_syslog'],
  844. stopsignal=stopsignal,
  845. stopwaitsecs=stopwaitsecs,
  846. stopasgroup=stopasgroup,
  847. killasgroup=killasgroup,
  848. exitcodes=exitcodes,
  849. redirect_stderr=redirect_stderr,
  850. environment=environment,
  851. serverurl=serverurl)
  852. programs.append(pconfig)
  853. programs.sort() # asc by priority
  854. return programs
  855. def _parse_servernames(self, parser, stype):
  856. options = []
  857. for section in parser.sections():
  858. if section.startswith(stype):
  859. parts = section.split(':', 1)
  860. if len(parts) > 1:
  861. name = parts[1]
  862. else:
  863. name = None # default sentinel
  864. options.append((name, section))
  865. return options
  866. def _parse_username_and_password(self, parser, section):
  867. get = parser.saneget
  868. username = get(section, 'username', None)
  869. password = get(section, 'password', None)
  870. if username is None and password is not None:
  871. raise ValueError(
  872. 'Must specify username if password is specified in [%s]'
  873. % section)
  874. return {'username':username, 'password':password}
  875. def server_configs_from_parser(self, parser):
  876. configs = []
  877. inet_serverdefs = self._parse_servernames(parser, 'inet_http_server')
  878. for name, section in inet_serverdefs:
  879. config = {}
  880. get = parser.saneget
  881. config.update(self._parse_username_and_password(parser, section))
  882. config['name'] = name
  883. config['family'] = socket.AF_INET
  884. port = get(section, 'port', None)
  885. if port is None:
  886. raise ValueError('section [%s] has no port value' % section)
  887. host, port = inet_address(port)
  888. config['host'] = host
  889. config['port'] = port
  890. config['section'] = section
  891. configs.append(config)
  892. unix_serverdefs = self._parse_servernames(parser, 'unix_http_server')
  893. for name, section in unix_serverdefs:
  894. config = {}
  895. get = parser.saneget
  896. sfile = get(section, 'file', None)
  897. if sfile is None:
  898. raise ValueError('section [%s] has no file value' % section)
  899. sfile = sfile.strip()
  900. config['name'] = name
  901. config['family'] = socket.AF_UNIX
  902. sfile = expand(sfile, {'here':self.here}, 'socket file')
  903. config['file'] = normalize_path(sfile)
  904. config.update(self._parse_username_and_password(parser, section))
  905. chown = get(section, 'chown', None)
  906. if chown is not None:
  907. try:
  908. chown = colon_separated_user_group(chown)
  909. except ValueError:
  910. raise ValueError('Invalid sockchown value %s' % chown)
  911. else:
  912. chown = (-1, -1)
  913. config['chown'] = chown
  914. chmod = get(section, 'chmod', None)
  915. if chmod is not None:
  916. try:
  917. chmod = octal_type(chmod)
  918. except (TypeError, ValueError):
  919. raise ValueError('Invalid chmod value %s' % chmod)
  920. else:
  921. chmod = 448 # 0700 on py2, 0o700 on py3
  922. config['chmod'] = chmod
  923. config['section'] = section
  924. configs.append(config)
  925. return configs
  926. def daemonize(self):
  927. self.poller.before_daemonize()
  928. self._daemonize()
  929. self.poller.after_daemonize()
  930. def _daemonize(self):
  931. # To daemonize, we need to become the leader of our own session
  932. # (process) group. If we do not, signals sent to our
  933. # parent process will also be sent to us. This might be bad because
  934. # signals such as SIGINT can be sent to our parent process during
  935. # normal (uninteresting) operations such as when we press Ctrl-C in the
  936. # parent terminal window to escape from a logtail command.
  937. # To disassociate ourselves from our parent's session group we use
  938. # os.setsid. It means "set session id", which has the effect of
  939. # disassociating a process from is current session and process group
  940. # and setting itself up as a new session leader.
  941. #
  942. # Unfortunately we cannot call setsid if we're already a session group
  943. # leader, so we use "fork" to make a copy of ourselves that is
  944. # guaranteed to not be a session group leader.
  945. #
  946. # We also change directories, set stderr and stdout to null, and
  947. # change our umask.
  948. #
  949. # This explanation was (gratefully) garnered from
  950. # http://www.hawklord.uklinux.net/system/daemons/d3.htm
  951. pid = os.fork()
  952. if pid != 0:
  953. # Parent
  954. self.logger.blather("supervisord forked; parent exiting")
  955. os._exit(0)
  956. # Child
  957. self.logger.info("daemonizing the supervisord process")
  958. if self.directory:
  959. try:
  960. os.chdir(self.directory)
  961. except OSError as err:
  962. self.logger.critical("can't chdir into %r: %s"
  963. % (self.directory, err))
  964. else:
  965. self.logger.info("set current directory: %r"
  966. % self.directory)
  967. os.close(0)
  968. self.stdin = sys.stdin = sys.__stdin__ = open("/dev/null")
  969. os.close(1)
  970. self.stdout = sys.stdout = sys.__stdout__ = open("/dev/null", "w")
  971. os.close(2)
  972. self.stderr = sys.stderr = sys.__stderr__ = open("/dev/null", "w")
  973. os.setsid()
  974. os.umask(self.umask)
  975. # XXX Stevens, in his Advanced Unix book, section 13.3 (page
  976. # 417) recommends calling umask(0) and closing unused
  977. # file descriptors. In his Network Programming book, he
  978. # additionally recommends ignoring SIGHUP and forking again
  979. # after the setsid() call, for obscure SVR4 reasons.
  980. def write_pidfile(self):
  981. pid = os.getpid()
  982. try:
  983. with open(self.pidfile, 'w') as f:
  984. f.write('%s\n' % pid)
  985. except (IOError, OSError):
  986. self.logger.critical('could not write pidfile %s' % self.pidfile)
  987. else:
  988. self.logger.info('supervisord started with pid %s' % pid)
  989. def cleanup(self):
  990. for config, server in self.httpservers:
  991. if config['family'] == socket.AF_UNIX:
  992. if self.unlink_socketfiles:
  993. socketname = config['file']
  994. self._try_unlink(socketname)
  995. self._try_unlink(self.pidfile)
  996. def _try_unlink(self, path):
  997. try:
  998. os.unlink(path)
  999. except OSError:
  1000. pass
  1001. def close_httpservers(self):
  1002. dispatcher_servers = []
  1003. for config, server in self.httpservers:
  1004. server.close()
  1005. # server._map is a reference to the asyncore socket_map
  1006. for dispatcher in self.get_socket_map().values():
  1007. dispatcher_server = getattr(dispatcher, 'server', None)
  1008. if dispatcher_server is server:
  1009. dispatcher_servers.append(dispatcher)
  1010. for server in dispatcher_servers:
  1011. # TODO: try to remove this entirely.
  1012. # For unknown reasons, sometimes an http_channel
  1013. # dispatcher in the socket map related to servers
  1014. # remains open *during a reload*. If one of these
  1015. # exists at this point, we need to close it by hand
  1016. # (thus removing it from the asyncore.socket_map). If
  1017. # we don't do this, 'cleanup_fds' will cause its file
  1018. # descriptor to be closed, but it will still remain in
  1019. # the socket_map, and eventually its file descriptor
  1020. # will be passed to # select(), which will bomb. See
  1021. # also http://www.plope.com/software/collector/253
  1022. server.close()
  1023. def close_logger(self):
  1024. self.logger.close()
  1025. def setsignals(self):
  1026. receive = self.signal_receiver.receive
  1027. signal.signal(signal.SIGTERM, receive)
  1028. signal.signal(signal.SIGINT, receive)
  1029. signal.signal(signal.SIGQUIT, receive)
  1030. signal.signal(signal.SIGHUP, receive)
  1031. signal.signal(signal.SIGCHLD, receive)
  1032. signal.signal(signal.SIGUSR2, receive)
  1033. def get_signal(self):
  1034. return self.signal_receiver.get_signal()
  1035. def openhttpservers(self, supervisord):
  1036. try:
  1037. self.httpservers = self.make_http_servers(supervisord)
  1038. except socket.error as why:
  1039. if why.args[0] == errno.EADDRINUSE:
  1040. self.usage('Another program is already listening on '
  1041. 'a port that one of our HTTP servers is '
  1042. 'configured to use. Shut this program '
  1043. 'down first before starting supervisord.')
  1044. else:
  1045. help = 'Cannot open an HTTP server: socket.error reported'
  1046. errorname = errno.errorcode.get(why.args[0])
  1047. if errorname is None:
  1048. self.usage('%s %s' % (help, why.args[0]))
  1049. else:
  1050. self.usage('%s errno.%s (%d)' %
  1051. (help, errorname, why.args[0]))
  1052. self.unlink_socketfiles = False
  1053. except ValueError as why:
  1054. self.usage(why.args[0])
  1055. def get_autochildlog_name(self, name, identifier, channel):
  1056. prefix='%s-%s---%s-' % (name, channel, identifier)
  1057. logfile = self.mktempfile(
  1058. suffix='.log',
  1059. prefix=prefix,
  1060. dir=self.childlogdir)
  1061. return logfile
  1062. def clear_autochildlogdir(self):
  1063. # must be called after realize()
  1064. childlogdir = self.childlogdir
  1065. fnre = re.compile(r'.+?---%s-\S+\.log\.{0,1}\d{0,4}' % self.identifier)
  1066. try:
  1067. filenames = os.listdir(childlogdir)
  1068. except (IOError, OSError):
  1069. self.logger.warn('Could not clear childlog dir')
  1070. return
  1071. for filename in filenames:
  1072. if fnre.match(filename):
  1073. pathname = os.path.join(childlogdir, filename)
  1074. try:
  1075. os.remove(pathname)
  1076. except (OSError, IOError):
  1077. self.logger.warn('Failed to clean up %r' % pathname)
  1078. def get_socket_map(self):
  1079. return asyncore.socket_map
  1080. def cleanup_fds(self):
  1081. # try to close any leaked file descriptors (for reload)
  1082. start = 5
  1083. for x in range(start, self.minfds):
  1084. try:
  1085. os.close(x)
  1086. except OSError:
  1087. pass
  1088. def kill(self, pid, signal):
  1089. os.kill(pid, signal)
  1090. def set_uid(self):
  1091. if self.uid is None:
  1092. if os.getuid() == 0:
  1093. return 'Supervisor running as root (no user in config file)'
  1094. return None
  1095. msg = self.dropPrivileges(self.uid)
  1096. if msg is None:
  1097. return 'Set uid to user %s' % self.uid
  1098. return msg
  1099. def dropPrivileges(self, user):
  1100. # Drop root privileges if we have them
  1101. if user is None:
  1102. return "No user specified to setuid to!"
  1103. # get uid for user, which can be a number or username
  1104. try:
  1105. uid = int(user)
  1106. except ValueError:
  1107. try:
  1108. pwrec = pwd.getpwnam(user)
  1109. except KeyError:
  1110. return "Can't find username %r" % user
  1111. uid = pwrec[2]
  1112. else:
  1113. try:
  1114. pwrec = pwd.getpwuid(uid)
  1115. except KeyError:
  1116. return "Can't find uid %r" % uid
  1117. current_uid = os.getuid()
  1118. if current_uid == uid:
  1119. # do nothing and return successfully if the uid is already the
  1120. # current one. this allows a supervisord running as an
  1121. # unprivileged user "foo" to start a process where the config
  1122. # has "user=foo" (same user) in it.
  1123. return
  1124. if current_uid != 0:
  1125. return "Can't drop privilege as nonroot user"
  1126. gid = pwrec[3]
  1127. if hasattr(os, 'setgroups'):
  1128. user = pwrec[0]
  1129. groups = [grprec[2] for grprec in grp.getgrall() if user in
  1130. grprec[3]]
  1131. # always put our primary gid first in this list, otherwise we can
  1132. # lose group info since sometimes the first group in the setgroups
  1133. # list gets overwritten on the subsequent setgid call (at least on
  1134. # freebsd 9 with python 2.7 - this will be safe though for all unix
  1135. # /python version combos)
  1136. groups.insert(0, gid)
  1137. try:
  1138. os.setgroups(groups)
  1139. except OSError:
  1140. return 'Could not set groups of effective user'
  1141. try:
  1142. os.setgid(gid)
  1143. except OSError:
  1144. return 'Could not set group id of effective user'
  1145. os.setuid(uid)
  1146. def waitpid(self):
  1147. # Need pthread_sigmask here to avoid concurrent sigchild, but Python
  1148. # doesn't offer in Python < 3.4. There is still a race condition here;
  1149. # we can get a sigchild while we're sitting in the waitpid call.
  1150. # However, AFAICT, if waitpid is interrupted bu SIGCHILD, as long as we
  1151. # call waitpid again (which happens every so often during the normal
  1152. # course in the mainloop), we'll eventually reap the child that we
  1153. # tried to reap during the interrupted call. At least on Linux, this
  1154. # appears to be true, or at least stopping 50 processes at once never
  1155. # left zombies laying around.
  1156. try:
  1157. pid, sts = os.waitpid(-1, os.WNOHANG)
  1158. except OSError as why:
  1159. err = why.args[0]
  1160. if err not in (errno.ECHILD, errno.EINTR):
  1161. self.logger.critical(
  1162. 'waitpid error; a process may not be cleaned up properly')
  1163. if err == errno.EINTR:
  1164. self.logger.blather('EINTR during reap')
  1165. pid, sts = None, None
  1166. return pid, sts
  1167. def set_rlimits(self):
  1168. limits = []
  1169. if hasattr(resource, 'RLIMIT_NOFILE'):
  1170. limits.append(
  1171. {
  1172. 'msg':('The minimum number of file descriptors required '
  1173. 'to run this process is %(min)s as per the "minfds" '
  1174. 'command-line argument or config file setting. '
  1175. 'The current environment will only allow you '
  1176. 'to open %(hard)s file descriptors. Either raise '
  1177. 'the number of usable file descriptors in your '
  1178. 'environment (see README.rst) or lower the '
  1179. 'minfds setting in the config file to allow '
  1180. 'the process to start.'),
  1181. 'min':self.minfds,
  1182. 'resource':resource.RLIMIT_NOFILE,
  1183. 'name':'RLIMIT_NOFILE',
  1184. })
  1185. if hasattr(resource, 'RLIMIT_NPROC'):
  1186. limits.append(
  1187. {
  1188. 'msg':('The minimum number of available processes required '
  1189. 'to run this program is %(min)s as per the "minprocs" '
  1190. 'command-line argument or config file setting. '
  1191. 'The current environment will only allow you '
  1192. 'to open %(hard)s processes. Either raise '
  1193. 'the number of usable processes in your '
  1194. 'environment (see README.rst) or lower the '
  1195. 'minprocs setting in the config file to allow '
  1196. 'the program to start.'),
  1197. 'min':self.minprocs,
  1198. 'resource':resource.RLIMIT_NPROC,
  1199. 'name':'RLIMIT_NPROC',
  1200. })
  1201. msgs = []
  1202. for limit in limits:
  1203. lmin = limit['min']
  1204. res = limit['resource']
  1205. msg = limit['msg']
  1206. name = limit['name']
  1207. name = name # name is used below by locals()
  1208. soft, hard = resource.getrlimit(res)
  1209. if (soft < lmin) and (soft != -1): # -1 means unlimited
  1210. if (hard < lmin) and (hard != -1):
  1211. # setrlimit should increase the hard limit if we are
  1212. # root, if not then setrlimit raises and we print usage
  1213. hard = lmin
  1214. try:
  1215. resource.setrlimit(res, (lmin, hard))
  1216. msgs.append('Increased %(name)s limit to %(lmin)s' %
  1217. locals())
  1218. except (resource.error, ValueError):
  1219. self.usage(msg % locals())
  1220. return msgs
  1221. def make_logger(self, critical_messages, warn_messages, info_messages):
  1222. # must be called after realize() and after supervisor does setuid()
  1223. format = '%(asctime)s %(levelname)s %(message)s\n'
  1224. self.logger = loggers.getLogger(self.loglevel)
  1225. if self.nodaemon:
  1226. loggers.handle_stdout(self.logger, format)
  1227. loggers.handle_file(
  1228. self.logger,
  1229. self.logfile,
  1230. format,
  1231. rotating=True,
  1232. maxbytes=self.logfile_maxbytes,
  1233. backups=self.logfile_backups,
  1234. )
  1235. for msg in critical_messages:
  1236. self.logger.critical(msg)
  1237. for msg in warn_messages:
  1238. self.logger.warn(msg)
  1239. for msg in info_messages:
  1240. self.logger.info(msg)
  1241. def make_http_servers(self, supervisord):
  1242. from supervisor.http import make_http_servers
  1243. return make_http_servers(self, supervisord)
  1244. def close_fd(self, fd):
  1245. try:
  1246. os.close(fd)
  1247. except OSError:
  1248. pass
  1249. def fork(self):
  1250. return os.fork()
  1251. def dup2(self, frm, to):
  1252. return os.dup2(frm, to)
  1253. def setpgrp(self):
  1254. return os.setpgrp()
  1255. def stat(self, filename):
  1256. return os.stat(filename)
  1257. def write(self, fd, data):
  1258. return os.write(fd, as_bytes(data))
  1259. def execve(self, filename, argv, env):
  1260. return os.execve(filename, argv, env)
  1261. def mktempfile(self, suffix, prefix, dir):
  1262. # set os._urandomfd as a hack around bad file descriptor bug
  1263. # seen in the wild, see
  1264. # http://www.plope.com/software/collector/252
  1265. os._urandomfd = None
  1266. fd, filename = tempfile.mkstemp(suffix, prefix, dir)
  1267. os.close(fd)
  1268. return filename
  1269. def remove(self, path):
  1270. os.remove(path)
  1271. def _exit(self, code):
  1272. os._exit(code)
  1273. def setumask(self, mask):
  1274. os.umask(mask)
  1275. def get_path(self):
  1276. """Return a list corresponding to $PATH, or a default."""
  1277. path = ["/bin", "/usr/bin", "/usr/local/bin"]
  1278. if "PATH" in os.environ:
  1279. p = os.environ["PATH"]
  1280. if p:
  1281. path = p.split(os.pathsep)
  1282. return path
  1283. def get_pid(self):
  1284. return os.getpid()
  1285. def check_execv_args(self, filename, argv, st):
  1286. if st is None:
  1287. raise NotFound("can't find command %r" % filename)
  1288. elif stat.S_ISDIR(st[stat.ST_MODE]):
  1289. raise NotExecutable("command at %r is a directory" % filename)
  1290. elif not (stat.S_IMODE(st[stat.ST_MODE]) & 73):
  1291. # 73 is spelled 0111 in py2, 0o111 in py3
  1292. raise NotExecutable("command at %r is not executable" % filename)
  1293. elif not os.access(filename, os.X_OK):
  1294. raise NoPermission("no permission to run command %r" % filename)
  1295. def reopenlogs(self):
  1296. self.logger.info('supervisord logreopen')
  1297. for handler in self.logger.handlers:
  1298. if hasattr(handler, 'reopen'):
  1299. handler.reopen()
  1300. def readfd(self, fd):
  1301. try:
  1302. data = os.read(fd, 2 << 16) # 128K
  1303. except OSError as why:
  1304. if why.args[0] not in (errno.EWOULDBLOCK, errno.EBADF, errno.EINTR):
  1305. raise
  1306. data = ''
  1307. return as_string(data)
  1308. def process_environment(self):
  1309. os.environ.update(self.environment or {})
  1310. def chdir(self, dir):
  1311. os.chdir(dir)
  1312. def make_pipes(self, stderr=True):
  1313. """ Create pipes for parent to child stdin/stdout/stderr
  1314. communications. Open fd in non-blocking mode so we can read them
  1315. in the mainloop without blocking. If stderr is False, don't
  1316. create a pipe for stderr. """
  1317. pipes = {'child_stdin':None,
  1318. 'stdin':None,
  1319. 'stdout':None,
  1320. 'child_stdout':None,
  1321. 'stderr':None,
  1322. 'child_stderr':None}
  1323. try:
  1324. stdin, child_stdin = os.pipe()
  1325. pipes['child_stdin'], pipes['stdin'] = stdin, child_stdin
  1326. stdout, child_stdout = os.pipe()
  1327. pipes['stdout'], pipes['child_stdout'] = stdout, child_stdout
  1328. if stderr:
  1329. stderr, child_stderr = os.pipe()
  1330. pipes['stderr'], pipes['child_stderr'] = stderr, child_stderr
  1331. for fd in (pipes['stdout'], pipes['stderr'], pipes['stdin']):
  1332. if fd is not None:
  1333. flags = fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NDELAY
  1334. fcntl.fcntl(fd, fcntl.F_SETFL, flags)
  1335. return pipes
  1336. except OSError:
  1337. for fd in pipes.values():
  1338. if fd is not None:
  1339. self.close_fd(fd)
  1340. raise
  1341. def close_parent_pipes(self, pipes):
  1342. for fdname in ('stdin', 'stdout', 'stderr'):
  1343. fd = pipes[fdname]
  1344. if fd is not None:
  1345. self.close_fd(fd)
  1346. def close_child_pipes(self, pipes):
  1347. for fdname in ('child_stdin', 'child_stdout', 'child_stderr'):
  1348. fd = pipes[fdname]
  1349. if fd is not None:
  1350. self.close_fd(fd)
  1351. class ClientOptions(Options):
  1352. positional_args_allowed = 1
  1353. interactive = None
  1354. prompt = None
  1355. serverurl = None
  1356. username = None
  1357. password = None
  1358. history_file = None
  1359. def __init__(self):
  1360. Options.__init__(self, require_configfile=False)
  1361. self.configroot = Dummy()
  1362. self.configroot.supervisorctl = Dummy()
  1363. self.configroot.supervisorctl.interactive = None
  1364. self.configroot.supervisorctl.prompt = 'supervisor'
  1365. self.configroot.supervisorctl.serverurl = None
  1366. self.configroot.supervisorctl.username = None
  1367. self.configroot.supervisorctl.password = None
  1368. self.configroot.supervisorctl.history_file = None
  1369. from supervisor.supervisorctl import DefaultControllerPlugin
  1370. default_factory = ('default', DefaultControllerPlugin, {})
  1371. # we always add the default factory. If you want to a supervisorctl
  1372. # without the default plugin, please write your own supervisorctl.
  1373. self.plugin_factories = [default_factory]
  1374. self.add("interactive", "supervisorctl.interactive", "i",
  1375. "interactive", flag=1, default=0)
  1376. self.add("prompt", "supervisorctl.prompt", default="supervisor")
  1377. self.add("serverurl", "supervisorctl.serverurl", "s:", "serverurl=",
  1378. url, default="http://localhost:9001")
  1379. self.add("username", "supervisorctl.username", "u:", "username=")
  1380. self.add("password", "supervisorctl.password", "p:", "password=")
  1381. self.add("history", "supervisorctl.history_file", "r:", "history_file=")
  1382. def realize(self, *arg, **kw):
  1383. Options.realize(self, *arg, **kw)
  1384. if not self.args:
  1385. self.interactive = 1
  1386. def read_config(self, fp):
  1387. section = self.configroot.supervisorctl
  1388. need_close = False
  1389. if not hasattr(fp, 'read'):
  1390. self.here = os.path.dirname(normalize_path(fp))
  1391. if not self.exists(fp):
  1392. raise ValueError("could not find config file %s" % fp)
  1393. try:
  1394. fp = self.open(fp, 'r')
  1395. need_close = True
  1396. except (IOError, OSError):
  1397. raise ValueError("could not read config file %s" % fp)
  1398. config = UnhosedConfigParser()
  1399. config.mysection = 'supervisorctl'
  1400. try:
  1401. config.read_file(fp)
  1402. except AttributeError:
  1403. config.readfp(fp)
  1404. if need_close:
  1405. fp.close()
  1406. sections = config.sections()
  1407. if not 'supervisorctl' in sections:
  1408. raise ValueError('.ini file does not include supervisorctl section')
  1409. serverurl = config.getdefault('serverurl', 'http://localhost:9001')
  1410. if serverurl.startswith('unix://'):
  1411. sf = serverurl[7:]
  1412. path = expand(sf, {'here':self.here}, 'serverurl')
  1413. path = normalize_path(path)
  1414. serverurl = 'unix://%s' % path
  1415. section.serverurl = serverurl
  1416. # The defaults used below are really set in __init__ (since
  1417. # section==self.configroot.supervisorctl)
  1418. section.prompt = config.getdefault('prompt', section.prompt)
  1419. section.username = config.getdefault('username', section.username)
  1420. section.password = config.getdefault('password', section.password)
  1421. history_file = config.getdefault('history_file', section.history_file)
  1422. if history_file:
  1423. history_file = normalize_path(history_file)
  1424. section.history_file = history_file
  1425. self.history_file = history_file
  1426. else:
  1427. section.history_file = None
  1428. self.history_file = None
  1429. self.plugin_factories += self.get_plugins(
  1430. config,
  1431. 'supervisor.ctl_factory',
  1432. 'ctlplugin:'
  1433. )
  1434. return section
  1435. # TODO: not covered by any test, but used by supervisorctl
  1436. def getServerProxy(self):
  1437. return xmlrpclib.ServerProxy(
  1438. # dumbass ServerProxy won't allow us to pass in a non-HTTP url,
  1439. # so we fake the url we pass into it and always use the transport's
  1440. # 'serverurl' to figure out what to attach to
  1441. 'http://127.0.0.1',
  1442. transport = xmlrpc.SupervisorTransport(self.username,
  1443. self.password,
  1444. self.serverurl)
  1445. )
  1446. _marker = []
  1447. class UnhosedConfigParser(ConfigParser.RawConfigParser):
  1448. mysection = 'supervisord'
  1449. def read_string(self, s):
  1450. s = StringIO(s)
  1451. try:
  1452. return self.read_file(s) # Python 3.2 or later
  1453. except AttributeError:
  1454. return self.readfp(s)
  1455. def saneget(self, section, option, default=_marker, do_expand=True,
  1456. expansions={}):
  1457. expansions.update(environ_expansions())
  1458. try:
  1459. optval = self.get(section, option)
  1460. if isinstance(optval, basestring) and do_expand:
  1461. return expand(optval,
  1462. expansions,
  1463. "%s.%s" % (section, option))
  1464. return optval
  1465. except ConfigParser.NoOptionError:
  1466. if default is _marker:
  1467. raise
  1468. else:
  1469. return default
  1470. def getdefault(self, option, default=_marker, expansions={}, **kwargs):
  1471. return self.saneget(self.mysection, option, default=default,
  1472. expansions=expansions, **kwargs)
  1473. class Config(object):
  1474. def __ne__(self, other):
  1475. return not self.__eq__(other)
  1476. def __lt__(self, other):
  1477. if self.priority == other.priority:
  1478. return self.name < other.name
  1479. return self.priority < other.priority
  1480. def __le__(self, other):
  1481. if self.priority == other.priority:
  1482. return self.name <= other.name
  1483. return self.priority <= other.priority
  1484. def __gt__(self, other):
  1485. if self.priority == other.priority:
  1486. return self.name > other.name
  1487. return self.priority > other.priority
  1488. def __ge__(self, other):
  1489. if self.priority == other.priority:
  1490. return self.name >= other.name
  1491. return self.priority >= other.priority
  1492. def __repr__(self):
  1493. return '<%s instance at %s named %s>' % (self.__class__, id(self),
  1494. self.name)
  1495. class ProcessConfig(Config):
  1496. req_param_names = [
  1497. 'name', 'uid', 'command', 'directory', 'umask', 'priority',
  1498. 'autostart', 'autorestart', 'startsecs', 'startretries',
  1499. 'stdout_logfile', 'stdout_capture_maxbytes',
  1500. 'stdout_events_enabled', 'stdout_syslog',
  1501. 'stdout_logfile_backups', 'stdout_logfile_maxbytes',
  1502. 'stderr_logfile', 'stderr_capture_maxbytes',
  1503. 'stderr_logfile_backups', 'stderr_logfile_maxbytes',
  1504. 'stderr_events_enabled', 'stderr_syslog',
  1505. 'stopsignal', 'stopwaitsecs', 'stopasgroup', 'killasgroup',
  1506. 'exitcodes', 'redirect_stderr' ]
  1507. optional_param_names = [ 'environment', 'serverurl' ]
  1508. def __init__(self, options, **params):
  1509. self.options = options
  1510. for name in self.req_param_names:
  1511. setattr(self, name, params[name])
  1512. for name in self.optional_param_names:
  1513. setattr(self, name, params.get(name, None))
  1514. def __eq__(self, other):
  1515. if not isinstance(other, ProcessConfig):
  1516. return False
  1517. for name in self.req_param_names + self.optional_param_names:
  1518. if Automatic in [getattr(self, name), getattr(other, name)] :
  1519. continue
  1520. if getattr(self, name) != getattr(other, name):
  1521. return False
  1522. return True
  1523. def create_autochildlogs(self):
  1524. # temporary logfiles which are erased at start time
  1525. get_autoname = self.options.get_autochildlog_name
  1526. sid = self.options.identifier
  1527. name = self.name
  1528. if self.stdout_logfile is Automatic:
  1529. self.stdout_logfile = get_autoname(name, sid, 'stdout')
  1530. if self.stderr_logfile is Automatic:
  1531. self.stderr_logfile = get_autoname(name, sid, 'stderr')
  1532. def make_process(self, group=None):
  1533. from supervisor.process import Subprocess
  1534. process = Subprocess(self)
  1535. process.group = group
  1536. return process
  1537. def make_dispatchers(self, proc):
  1538. use_stderr = not self.redirect_stderr
  1539. p = self.options.make_pipes(use_stderr)
  1540. stdout_fd,stderr_fd,stdin_fd = p['stdout'],p['stderr'],p['stdin']
  1541. dispatchers = {}
  1542. from supervisor.dispatchers import POutputDispatcher
  1543. from supervisor.dispatchers import PInputDispatcher
  1544. from supervisor import events
  1545. if stdout_fd is not None:
  1546. etype = events.ProcessCommunicationStdoutEvent
  1547. dispatchers[stdout_fd] = POutputDispatcher(proc, etype, stdout_fd)
  1548. if stderr_fd is not None:
  1549. etype = events.ProcessCommunicationStderrEvent
  1550. dispatchers[stderr_fd] = POutputDispatcher(proc,etype, stderr_fd)
  1551. if stdin_fd is not None:
  1552. dispatchers[stdin_fd] = PInputDispatcher(proc, 'stdin', stdin_fd)
  1553. return dispatchers, p
  1554. class EventListenerConfig(ProcessConfig):
  1555. def make_dispatchers(self, proc):
  1556. # always use_stderr=True for eventlisteners because mixing stderr
  1557. # messages into stdout would break the eventlistener protocol
  1558. use_stderr = True
  1559. p = self.options.make_pipes(use_stderr)
  1560. stdout_fd,stderr_fd,stdin_fd = p['stdout'],p['stderr'],p['stdin']
  1561. dispatchers = {}
  1562. from supervisor.dispatchers import PEventListenerDispatcher
  1563. from supervisor.dispatchers import PInputDispatcher
  1564. from supervisor.dispatchers import POutputDispatcher
  1565. from supervisor import events
  1566. if stdout_fd is not None:
  1567. dispatchers[stdout_fd] = PEventListenerDispatcher(proc, 'stdout',
  1568. stdout_fd)
  1569. if stderr_fd is not None:
  1570. etype = events.ProcessCommunicationStderrEvent
  1571. dispatchers[stderr_fd] = POutputDispatcher(proc, etype, stderr_fd)
  1572. if stdin_fd is not None:
  1573. dispatchers[stdin_fd] = PInputDispatcher(proc, 'stdin', stdin_fd)
  1574. return dispatchers, p
  1575. class FastCGIProcessConfig(ProcessConfig):
  1576. def make_process(self, group=None):
  1577. if group is None:
  1578. raise NotImplementedError('FastCGI programs require a group')
  1579. from supervisor.process import FastCGISubprocess
  1580. process = FastCGISubprocess(self)
  1581. process.group = group
  1582. return process
  1583. def make_dispatchers(self, proc):
  1584. dispatchers, p = ProcessConfig.make_dispatchers(self, proc)
  1585. #FastCGI child processes expect the FastCGI socket set to
  1586. #file descriptor 0, so supervisord cannot use stdin
  1587. #to communicate with the child process
  1588. stdin_fd = p['stdin']
  1589. if stdin_fd is not None:
  1590. dispatchers[stdin_fd].close()
  1591. return dispatchers, p
  1592. class ProcessGroupConfig(Config):
  1593. def __init__(self, options, name, priority, process_configs):
  1594. self.options = options
  1595. self.name = name
  1596. self.priority = priority
  1597. self.process_configs = process_configs
  1598. def __eq__(self, other):
  1599. if not isinstance(other, ProcessGroupConfig):
  1600. return False
  1601. if self.name != other.name:
  1602. return False
  1603. if self.priority != other.priority:
  1604. return False
  1605. if self.process_configs != other.process_configs:
  1606. return False
  1607. return True
  1608. def after_setuid(self):
  1609. for config in self.process_configs:
  1610. config.create_autochildlogs()
  1611. def make_group(self):
  1612. from supervisor.process import ProcessGroup
  1613. return ProcessGroup(self)
  1614. class EventListenerPoolConfig(Config):
  1615. def __init__(self, options, name, priority, process_configs, buffer_size,
  1616. pool_events, result_handler):
  1617. self.options = options
  1618. self.name = name
  1619. self.priority = priority
  1620. self.process_configs = process_configs
  1621. self.buffer_size = buffer_size
  1622. self.pool_events = pool_events
  1623. self.result_handler = result_handler
  1624. def __eq__(self, other):
  1625. if not isinstance(other, EventListenerPoolConfig):
  1626. return False
  1627. if (self.name == other.name) and (self.priority == other.priority):
  1628. return True
  1629. return False
  1630. def after_setuid(self):
  1631. for config in self.process_configs:
  1632. config.create_autochildlogs()
  1633. def make_group(self):
  1634. from supervisor.process import EventListenerPool
  1635. return EventListenerPool(self)
  1636. class FastCGIGroupConfig(ProcessGroupConfig):
  1637. def __init__(self, options, name, priority, process_configs, socket_config):
  1638. ProcessGroupConfig.__init__(
  1639. self,
  1640. options,
  1641. name,
  1642. priority,
  1643. process_configs,
  1644. )
  1645. self.socket_config = socket_config
  1646. def __eq__(self, other):
  1647. if not isinstance(other, FastCGIGroupConfig):
  1648. return False
  1649. if self.socket_config != other.socket_config:
  1650. return False
  1651. return ProcessGroupConfig.__eq__(self, other)
  1652. def make_group(self):
  1653. from supervisor.process import FastCGIProcessGroup
  1654. return FastCGIProcessGroup(self)
  1655. def readFile(filename, offset, length):
  1656. """ Read length bytes from the file named by filename starting at
  1657. offset """
  1658. absoffset = abs(offset)
  1659. abslength = abs(length)
  1660. try:
  1661. with open(filename, 'rb') as f:
  1662. if absoffset != offset:
  1663. # negative offset returns offset bytes from tail of the file
  1664. if length:
  1665. raise ValueError('BAD_ARGUMENTS')
  1666. f.seek(0, 2)
  1667. sz = f.tell()
  1668. pos = int(sz - absoffset)
  1669. if pos < 0:
  1670. pos = 0
  1671. f.seek(pos)
  1672. data = f.read(absoffset)
  1673. else:
  1674. if abslength != length:
  1675. raise ValueError('BAD_ARGUMENTS')
  1676. if length == 0:
  1677. f.seek(offset)
  1678. data = f.read()
  1679. else:
  1680. f.seek(offset)
  1681. data = f.read(length)
  1682. except (OSError, IOError):
  1683. raise ValueError('FAILED')
  1684. return data
  1685. def tailFile(filename, offset, length):
  1686. """
  1687. Read length bytes from the file named by filename starting at
  1688. offset, automatically increasing offset and setting overflow
  1689. flag if log size has grown beyond (offset + length). If length
  1690. bytes are not available, as many bytes as are available are returned.
  1691. """
  1692. try:
  1693. with open(filename, 'rb') as f:
  1694. overflow = False
  1695. f.seek(0, 2)
  1696. sz = f.tell()
  1697. if sz > (offset + length):
  1698. overflow = True
  1699. offset = sz - 1
  1700. if (offset + length) > sz:
  1701. if offset > (sz - 1):
  1702. length = 0
  1703. offset = sz - length
  1704. if offset < 0:
  1705. offset = 0
  1706. if length < 0:
  1707. length = 0
  1708. if length == 0:
  1709. data = ''
  1710. else:
  1711. f.seek(offset)
  1712. data = f.read(length)
  1713. offset = sz
  1714. return [as_string(data), offset, overflow]
  1715. except (OSError, IOError):
  1716. return ['', offset, False]
  1717. # Helpers for dealing with signals and exit status
  1718. def decode_wait_status(sts):
  1719. """Decode the status returned by wait() or waitpid().
  1720. Return a tuple (exitstatus, message) where exitstatus is the exit
  1721. status, or -1 if the process was killed by a signal; and message
  1722. is a message telling what happened. It is the caller's
  1723. responsibility to display the message.
  1724. """
  1725. if os.WIFEXITED(sts):
  1726. es = os.WEXITSTATUS(sts) & 0xffff
  1727. msg = "exit status %s" % es
  1728. return es, msg
  1729. elif os.WIFSIGNALED(sts):
  1730. sig = os.WTERMSIG(sts)
  1731. msg = "terminated by %s" % signame(sig)
  1732. if hasattr(os, "WCOREDUMP"):
  1733. iscore = os.WCOREDUMP(sts)
  1734. else:
  1735. iscore = sts & 0x80
  1736. if iscore:
  1737. msg += " (core dumped)"
  1738. return -1, msg
  1739. else:
  1740. msg = "unknown termination cause 0x%04x" % sts
  1741. return -1, msg
  1742. _signames = None
  1743. def signame(sig):
  1744. """Return a symbolic name for a signal.
  1745. Return "signal NNN" if there is no corresponding SIG name in the
  1746. signal module.
  1747. """
  1748. if _signames is None:
  1749. _init_signames()
  1750. return _signames.get(sig) or "signal %d" % sig
  1751. def _init_signames():
  1752. global _signames
  1753. d = {}
  1754. for k, v in signal.__dict__.items():
  1755. k_startswith = getattr(k, "startswith", None)
  1756. if k_startswith is None:
  1757. continue
  1758. if k_startswith("SIG") and not k_startswith("SIG_"):
  1759. d[v] = k
  1760. _signames = d
  1761. class SignalReceiver:
  1762. def __init__(self):
  1763. self._signals_recvd = []
  1764. def receive(self, sig, frame):
  1765. if sig not in self._signals_recvd:
  1766. self._signals_recvd.append(sig)
  1767. def get_signal(self):
  1768. if self._signals_recvd:
  1769. sig = self._signals_recvd.pop(0)
  1770. else:
  1771. sig = None
  1772. return sig
  1773. # miscellaneous utility functions
  1774. def expand(s, expansions, name):
  1775. try:
  1776. return s % expansions
  1777. except KeyError as ex:
  1778. available = list(expansions.keys())
  1779. available.sort()
  1780. raise ValueError(
  1781. 'Format string %r for %r contains names (%s) which cannot be '
  1782. 'expanded. Available names: %s' %
  1783. (s, name, str(ex), ", ".join(available)))
  1784. except Exception as ex:
  1785. raise ValueError(
  1786. 'Format string %r for %r is badly formatted: %s' %
  1787. (s, name, str(ex))
  1788. )
  1789. def environ_expansions():
  1790. """Return dict of environment variables, suitable for use in string
  1791. expansions.
  1792. Every environment variable is prefixed by 'ENV_'.
  1793. """
  1794. x = {}
  1795. for key, value in os.environ.items():
  1796. x['ENV_%s' % key] = value
  1797. return x
  1798. def make_namespec(group_name, process_name):
  1799. # we want to refer to the process by its "short name" (a process named
  1800. # process1 in the group process1 has a name "process1"). This is for
  1801. # backwards compatibility
  1802. if group_name == process_name:
  1803. name = process_name
  1804. else:
  1805. name = '%s:%s' % (group_name, process_name)
  1806. return name
  1807. def split_namespec(namespec):
  1808. names = namespec.split(':', 1)
  1809. if len(names) == 2:
  1810. # group and process name differ
  1811. group_name, process_name = names
  1812. if not process_name or process_name == '*':
  1813. process_name = None
  1814. else:
  1815. # group name is same as process name
  1816. group_name, process_name = namespec, namespec
  1817. return group_name, process_name
  1818. # exceptions
  1819. class ProcessException(Exception):
  1820. """ Specialized exceptions used when attempting to start a process """
  1821. class BadCommand(ProcessException):
  1822. """ Indicates the command could not be parsed properly. """
  1823. class NotExecutable(ProcessException):
  1824. """ Indicates that the filespec cannot be executed because its path
  1825. resolves to a file which is not executable, or which is a directory. """
  1826. class NotFound(ProcessException):
  1827. """ Indicates that the filespec cannot be executed because it could not
  1828. be found """
  1829. class NoPermission(ProcessException):
  1830. """ Indicates that the file cannot be executed because the supervisor
  1831. process does not possess the appropriate UNIX filesystem permission
  1832. to execute the file. """