]> www.vanbest.org Git - motioneye-debian/commitdiff
added smbctl
authorCalin Crisan <ccrisan@gmail.com>
Mon, 23 Jun 2014 18:17:24 +0000 (21:17 +0300)
committerCalin Crisan <ccrisan@gmail.com>
Mon, 23 Jun 2014 18:17:24 +0000 (21:17 +0300)
motioneye.py
settings_default.py
src/handlers.py
src/smbctl.py [new file with mode: 0644]
static/css/main.css
templates/main.html

index c5eee785006cd6ecc7de1ac183081e8a6521276b..d42d881accec226b9e62bf87a894f56fac2419d8 100755 (executable)
@@ -47,6 +47,7 @@ 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('MOTION_CHECK_INTERVAL', 10)
     set_default_setting('CLEANUP_INTERVAL', 43200)
     set_default_setting('THUMBNAILER_INTERVAL', 60)
@@ -121,6 +122,9 @@ def _configure_settings():
 
 
 def _test_requirements():
+    if settings.SYS_SETTINGS and os.geteuid() != 0:
+        print('SYS_SETTINGS require root privileges')
+        return False
     
     try:
         import tornado  # @UnusedImport
@@ -152,6 +156,9 @@ 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
     if not tornado:
         print('please install tornado (python-tornado)')
@@ -177,6 +184,10 @@ def _test_requirements():
         print('please install v4l-utils')
         ok = False
 
+    if settings.SYS_SETTINGS and not mount_cifs:
+        print('please install cifs-utils')
+        ok = False
+
     return ok
 
         
index c753921ab334ced94c37307382dcf1c623d3aa7f..c8e1ce0cdf5b3bcd0a27f6969b6af4f1ee82e141 100644 (file)
@@ -29,6 +29,9 @@ 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
+
 # interval in seconds at which motionEye checks if motion is running
 MOTION_CHECK_INTERVAL = 10
 
index f4548629eb9644cd72159452aba5bb0a1a565c5c..ffa4f3b85ea386136f57fb9d40ca42b8aa97ad77 100644 (file)
@@ -143,7 +143,7 @@ class NotFoundHandler(BaseHandler):
 class MainHandler(BaseHandler):
     @BaseHandler.auth()
     def get(self):
-        self.render('main.html')
+        self.render('main.html', sys_settings=getattr(settings, 'SYS_SETTINGS', False))
 
 
 class ConfigHandler(BaseHandler):
diff --git a/src/smbctl.py b/src/smbctl.py
new file mode 100644 (file)
index 0000000..166c427
--- /dev/null
@@ -0,0 +1,135 @@
+
+# Copyright (c) 2013 Calin Crisan
+# This file is part of motionEye.
+#
+# motionEye is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# 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 logging
+import os
+import re
+import subprocess
+
+
+def find_mount_cifs():
+    try:
+        return subprocess.check_output('which mount.cifs', shell=True).strip()
+    
+    except subprocess.CalledProcessError: # not found
+        return None
+
+
+def _make_mount_point(server, share, username):
+    server = re.sub('[^a-zA-Z0-9]', '_', server).lower()
+    share = re.sub('[^a-zA-Z0-9]', '_', share).lower()
+    
+    if username:
+        mount_point = '/media/motioneye_%s_%s_%s' % (server, share, username) 
+    
+    else:
+        mount_point = '/media/motioneye_%s_%s' % (server, share)
+
+    logging.debug('making sure mount point "%s" exists' % mount_point)    
+    os.makedirs(mount_point)
+    
+    return mount_point
+
+
+def list_mounts():
+    logging.debug('listing smb mounts...')
+    
+    mounts = []
+    with open('/proc/mounts', 'r') as f:
+        for line in f:
+            line = line.strip()
+            if not line:
+                continue
+            
+            parts = line.split()
+            if len(parts) < 4:
+                continue
+            
+            target = parts[0]
+            mount_point = parts[1]
+            fstype = parts[2]
+            opts = parts[3]
+            
+            if fstype != 'cifs':
+                continue
+            
+            match = re.match('//([^/]+)/(.+)', target)
+            if not match:
+                continue
+            
+            if len(match.groups()) != 2:
+                continue
+            
+            server, share = match.groups()
+            
+            match = re.search('username=(\w+)', opts)
+            if match:
+                username = match.group(1)
+            
+            else:
+                username = None
+            
+            logging.debug('found smb mount "//%s/%s" at "%s"' % (server, share, mount_point))
+            
+            mounts.append({
+                'server': server,
+                'share': share,
+                'username': username,
+                'mount_point': mount_point
+            })
+            
+    return mounts
+
+
+def is_motioneye_mount(mount_point):
+    return bool(re.match('^/media/motioneye_\w+$', mount_point))
+
+
+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))
+    
+    if username:
+        opts = 'username=%s,password=%s' % (username, password)
+        
+    else:
+        opts = 'guest'
+
+    try:
+        subprocess.check_call('mount.cifs //%s/%s %s -o %s' % (server, share, mount_point, opts), shell=True)
+        
+        return mount_point
+
+    except subprocess.CalledProcessError:
+        logging.error('failed to mount smb share "//%s/%s" at "%s"' % (server, share, mount_point))
+        
+        return False
+
+
+def umount(server, share, username):
+    mount_point = _make_mount_point(server, share, username)
+    logging.debug('unmounting "//%s/%s" from "%s"' % (server, share, mount_point))
+    
+    try:
+        subprocess.check_call('umount %s' % mount_point, shell=True)
+        
+        return True
+
+    except subprocess.CalledProcessError:
+        logging.error('failed to unmount smb share "//%s/%s" from "%s"' % (server, share, mount_point))
+        
+        return False
index d541c61641088ffe29d3cfdce236e1afcedc3dc4..fd89cf9487360271a5fa14b84e32985d895a1719 100644 (file)
@@ -395,8 +395,8 @@ span.disk-usage-percent {
     position: relative;
 }
 
-div.rpi,
-tr.rpi {
+div.hidden,
+tr.hidden {
     display: none;
 }
 
index 41a5e530662db2e03364fd8e38333fb9bde68684..85e2a92cc22781a09297243b05d4fddceb713ce3 100644 (file)
                 
                 <div class="settings-section-title advanced-setting">File Storage</div>
                 <table class="settings advanced-setting">
-                    <tr class="settings-item advanced-setting rpi">
+                    <tr class="settings-item advanced-setting {% if not sys_settings %}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 rpi">
+                    <tr class="settings-item advanced-setting {% if not sys_settings %}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 rpi">
+                    <tr class="settings-item advanced-setting {% if not sys_settings %}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 rpi">
+                    <tr class="settings-item advanced-setting {% if not sys_settings %}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 rpi">
+                    <tr class="settings-item advanced-setting {% if not sys_settings %}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>
                     </tr>
                 </table>
                 
-                <div class="settings-section-title rpi"><input type="checkbox" class="styled section notifications" id="motionNotificationsSwitch">Motion Notifications</div>
+                <div class="settings-section-title hidden"><input type="checkbox" class="styled section notifications" id="motionNotificationsSwitch">Motion Notifications</div>
                 <table class="settings">
-                    <tr class="settings-item rpi">
+                    <tr class="settings-item hidden">
                         <td class="settings-item-label"><span class="settings-item-label">Email Addresses</span></td>
                         <td class="settings-item-value"><input type="text" class="styled notifications" id="emailAddressesEntry" placeholder="email addresses..."></td>
                         <td><span class="help-mark" title="email addresses (separated by comma) that are added here will receive notifications whenever a motion event is detected (leave empty to disable email notifications)">?</span></td>
                     </tr>
                 </table>
 
-                <div class="settings-section-title rpi"><input type="checkbox" class="styled section working-schedule" id="workingScheduleSwitch">Working Schedule</div>
-                <table class="settings rpi">
+                <div class="settings-section-title hidden"><input type="checkbox" class="styled section working-schedule" id="workingScheduleSwitch">Working Schedule</div>
+                <table class="settings hidden">
                     <tr class="settings-item">
                         <td class="settings-item-label"><span class="settings-item-label">Monday</span></td>
                         <td class="settings-item-value">