def main(argv=sys.argv): # Refuse to run as root if not getattr(os, 'getuid', lambda: -1)(): sys.stderr.write('%s: error: refusing to run as root to prevent ' 'potential damage.\n' % os.path.basename(argv[0])) sys.exit(77) volttron_home = os.path.normpath( config.expandall(os.environ.get('VOLTTRON_HOME', '~/.volttron'))) os.environ['VOLTTRON_HOME'] = volttron_home # Setup option parser parser = config.ArgumentParser( prog=os.path.basename(argv[0]), add_help=False, description='VOLTTRON platform service', usage='%(prog)s [OPTION]...', argument_default=argparse.SUPPRESS, epilog='Boolean options, which take no argument, may be inversed by ' 'prefixing the option with no- (e.g. --autostart may be ' 'inversed using --no-autostart).') parser.add_argument('-c', '--config', metavar='FILE', action='parse_config', ignore_unknown=True, sections=[None, 'volttron'], help='read configuration from FILE') parser.add_argument('-l', '--log', metavar='FILE', default=None, help='send log output to FILE instead of stderr') parser.add_argument('-L', '--log-config', metavar='FILE', help='read logging configuration from FILE') parser.add_argument('--log-level', metavar='LOGGER:LEVEL', action=LogLevelAction, help='override default logger logging level') parser.add_argument('--monitor', action='store_true', help='monitor and log connections (implies -v)') parser.add_argument( '-q', '--quiet', action='add_const', const=10, dest='verboseness', help='decrease logger verboseness; may be used multiple times') parser.add_argument( '-v', '--verbose', action='add_const', const=-10, dest='verboseness', help='increase logger verboseness; may be used multiple times') parser.add_argument('--verboseness', type=int, metavar='LEVEL', default=logging.WARNING, help='set logger verboseness') #parser.add_argument( # '--volttron-home', env_var='VOLTTRON_HOME', metavar='PATH', # help='VOLTTRON configuration directory') parser.add_argument('--show-config', action='store_true', help=argparse.SUPPRESS) parser.add_help_argument() parser.add_version_argument(version='%(prog)s ' + __version__) agents = parser.add_argument_group('agent options') agents.add_argument('--autostart', action='store_true', inverse='--no-autostart', help='automatically start enabled agents and services') agents.add_argument('--no-autostart', action='store_false', dest='autostart', help=argparse.SUPPRESS) agents.add_argument( '--publish-address', metavar='ZMQADDR', help='ZeroMQ URL used for pre-3.x agent publishing (deprecated)') agents.add_argument( '--subscribe-address', metavar='ZMQADDR', help='ZeroMQ URL used for pre-3.x agent subscriptions (deprecated)') agents.add_argument('--vip-address', metavar='ZMQADDR', action='append', default=[], help='ZeroMQ URL to bind for VIP connections') agents.add_argument( '--vip-local-address', metavar='ZMQADDR', help='ZeroMQ URL to bind for local agent VIP connections') # XXX: re-implement control options #on #control.add_argument( # '--allow-root', action='store_true', inverse='--no-allow-root', # help='allow root to connect to control socket') #control.add_argument( # '--no-allow-root', action='store_false', dest='allow_root', # help=argparse.SUPPRESS) #control.add_argument( # '--allow-users', action='store_list', metavar='LIST', # help='users allowed to connect to control socket') #control.add_argument( # '--allow-groups', action='store_list', metavar='LIST', # help='user groups allowed to connect to control socket') if HAVE_RESTRICTED: class RestrictedAction(argparse.Action): def __init__(self, option_strings, dest, const=True, help=None, **kwargs): super(RestrictedAction, self).__init__(option_strings, dest=argparse.SUPPRESS, nargs=0, const=const, help=help) def __call__(self, parser, namespace, values, option_string=None): namespace.verify_agents = self.const namespace.resource_monitor = self.const #namespace.mobility = self.const restrict = parser.add_argument_group('restricted options') restrict.add_argument( '--restricted', action=RestrictedAction, inverse='--no-restricted', help='shortcut to enable all restricted features') restrict.add_argument('--no-restricted', action=RestrictedAction, const=False, help=argparse.SUPPRESS) restrict.add_argument('--verify', action='store_true', inverse='--no-verify', help='verify agent integrity before execution') restrict.add_argument('--no-verify', action='store_false', dest='verify_agents', help=argparse.SUPPRESS) restrict.add_argument('--resource-monitor', action='store_true', inverse='--no-resource-monitor', help='enable agent resource management') restrict.add_argument('--no-resource-monitor', action='store_false', dest='resource_monitor', help=argparse.SUPPRESS) #restrict.add_argument( # '--mobility', action='store_true', inverse='--no-mobility', # help='enable agent mobility') #restrict.add_argument( # '--no-mobility', action='store_false', dest='mobility', # help=argparse.SUPPRESS) ipc = 'ipc://%s$VOLTTRON_HOME/run/' % ( '@' if sys.platform.startswith('linux') else '') parser.set_defaults( log=None, log_config=None, monitor=False, verboseness=logging.WARNING, volttron_home=volttron_home, autostart=True, publish_address=ipc + 'publish', subscribe_address=ipc + 'subscribe', vip_address=[], vip_local_address=ipc + 'vip.socket', #allow_root=False, #allow_users=None, #allow_groups=None, verify_agents=True, resource_monitor=True, #mobility=True, ) # Parse and expand options args = argv[1:] conf = os.path.join(volttron_home, 'config') if os.path.exists(conf) and 'SKIP_VOLTTRON_CONFIG' not in os.environ: args = ['--config', conf] + args logging.getLogger().setLevel(logging.NOTSET) opts = parser.parse_args(args) if opts.log: opts.log = config.expandall(opts.log) if opts.log_config: opts.log_config = config.expandall(opts.log_config) opts.publish_address = config.expandall(opts.publish_address) opts.subscribe_address = config.expandall(opts.subscribe_address) opts.vip_address = [config.expandall(addr) for addr in opts.vip_address] opts.vip_local_address = config.expandall(opts.vip_local_address) if getattr(opts, 'show_config', False): for name, value in sorted(vars(opts).iteritems()): print(name, repr(value)) return # Configure logging level = max(1, opts.verboseness) if opts.monitor and level > logging.INFO: level = logging.INFO if opts.log is None: log_to_file(sys.stderr, level) elif opts.log == '-': log_to_file(sys.stdout, level) elif opts.log: log_to_file(opts.log, level, handler_class=handlers.WatchedFileHandler) else: log_to_file(None, 100, handler_class=lambda x: logging.NullHandler()) if opts.log_config: error = configure_logging(opts.log_config) if error: parser.error('{}: {}'.format(*error)) # Increase open files resource limit to max or 8192 if unlimited try: soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE) except OSError: _log.exception('error getting open file limits') else: if soft != hard and soft != resource.RLIM_INFINITY: try: limit = 8192 if hard == resource.RLIM_INFINITY else hard resource.setrlimit(resource.RLIMIT_NOFILE, (limit, hard)) except OSError: _log.exception('error setting open file limits') else: _log.debug('open file resource limit increased from %d to %d', soft, limit) # Set configuration if HAVE_RESTRICTED: if opts.verify_agents: _log.info('Agent integrity verification enabled') if opts.resource_monitor: _log.info('Resource monitor enabled') opts.resmon = resmon.ResourceMonitor() opts.aip = aip.AIPplatform(opts) opts.aip.setup() # Check for secure mode/permissions on VOLTTRON_HOME directory mode = os.stat(volttron_home).st_mode if mode & (stat.S_IWGRP | stat.S_IWOTH): _log.warning('insecure mode on directory: %s', volttron_home) # Get or generate encryption key keyfile = os.path.join(volttron_home, 'curve.key') _log.debug('using key file %s', keyfile) try: st = os.stat(keyfile) except OSError as exc: if exc.errno != errno.ENOENT: parser.error(str(exc)) # Key doesn't exist, so create it securely _log.info('generating missing key file') try: fd = os.open(keyfile, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600) except OSError as exc: parser.error(str(exc)) try: key = ''.join(curve_keypair()) os.write(fd, key) finally: os.close(fd) else: if st.st_mode & (stat.S_IRWXG | stat.S_IRWXO): _log.warning('insecure mode on key file') if not st.st_size: _log.warning('empty key file; VIP encryption is disabled!') key = '' else: # Allow two extra bytes in case someone opened the file with # a text editor and it appended '\n' or '\r\n'. if not 80 <= st.st_size <= 82: _log.warning('key file is wrong size; connections may fail') with open(keyfile) as infile: key = infile.read(80) publickey = key[:40] if publickey: _log.info('public key: %r (%s)', publickey, encode_key(publickey)) secretkey = key[40:] # The following line doesn't appear to do anything, but it creates # a context common to the green and non-green zmq modules. zmq.Context.instance() # DO NOT REMOVE LINE!! # Main loops def router(stop): try: Router(opts.vip_local_address, opts.vip_address, secretkey=secretkey, default_user_id=b'vip.service', monitor=opts.monitor).run() except Exception: _log.exception('Unhandled exception in router loop') finally: stop() address = 'inproc://vip' try: # Ensure auth service is running before router auth = AuthService(address=address, identity='auth') event = gevent.event.Event() auth_task = gevent.spawn(auth.core.run, event) event.wait() del event # Start router in separate thread to remain responsive thread = threading.Thread(target=router, args=(auth.core.stop, )) thread.daemon = True thread.start() # Launch additional services and wait for them to start before # auto-starting agents services = [ ControlService(opts.aip, address=address, identity='control'), PubSubService(address=address, identity='pubsub'), CompatPubSub(address=address, identity='pubsub.compat', publish_address=opts.publish_address, subscribe_address=opts.subscribe_address), ] events = [gevent.event.Event() for service in services] tasks = [ gevent.spawn(service.core.run, event) for service, event in zip(services, events) ] tasks.append(auth_task) gevent.wait(events) del events # Auto-start agents now that all services are up if opts.autostart: for name, error in opts.aip.autostart(): _log.error('error starting {!r}: {}\n'.format(name, error)) # Wait for any service to stop, signaling exit try: gevent.wait(tasks, count=1) except KeyboardInterrupt: _log.info('SIGINT received; shutting down') sys.stderr.write('Shutting down.\n') for task in tasks: task.kill(block=False) gevent.wait(tasks) finally: opts.aip.finish()
def main(argv=sys.argv): # Refuse to run as root if not getattr(os, 'getuid', lambda: -1)(): sys.stderr.write('%s: error: refusing to run as root to prevent ' 'potential damage.\n' % os.path.basename(argv[0])) sys.exit(77) volttron_home = os.path.normpath( config.expandall(os.environ.get('VOLTTRON_HOME', '~/.volttron'))) os.environ['VOLTTRON_HOME'] = volttron_home # Setup option parser parser = config.ArgumentParser( prog=os.path.basename(argv[0]), add_help=False, description='VOLTTRON platform service', usage='%(prog)s [OPTION]...', argument_default=argparse.SUPPRESS, epilog='Boolean options, which take no argument, may be inversed by ' 'prefixing the option with no- (e.g. --autostart may be ' 'inversed using --no-autostart).') parser.add_argument('-c', '--config', metavar='FILE', action='parse_config', ignore_unknown=False, sections=[None, 'volttron'], help='read configuration from FILE') parser.add_argument('-l', '--log', metavar='FILE', default=None, help='send log output to FILE instead of stderr') parser.add_argument('-L', '--log-config', metavar='FILE', help='read logging configuration from FILE') parser.add_argument('--log-level', metavar='LOGGER:LEVEL', action=LogLevelAction, help='override default logger logging level') parser.add_argument('--monitor', action='store_true', help='monitor and log connections (implies -v)') parser.add_argument( '-q', '--quiet', action='add_const', const=10, dest='verboseness', help='decrease logger verboseness; may be used multiple times') parser.add_argument( '-v', '--verbose', action='add_const', const=-10, dest='verboseness', help='increase logger verboseness; may be used multiple times') parser.add_argument('--verboseness', type=int, metavar='LEVEL', default=logging.WARNING, help='set logger verboseness') # parser.add_argument( # '--volttron-home', env_var='VOLTTRON_HOME', metavar='PATH', # help='VOLTTRON configuration directory') parser.add_argument('--show-config', action='store_true', help=argparse.SUPPRESS) parser.add_help_argument() parser.add_version_argument(version='%(prog)s ' + __version__) agents = parser.add_argument_group('agent options') agents.add_argument('--autostart', action='store_true', inverse='--no-autostart', help='automatically start enabled agents and services') agents.add_argument('--no-autostart', action='store_false', dest='autostart', help=argparse.SUPPRESS) agents.add_argument( '--publish-address', metavar='ZMQADDR', help='ZeroMQ URL used for pre-3.x agent publishing (deprecated)') agents.add_argument( '--subscribe-address', metavar='ZMQADDR', help='ZeroMQ URL used for pre-3.x agent subscriptions (deprecated)') agents.add_argument('--vip-address', metavar='ZMQADDR', action='append', default=[], help='ZeroMQ URL to bind for VIP connections') agents.add_argument( '--vip-local-address', metavar='ZMQADDR', help='ZeroMQ URL to bind for local agent VIP connections') agents.add_argument( '--bind-web-address', metavar='BINDWEBADDR', default=None, help='Bind a web server to the specified ip:port passed') agents.add_argument( '--volttron-central-address', default=None, help='The web address of a volttron central install instance.') agents.add_argument('--volttron-central-serverkey', default=None, help='The serverkey of volttron central.') agents.add_argument( '--instance-name', default=None, help='The name of the instance that will be reported to ' 'VOLTTRON central.') agents.add_argument('--msgdebug', action='store_true', help='Route all messages to an agent while debugging.') agents.add_argument( '--setup-mode', action='store_true', help= 'Setup mode flag for setting up authorization of external platforms.') # XXX: re-implement control options # on # control.add_argument( # '--allow-root', action='store_true', inverse='--no-allow-root', # help='allow root to connect to control socket') # control.add_argument( # '--no-allow-root', action='store_false', dest='allow_root', # help=argparse.SUPPRESS) # control.add_argument( # '--allow-users', action='store_list', metavar='LIST', # help='users allowed to connect to control socket') # control.add_argument( # '--allow-groups', action='store_list', metavar='LIST', # help='user groups allowed to connect to control socket') if HAVE_RESTRICTED: class RestrictedAction(argparse.Action): def __init__(self, option_strings, dest, const=True, help=None, **kwargs): super(RestrictedAction, self).__init__(option_strings, dest=argparse.SUPPRESS, nargs=0, const=const, help=help) def __call__(self, parser, namespace, values, option_string=None): namespace.verify_agents = self.const namespace.resource_monitor = self.const # namespace.mobility = self.const restrict = parser.add_argument_group('restricted options') restrict.add_argument( '--restricted', action=RestrictedAction, inverse='--no-restricted', help='shortcut to enable all restricted features') restrict.add_argument('--no-restricted', action=RestrictedAction, const=False, help=argparse.SUPPRESS) restrict.add_argument('--verify', action='store_true', inverse='--no-verify', help='verify agent integrity before execution') restrict.add_argument('--no-verify', action='store_false', dest='verify_agents', help=argparse.SUPPRESS) restrict.add_argument('--resource-monitor', action='store_true', inverse='--no-resource-monitor', help='enable agent resource management') restrict.add_argument('--no-resource-monitor', action='store_false', dest='resource_monitor', help=argparse.SUPPRESS) # restrict.add_argument( # '--mobility', action='store_true', inverse='--no-mobility', # help='enable agent mobility') # restrict.add_argument( # '--no-mobility', action='store_false', dest='mobility', # help=argparse.SUPPRESS) ipc = 'ipc://%s$VOLTTRON_HOME/run/' % ( '@' if sys.platform.startswith('linux') else '') parser.set_defaults( log=None, log_config=None, monitor=False, verboseness=logging.WARNING, volttron_home=volttron_home, autostart=True, publish_address=ipc + 'publish', subscribe_address=ipc + 'subscribe', vip_address=[], vip_local_address=ipc + 'vip.socket', # This is used to start the web server from the web module. bind_web_address=None, # Used to contact volttron central when registering volttron central # platform agent. volttron_central_address=None, volttron_central_serverkey=None, instance_name=None, # allow_root=False, # allow_users=None, # allow_groups=None, verify_agents=True, resource_monitor=True, # mobility=True, msgdebug=None, setup_mode=False) # Parse and expand options args = argv[1:] conf = os.path.join(volttron_home, 'config') if os.path.exists(conf) and 'SKIP_VOLTTRON_CONFIG' not in os.environ: args = ['--config', conf] + args logging.getLogger().setLevel(logging.NOTSET) opts = parser.parse_args(args) start_volttron_process(opts)
def main(argv=sys.argv): volttron_home = config.expandall( os.environ.get('VOLTTRON_HOME', '~/.volttron')) os.environ['VOLTTRON_HOME'] = volttron_home # Setup option parser parser = config.ArgumentParser( prog=os.path.basename(argv[0]), add_help=False, description='VOLTTRON platform service', usage='%(prog)s [OPTION]...', argument_default=argparse.SUPPRESS, epilog='Boolean options, which take no argument, may be inversed by ' 'prefixing the option with no- (e.g. --autostart may be ' 'inversed using --no-autostart).') parser.add_argument('-c', '--config', metavar='FILE', action='parse_config', ignore_unknown=True, sections=[None, 'volttron'], help='read configuration from FILE') parser.add_argument('-l', '--log', metavar='FILE', default=None, help='send log output to FILE instead of stderr') parser.add_argument('-L', '--log-config', metavar='FILE', help='read logging configuration from FILE') parser.add_argument('--log-level', metavar='LOGGER:LEVEL', action=LogLevelAction, help='override default logger logging level') parser.add_argument( '-q', '--quiet', action='add_const', const=10, dest='verboseness', help='decrease logger verboseness; may be used multiple times') parser.add_argument( '-v', '--verbose', action='add_const', const=-10, dest='verboseness', help='increase logger verboseness; may be used multiple times') parser.add_argument('--verboseness', type=int, metavar='LEVEL', default=logging.WARNING, help='set logger verboseness') #parser.add_argument( # '--volttron-home', env_var='VOLTTRON_HOME', metavar='PATH', # help='VOLTTRON configuration directory') parser.add_argument('--show-config', action='store_true', help=argparse.SUPPRESS) parser.add_help_argument() parser.add_version_argument(version='%(prog)s ' + __version__) agents = parser.add_argument_group('agent options') agents.add_argument('--autostart', action='store_true', inverse='--no-autostart', help='automatically start enabled agents and services') agents.add_argument('--no-autostart', action='store_false', dest='autostart', help=argparse.SUPPRESS) agents.add_argument('--publish-address', metavar='ZMQADDR', help='ZeroMQ URL used for agent publishing') agents.add_argument('--subscribe-address', metavar='ZMQADDR', help='ZeroMQ URL used for agent subscriptions') agents.add_argument('--vip-address', metavar='ZMQADDR', action='append', default=[], help='ZeroMQ URL to bind for VIP connections') # XXX: re-implement control options #on #control.add_argument( # '--allow-root', action='store_true', inverse='--no-allow-root', # help='allow root to connect to control socket') #control.add_argument( # '--no-allow-root', action='store_false', dest='allow_root', # help=argparse.SUPPRESS) #control.add_argument( # '--allow-users', action='store_list', metavar='LIST', # help='users allowed to connect to control socket') #control.add_argument( # '--allow-groups', action='store_list', metavar='LIST', # help='user groups allowed to connect to control socket') if HAVE_RESTRICTED: class RestrictedAction(argparse.Action): def __init__(self, option_strings, dest, const=True, help=None, **kwargs): super(RestrictedAction, self).__init__(option_strings, dest=argparse.SUPPRESS, nargs=0, const=const, help=help) def __call__(self, parser, namespace, values, option_string=None): namespace.verify_agents = self.const namespace.resource_monitor = self.const namespace.mobility = self.const restrict = parser.add_argument_group('restricted options') restrict.add_argument( '--restricted', action=RestrictedAction, inverse='--no-restricted', help='shortcut to enable all restricted features') restrict.add_argument('--no-restricted', action=RestrictedAction, const=False, help=argparse.SUPPRESS) restrict.add_argument('--verify', action='store_true', inverse='--no-verify', help='verify agent integrity before execution') restrict.add_argument('--no-verify', action='store_false', dest='verify_agents', help=argparse.SUPPRESS) restrict.add_argument('--resource-monitor', action='store_true', inverse='--no-resource-monitor', help='enable agent resource management') restrict.add_argument('--no-resource-monitor', action='store_false', dest='resource_monitor', help=argparse.SUPPRESS) restrict.add_argument('--mobility', action='store_true', inverse='--no-mobility', help='enable agent mobility') restrict.add_argument('--no-mobility', action='store_false', dest='mobility', help=argparse.SUPPRESS) restrict.add_argument('--mobility-address', metavar='ADDRESS', help='specify the address on which to listen') restrict.add_argument('--mobility-port', type=int, metavar='NUMBER', help='specify the port on which to listen') vip_path = '$VOLTTRON_HOME/run/vip.socket' if sys.platform.startswith('linux'): vip_path = '@' + vip_path parser.set_defaults( log=None, log_config=None, verboseness=logging.WARNING, volttron_home=volttron_home, autostart=True, publish_address='ipc://$VOLTTRON_HOME/run/publish', subscribe_address='ipc://$VOLTTRON_HOME/run/subscribe', vip_address=['ipc://' + vip_path], #allow_root=False, #allow_users=None, #allow_groups=None, verify_agents=True, resource_monitor=True, mobility=True, mobility_address=None, mobility_port=2522) # Parse and expand options args = argv[1:] conf = os.path.join(volttron_home, 'config') if os.path.exists(conf) and 'SKIP_VOLTTRON_CONFIG' not in os.environ: args = ['--config', conf] + args logging.getLogger().setLevel(logging.NOTSET) opts = parser.parse_args(args) if opts.log: opts.log = config.expandall(opts.log) if opts.log_config: opts.log_config = config.expandall(opts.log_config) opts.publish_address = config.expandall(opts.publish_address) opts.subscribe_address = config.expandall(opts.subscribe_address) opts.vip_address = [config.expandall(addr) for addr in opts.vip_address] if HAVE_RESTRICTED: # Set mobility defaults if opts.mobility_address is None: info = socket.getaddrinfo(None, 0, 0, socket.SOCK_STREAM, 0, socket.AI_NUMERICHOST) family = info[0][0] if info else '' opts.mobility_address = '::' if family == socket.AF_INET6 else '' if getattr(opts, 'show_config', False): for name, value in sorted(vars(opts).iteritems()): print(name, repr(value)) return # Configure logging level = max(1, opts.verboseness) if opts.log is None: log_to_file(sys.stderr, level) elif opts.log == '-': log_to_file(sys.stdout, level) elif opts.log: log_to_file(opts.log, level, handler_class=handlers.WatchedFileHandler) else: log_to_file(None, 100, handler_class=lambda x: logging.NullHandler()) if opts.log_config: error = configure_logging(opts.log_config) if error: parser.error('{}: {}'.format(*error)) # Setup mobility server if HAVE_RESTRICTED and opts.mobility: ssh_dir = os.path.join(opts.volttron_home, 'ssh') try: priv_key = RSAKey(filename=os.path.join(ssh_dir, 'id_rsa')) authorized_keys = comms.load_authorized_keys( os.path.join(ssh_dir, 'authorized_keys')) except (OSError, IOError, PasswordRequiredException, SSHException) as exc: parser.error(exc) # Set configuration if HAVE_RESTRICTED: if opts.verify_agents: _log.info('Agent integrity verification enabled') if opts.resource_monitor: _log.info('Resource monitor enabled') opts.resmon = resmon.ResourceMonitor() opts.aip = aip.AIPplatform(opts) opts.aip.setup() if opts.autostart: for name, error in opts.aip.autostart(): _log.error('error starting {!r}: {}\n'.format(name, error)) # Main loops try: router = Router(opts.vip_address) exchange = gevent.spawn(agent_exchange, opts.publish_address, opts.subscribe_address) control = gevent.spawn( ControlService(opts.aip, vip_address='inproc://vip', vip_identity='control').run) pubsub = gevent.spawn( PubSubService(vip_address='inproc://vip', vip_identity='pubsub').run) if HAVE_RESTRICTED and opts.mobility: address = (opts.mobility_address, opts.mobility_port) mobility_in = comms_server.ThreadedServer(address, priv_key, authorized_keys, opts.aip) mobility_in.start() mobility_out = MobilityAgent( opts.aip, subscribe_address=opts.subscribe_address, publish_address=opts.publish_address) gevent.spawn(mobility_out.run) try: router.run() finally: control.kill() pubsub.kill() exchange.kill() finally: opts.aip.finish()