blob: 3175972717bd4ce79f046d3a7fcf052e96c82408 [file] [log] [blame]
#!/usr/bin/env python
# Copyright 2011 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# TR-069 has mandatory attribute names that don't comply with policy
# pylint:disable=invalid-name
#
"""The main server process for our TR-069 CPE device."""
__author__ = 'apenwarr@google.com (Avery Pennarun)'
# this needs to be first, before any tornado imports
import tr.epoll_fix # pylint:disable=g-bad-import-order
import errno
import os.path
import sys
import google3
import bup.options
import diagui.main
import dm_root
import factory
import tr.acs_config
import tr.api
import tr.cwmptypes
import tr.filenotifier
import tr.handle
import tr.http
import tr.mainloop
import tr.rcommand
optspec = """
cwmpd [options]
--
r,rcmd-port= TCP port to listen for rcommands on; 0 to disable [12999]
u,unix-path= Unix socket to listen on [/tmp/cwmpd.sock]
i,ip= IP address to report to ACS. (default=finds interface IP address)
l,listenip= IP address to listen on [::1]
p,port= TCP port to listen for TR-069 on [7547]
ping-path= Force CPE ping listener to this URL path (default=random)
acs-url= URL of the TR-069 ACS server to connect to
fake-acs Run a fake ACS (and auto-set --acs-url to that)
no-cpe Don't run a CPE (and thus never connect to ACS)
cpe-listener Let CPE listen for http requests (not TR-069 compliant)
platform= Activate the platform-specific device tree (see platform/ dir)
ext-dir= Activate any extension modules in this dir [%(ext_dir)s]
close-stdio Close stdout after listeners are running; exit when stdin closes
ca-certs= SSL ca_certificates.crt file to use
client-cert= SSL client certificate to use
client-key= SSL client private key to use
restrict-acs-hosts= Domain names allowed for ACS URL. Default=unrestricted. Example: 'google.com gfsvc.com'
diagui Listen for diagui requests on diagui-port
diagui-port= Port to listen on (0=random) [80]
techui Also handle TechUI requests on the diagui-port
licenseui Listen for license requests on the diagui-port
factory Listen for factory requests on factory-port
factory-port= Port to listen on (0=random) [8883]
"""
def _GotData(loop, fd, unused_flags):
if not os.read(fd, 1024):
loop.ioloop.stop()
def main():
try:
os.mkdir(tr.acs_config.CWMP_TMP_DIR)
except OSError, e:
if e.errno == errno.EEXIST:
pass
else:
raise
cwmpdir = os.path.dirname(os.path.abspath(sys.argv[0]))
defargs = dict(ext_dir=os.environ.get('CWMPD_EXT_DIR', cwmpdir + '/ext'))
o = bup.options.Options(optspec % defargs)
(opt, unused_flags, unused_extra) = o.parse(sys.argv[1:])
if not opt.platform:
o.fatal('You must specify a --platform')
loop = tr.mainloop.MainLoop()
root = dm_root.DeviceModelRoot(loop, opt.platform, opt.ext_dir)
root.add_cwmp_extensions()
handle = root.handle
notifier = tr.filenotifier.FileNotifier(loop)
tr.cwmptypes.SetFileBackedNotifier(notifier)
acs = cpe = None
if opt.fake_acs:
if not opt.port:
o.fatal("Can't use --fake-acs without --port")
acs = tr.api.ACS()
if not opt.acs_url:
opt.acs_url = 'http://localhost:%d/acs' % opt.port
if opt.cpe:
cpe = tr.api.CPE(handle)
if not opt.cpe_listener:
print 'CPE API is client mode only.'
if cpe:
# Arguments to pass to Tornado HTTPClient.fetch
fetch_args = {'user_agent': 'catawampus-tr69'}
if opt.ca_certs:
fetch_args['ca_certs'] = opt.ca_certs
fetch_args['validate_cert'] = True
if opt.client_cert and opt.client_key:
fetch_args['client_cert'] = opt.client_cert
fetch_args['client_key'] = opt.client_key
pc = root.get_platform_config(ioloop=loop.ioloop)
cpe.download_manager.SetDirectories(config_dir=pc.ConfigDir(),
download_dir=pc.DownloadDir())
cpe_machine = tr.http.Listen(ip=opt.ip, port=opt.port,
ping_path=opt.ping_path,
acs=acs, cpe=cpe,
restrict_acs_hosts=opt.restrict_acs_hosts,
cpe_listener=opt.cpe_listener,
acs_config=tr.acs_config.AcsConfig(),
acs_url=opt.acs_url,
fetch_args=fetch_args)
ms = cpe_machine.GetManagementServer()
root.add_management_server(ms)
root.configure_tr157(cpe_machine)
else:
cpe_machine = None
if opt.rcmd_port:
loop.ListenInet((opt.listenip, opt.rcmd_port),
tr.rcommand.MakeRemoteCommandStreamer(handle, cpe_machine))
if opt.unix_path:
loop.ListenUnix(opt.unix_path,
tr.rcommand.MakeRemoteCommandStreamer(handle, cpe_machine))
if opt.diagui or opt.licenseui:
inst = diagui.main.MainApplication(root, cpe_machine,
run_diagui=opt.diagui,
run_techui=opt.techui,
run_licenseui=opt.licenseui)
print 'Listening for diagui on port %d' % (opt.diagui_port,)
inst.listen(opt.diagui_port)
if opt.factory:
print 'Listening for factoryui on port %d' % (opt.factory_port,)
inst = factory.FactoryFactory()
inst.listen(opt.factory_port)
if cpe_machine:
cpe_machine.Startup()
if opt.close_stdio:
nullf = open('/dev/null', 'w+')
os.dup2(nullf.fileno(), 1)
nullf.close()
loop.ioloop.add_handler(sys.stdin.fileno(),
lambda *args: _GotData(loop, *args),
loop.ioloop.READ)
loop.Start()
if __name__ == '__main__':
sys.stdout.flush()
sys.stdout = os.fdopen(1, 'w', 1) # force line buffering even if redirected
sys.stderr.flush()
sys.stderr = os.fdopen(2, 'w', 1) # force line buffering even if redirected
print
main()