options.py 71 KB

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