]> www.vanbest.org Git - motioneye-debian/commitdiff
fully implemented smb network shares
authorCalin Crisan <ccrisan@gmail.com>
Tue, 24 Jun 2014 18:26:48 +0000 (21:26 +0300)
committerCalin Crisan <ccrisan@gmail.com>
Tue, 24 Jun 2014 18:26:48 +0000 (21:26 +0300)
motioneye.py
settings_default.py
src/config.py
src/handlers.py
src/smbctl.py
templates/main.html

index d42d881accec226b9e62bf87a894f56fac2419d8..94906a2f8c46a833624aa881f221276eb4ee2b81 100755 (executable)
@@ -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()
index c8e1ce0cdf5b3bcd0a27f6969b6af4f1ee82e141..831ea31d0e5c52f1c64d3cb763c7a23f4f965e17 100644 (file)
@@ -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
index a5fa4cd49bf2c6546459c056505b7c871a8ca6a2..934a77e58333c07ee47f752537102b161babced8 100644 (file)
@@ -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):]
     
index c82252db72fe42f8410809853476489e0347a501..b94062ff830a5f842677508ae388f6ce6a11b4c2 100644 (file)
@@ -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)
index 009f69fea858ed072821304398525bcbe7fc0d20..061824f0eb1bf12c4e1325af8d0eefaf2e80d66e 100644 (file)
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 
+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)
index 85e2a92cc22781a09297243b05d4fddceb713ce3..c45e59d500d6b47be4d602cc47869153e67b2cd9 100644 (file)
                 
                 <div class="settings-section-title advanced-setting">File Storage</div>
                 <table class="settings advanced-setting">
-                    <tr class="settings-item advanced-setting {% if not sys_settings %}hidden{% endif %}">
+                    <tr class="settings-item advanced-setting {% if not smb_shares %}hidden{% endif %}">
                         <td class="settings-item-label"><span class="settings-item-label">Storage Device</span></td>
                         <td class="settings-item-value">
                             <select class="styled storage" id="storageDeviceSelect">
                         </td>
                         <td><span class="help-mark" title="indicates the storage device where the image and video files will be saved">?</span></td>
                     </tr>
-                    <tr class="settings-item advanced-setting {% if not sys_settings %}hidden{% endif %}">
+                    <tr class="settings-item advanced-setting {% if not smb_shares %}hidden{% endif %}">
                         <td class="settings-item-label"><span class="settings-item-label">Network Server</span></td>
                         <td class="settings-item-value"><input type="text" class="styled storage" id="networkServerEntry"></td>
                         <td><span class="help-mark" title="the address of the network server (IP address or hostname)">?</span></td>
                     </tr>
-                    <tr class="settings-item advanced-setting {% if not sys_settings %}hidden{% endif %}">
+                    <tr class="settings-item advanced-setting {% if not smb_shares %}hidden{% endif %}">
                         <td class="settings-item-label"><span class="settings-item-label">Share Name</span></td>
                         <td class="settings-item-value"><input type="text" class="styled storage" id="networkShareNameEntry"></td>
                         <td><span class="help-mark" title="the name of the network share">?</span></td>
                     </tr>
-                    <tr class="settings-item advanced-setting {% if not sys_settings %}hidden{% endif %}">
+                    <tr class="settings-item advanced-setting {% if not smb_shares %}hidden{% endif %}">
                         <td class="settings-item-label"><span class="settings-item-label">Share Username</span></td>
                         <td class="settings-item-value"><input type="text" class="styled storage" id="networkUsernameEntry"></td>
                         <td><span class="help-mark" title="the username to be supplied when accessing the network share (leave empty if no username is required)">?</span></td>
                     </tr>
-                    <tr class="settings-item advanced-setting {% if not sys_settings %}hidden{% endif %}">
+                    <tr class="settings-item advanced-setting {% if not smb_shares %}hidden{% endif %}">
                         <td class="settings-item-label"><span class="settings-item-label">Share Password</span></td>
                         <td class="settings-item-value"><input type="password" class="styled storage" id="networkPasswordEntry"></td>
                         <td><span class="help-mark" title="the password required by the network share (leave empty if no password is required)">?</span></td>