From 57a34f8e092ff7577c89d40b0e688cb9b4173a06 Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Tue, 24 Jun 2014 21:26:48 +0300 Subject: [PATCH] fully implemented smb network shares --- motioneye.py | 61 ++++++++++++++++++++++++++------------------- settings_default.py | 7 ++++-- src/config.py | 27 ++++++++++++++++++-- src/handlers.py | 10 +++++++- src/smbctl.py | 53 ++++++++++++++++++++++++++++++++++++--- templates/main.html | 10 ++++---- 6 files changed, 130 insertions(+), 38 deletions(-) diff --git a/motioneye.py b/motioneye.py index d42d881..94906a2 100755 --- a/motioneye.py +++ b/motioneye.py @@ -29,6 +29,8 @@ import settings sys.path.append(os.path.join(getattr(settings, 'PROJECT_PATH', os.path.dirname(sys.argv[0])), 'src')) +import smbctl + VERSION = '0.12' @@ -47,7 +49,8 @@ def _configure_settings(): set_default_setting('LOG_LEVEL', logging.INFO) set_default_setting('LISTEN', '0.0.0.0') set_default_setting('PORT', 8765) - set_default_setting('SYS_SETTINGS', False) + set_default_setting('SMB_SHARES', False) + set_default_setting('MOUNT_CHECK_INTERVAL', 300) set_default_setting('MOTION_CHECK_INTERVAL', 10) set_default_setting('CLEANUP_INTERVAL', 43200) set_default_setting('THUMBNAILER_INTERVAL', 60) @@ -122,10 +125,11 @@ def _configure_settings(): def _test_requirements(): - if settings.SYS_SETTINGS and os.geteuid() != 0: - print('SYS_SETTINGS require root privileges') - return False - + if os.geteuid() != 0: + if settings.SMB_SHARES: + print('SMB_SHARES require root privileges') + return False + try: import tornado # @UnusedImport tornado = True @@ -156,7 +160,6 @@ def _test_requirements(): import v4l2ctl v4lutils = v4l2ctl.find_v4l2_ctl() is not None - import smbctl mount_cifs = smbctl.find_mount_cifs() is not None ok = True @@ -184,7 +187,7 @@ def _test_requirements(): print('please install v4l-utils') ok = False - if settings.SYS_SETTINGS and not mount_cifs: + if settings.SMB_SHARES and not mount_cifs: print('please install cifs-utils') ok = False @@ -193,9 +196,6 @@ def _test_requirements(): def _configure_signals(): def bye_handler(signal, frame): - import cleanup - import motionctl - import thumbnailer import tornado.ioloop logging.info('interrupt signal received, shutting down...') @@ -203,19 +203,6 @@ def _configure_signals(): # shut down the IO loop if it has been started ioloop = tornado.ioloop.IOLoop.instance() ioloop.stop() - logging.info('server stopped') - - if thumbnailer.running(): - thumbnailer.stop() - logging.info('thumbnailer stopped') - - if cleanup.running(): - cleanup.stop() - logging.info('cleanup stopped') - - if motionctl.running(): - motionctl.stop() - logging.info('motion stopped') def child_handler(signal, frame): # this is required for the multiprocessing mechanism to work @@ -256,7 +243,10 @@ def _print_help(): print('') -def _start_server(): +def _run_server(): + import cleanup + import motionctl + import thumbnailer import tornado.ioloop import server @@ -265,6 +255,24 @@ def _start_server(): tornado.ioloop.IOLoop.instance().start() + logging.info('server stopped') + + if thumbnailer.running(): + thumbnailer.stop() + logging.info('thumbnailer stopped') + + if cleanup.running(): + cleanup.stop() + logging.info('cleanup stopped') + + if motionctl.running(): + motionctl.stop() + logging.info('motion stopped') + + if settings.SMB_SHARES: + smbctl.umount_all() + logging.info('SMB shares unmounted') + def _start_motion(): import tornado.ioloop @@ -314,10 +322,13 @@ if __name__ == '__main__': _configure_signals() _configure_logging() + if settings.SMB_SHARES: + smbctl.update_mounts() + _start_motion() _start_cleanup() if settings.THUMBNAILER_INTERVAL: _start_thumbnailer() - _start_server() + _run_server() diff --git a/settings_default.py b/settings_default.py index c8e1ce0..831ea31 100644 --- a/settings_default.py +++ b/settings_default.py @@ -29,8 +29,11 @@ LISTEN = '0.0.0.0' # change the port according to your requirements/restrictions PORT = 8765 -# enable system settings that require root (SMB shares, WiFI setup) -SYS_SETTINGS = False +# enable SMB shares (requires root) +SMB_SHARES = False + +# interval in seconds at which motionEye checks the SMB mounts +MOUNT_CHECK_INTERVAL = 300 # interval in seconds at which motionEye checks if motion is running MOTION_CHECK_INTERVAL = 10 diff --git a/src/config.py b/src/config.py index a5fa4cd..934a77e 100644 --- a/src/config.py +++ b/src/config.py @@ -187,6 +187,29 @@ def has_enabled_cameras(): return bool([c for c in cameras if c['@enabled'] and c['@proto'] == 'v4l2']) +def get_network_shares(): + if not get_main().get('@enabled'): + return [] + + camera_ids = get_camera_ids() + cameras = [get_camera(camera_id) for camera_id in camera_ids] + + mounts = [] + for camera in cameras: + if camera['@storage_device'] != 'network-share': + continue + + mounts.append({ + 'server': camera['@network_server'], + 'share': camera['@network_share_name'], + 'username': camera['@network_username'], + 'password': camera['@network_password'], + + }) + + return mounts + + def get_camera(camera_id, as_lines=False): global _camera_config_cache @@ -560,7 +583,7 @@ def camera_ui_to_dict(ui): else: data['hue'] = max(1, int(round(int(ui['hue']) * 2.55))) - if ui['storage_device'] == 'network-share': + if (ui['storage_device'] == 'network-share') and settings.SMB_SHARES: mount_point = smbctl.make_mount_point(ui['network_server'], ui['network_share_name'], ui['network_username']) if ui['root_directory'].startswith('/'): ui['root_directory'] = ui['root_directory'][1:] @@ -754,7 +777,7 @@ def camera_dict_to_ui(data): else: ui['hue'] = 50 - if data['@storage_device'] == 'network-share': + if (data['@storage_device'] == 'network-share') and settings.SMB_SHARES: mount_point = smbctl.make_mount_point(data['@network_server'], data['@network_share_name'], data['@network_username']) ui['root_directory'] = data['target_dir'][len(mount_point):] diff --git a/src/handlers.py b/src/handlers.py index c82252d..b94062f 100644 --- a/src/handlers.py +++ b/src/handlers.py @@ -27,6 +27,7 @@ import mediafiles import motionctl import remote import settings +import smbctl import template import update import utils @@ -141,7 +142,7 @@ class NotFoundHandler(BaseHandler): class MainHandler(BaseHandler): @BaseHandler.auth() def get(self): - self.render('main.html', sys_settings=getattr(settings, 'SYS_SETTINGS', False)) + self.render('main.html', smb_shares=settings.SMB_SHARES) class ConfigHandler(BaseHandler): @@ -292,6 +293,10 @@ class ConfigHandler(BaseHandler): def finish(): if restart[0]: + if settings.SMB_SHARES: + logging.debug('updating SMB mounts') + smbctl.update_mounts() + logging.debug('motion needs to be restarted') motionctl.restart() @@ -495,6 +500,9 @@ class ConfigHandler(BaseHandler): camera_config['@id'] = camera_id if proto == 'v4l2': + if settings.SMB_SHARES: + smbctl.update_mounts() + motionctl.restart() ui_config = config.camera_dict_to_ui(camera_config) diff --git a/src/smbctl.py b/src/smbctl.py index 009f69f..061824f 100644 --- a/src/smbctl.py +++ b/src/smbctl.py @@ -15,12 +15,18 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import datetime import logging import os import re import subprocess import time +import config +import settings + +from tornado import ioloop + def find_mount_cifs(): try: @@ -56,7 +62,6 @@ def list_mounts(): line = line.strip() if not line: continue - parts = line.split() if len(parts) < 4: continue @@ -104,8 +109,10 @@ def mount(server, share, username, password): mount_point = make_mount_point(server, share, username) logging.debug('mounting "//%s/%s" at "%s"' % (server, share, mount_point)) - logging.debug('making sure mount point "%s" exists' % mount_point) - os.makedirs(mount_point) + logging.debug('making sure mount point "%s" exists' % mount_point) + + if not os.path.exists(mount_point): + os.makedirs(mount_point) if username: opts = 'username=%s,password=%s' % (username, password) @@ -149,3 +156,43 @@ def umount(server, share, username): logging.error('failed to unmount smb share "//%s/%s" from "%s"' % (server, share, mount_point)) return False + + +def update_mounts(): + network_shares = config.get_network_shares() + + mounts = list_mounts() + mounts = dict(((m['server'], m['share'], m['username'] or ''), False) for m in mounts) + + for network_share in network_shares: + key = (network_share['server'], network_share['share'], network_share['username'] or '') + if key in mounts: # found + mounts[key] = True + + else: # needs to be mounted + mount(network_share['server'], network_share['share'], network_share['username'], network_share['password']) + + # unmount the no longer necessary mounts + for (network_share['server'], network_share['share'], network_share['username']), required in mounts.items(): + if not required: + umount(network_share['server'], network_share['share'], network_share['username']) + + +def umount_all(): + for mount in list_mounts(): + umount(mount['server'], mount['share'], mount['username']) + + +def _check_mounts(): + logging.debug('checking SMB mounts...') + + update_mounts() + + io_loop = ioloop.IOLoop.instance() + io_loop.add_timeout(datetime.timedelta(seconds=settings.MOUNT_CHECK_INTERVAL), _check_mounts) + + +if settings.SMB_SHARES: + # schedule the mount checker + io_loop = ioloop.IOLoop.instance() + io_loop.add_timeout(datetime.timedelta(seconds=settings.MOUNT_CHECK_INTERVAL), _check_mounts) diff --git a/templates/main.html b/templates/main.html index 85e2a92..c45e59d 100644 --- a/templates/main.html +++ b/templates/main.html @@ -152,7 +152,7 @@
File Storage
- + - + - + - + - + -- 2.39.5
Storage Device ?
Network Server ?
Share Name ?
Share Username ?
Share Password ?