From 8b8fd0e09ef83fc7bbec219939362d1d5d3d9781 Mon Sep 17 00:00:00 2001 From: Calin Crisan Date: Sat, 5 Jul 2014 18:59:07 +0300 Subject: [PATCH] added diskctl.py to list available mounted disks --- src/config.py | 6 +- src/diskctl.py | 154 ++++++++++++++++++++++++++++++++++++++++++++++ src/mjpgclient.py | 3 +- 3 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 src/diskctl.py diff --git a/src/config.py b/src/config.py index 8faabf6..c1aff34 100644 --- a/src/config.py +++ b/src/config.py @@ -22,6 +22,7 @@ import re from collections import OrderedDict +import diskctl import motionctl import settings import smbctl @@ -282,7 +283,7 @@ def get_camera(camera_id, as_lines=False): camera_config['stream_maxrate'] = camera_config.pop('webcam_maxrate') if 'webcam_localhost' in camera_config: camera_config['stream_localhost'] = camera_config.pop('webcam_localhost') - + _set_default_motion_camera(camera_id, camera_config, False) _camera_config_cache[camera_id] = dict(camera_config) @@ -704,7 +705,8 @@ def camera_dict_to_ui(data): 'network_password': data['@network_password'], 'disk_used': disk_used, 'disk_total': disk_total, - + 'available_disks': diskctl.list_mounted_disks(), + # text overlay 'text_overlay': False, 'left_text': 'camera-name', diff --git a/src/diskctl.py b/src/diskctl.py new file mode 100644 index 0000000..92a64f0 --- /dev/null +++ b/src/diskctl.py @@ -0,0 +1,154 @@ + +# 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 . + +import logging +import os +import re + + +def _list_mounts(): + logging.debug('listing 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 == 'fuseblk': + fstype = 'ntfs' # most likely' + + logging.debug('found mount "%s" at "%s"' % (target, mount_point)) + + mounts.append({ + 'target': target, + 'mount_point': mount_point, + 'fstype': fstype, + 'opts': opts, + }) + + return mounts + + +def _list_disks(): + logging.debug('listing disks...') + + disks_by_dev = {} + partitions_by_dev = {} + + for entry in os.listdir('/dev/disk/by-id/'): + parts = entry.split('-', 1) + if len(parts) < 2: + continue + + target = os.path.realpath(os.path.join('/dev/disk/by-id/', entry)) + + bus, entry = parts + m = re.search('-part(\d+)$', entry) + if m: + part_no = int(m.group(1)) + entry = re.sub('-part\d+$', '', entry) + + else: + part_no = None + + parts = entry.split('_') + if len(parts) < 2: + vendor = parts[0] + model = '' + + else: + vendor, model = parts[:2] + + if part_no is not None: + logging.debug('found partition "%s" at "%s" on bus "%s": "%s %s"' % (part_no, target, bus, vendor, model)) + + partitions_by_dev[target] = { + 'target': target, + 'bus': bus, + 'vendor': vendor, + 'model': model, + 'part_no': part_no, + 'unmatched': True + } + + else: + logging.debug('found disk at "%s" on bus "%s": "%s %s"' % (target, bus, vendor, model)) + + disks_by_dev[target] = { + 'target': target, + 'bus': bus, + 'vendor': vendor, + 'model': model, + 'partitions': [] + } + + # group partitions by disk + for dev, partition in partitions_by_dev.items(): + for disk_dev, disk in disks_by_dev.items(): + if dev.startswith(disk_dev): + disk['partitions'].append(partition) + partition.pop('unmatched') + + # add separate partitions that did not match any disk + for partition in partitions_by_dev.values(): + if partition.pop('unmatched', False): + disks_by_dev[partition['target']] = partition + partition['partitions'] = [dict(partition)] + + # prepare flat list of disks + disks = disks_by_dev.values() + disks.sort(key=lambda d: d['vendor']) + + for disk in disks: + disk['partitions'].sort(key=lambda p: p['part_no']) + + return disks + + +def list_mounted_disks(): + mounted_disks = [] + + try: + disks = _list_disks() + mounts_by_target = dict((m['target'], m) for m in _list_mounts()) + + for disk in disks: + for partition in disk['partitions']: + mount = mounts_by_target.get(partition['target']) + if mount: + partition.update(mount) + + # filter out unmounted partitions + disk['partitions'] = [p for p in disk['partitions'] if p.get('mount_point')] + + # filter out unmounted disks + mounted_disks = [d for d in disks if d['partitions']] + + except Exception as e: + logging.error('failed to list mounted disks: %s' % e, exc_info=True) + + return mounted_disks diff --git a/src/mjpgclient.py b/src/mjpgclient.py index 6c8efe0..7aad8cd 100644 --- a/src/mjpgclient.py +++ b/src/mjpgclient.py @@ -149,7 +149,8 @@ def _garbage_collector(): logging.debug('mjpg client timed out receiving data for camera %(camera_id)s on port %(port)s' % { 'camera_id': camera_id, 'port': port}) - motionctl.restart() # this will close all the mjpg clients + motionctl.stop() # this will close all the mjpg clients + motionctl.start() # this will close all the mjpg clients break -- 2.39.5