diff --git a/actions/udiskie b/actions/udiskie new file mode 100755 index 000000000..2b249a163 --- /dev/null +++ b/actions/udiskie @@ -0,0 +1,60 @@ +#!/usr/bin/python3 +# -*- mode: python -*- +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +""" +Configuration helper for udiskie. +""" + +import argparse + +from plinth import action_utils + + +def parse_arguments(): + """Return parsed command line arguments as dictionary.""" + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') + + subparsers.add_parser('enable', help='Enable udiskie') + subparsers.add_parser('disable', help='Disable udiskie') + + subparsers.required = True + return parser.parse_args() + + +def subcommand_enable(_): + """Enable web configuration and reload.""" + action_utils.service_enable('freedombox-udiskie') + + +def subcommand_disable(_): + """Disable web configuration and reload.""" + action_utils.service_disable('freedombox-udiskie') + + +def main(): + """Parse arguments and perform all duties.""" + arguments = parse_arguments() + + subcommand = arguments.subcommand.replace('-', '_') + subcommand_method = globals()['subcommand_' + subcommand] + subcommand_method(arguments) + + +if __name__ == '__main__': + main() diff --git a/data/etc/plinth/modules-enabled/udiskie b/data/etc/plinth/modules-enabled/udiskie new file mode 100644 index 000000000..bb489cbd2 --- /dev/null +++ b/data/etc/plinth/modules-enabled/udiskie @@ -0,0 +1 @@ +plinth.modules.udiskie diff --git a/data/lib/systemd/system/freedombox-udiskie.service b/data/lib/systemd/system/freedombox-udiskie.service new file mode 100644 index 000000000..a905d999c --- /dev/null +++ b/data/lib/systemd/system/freedombox-udiskie.service @@ -0,0 +1,26 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +[Unit] +Description=handle automounting +Documentation=man:udiskie(1) + +[Service] +ExecStart=/usr/bin/udiskie + +[Install] +WantedBy=multi-user.target diff --git a/debian/control b/debian/control index ec78660dd..c6128f01c 100644 --- a/debian/control +++ b/debian/control @@ -25,6 +25,7 @@ Build-Depends: debhelper (>= 11~) , python3-cherrypy3 , python3-configobj , python3-coverage + , python3-dbus , python3-django (>= 1.11) , python3-django-axes (>= 3.0.3) , python3-django-captcha @@ -69,6 +70,7 @@ Depends: ${python3:Depends} , python3-bootstrapform , python3-cherrypy3 , python3-configobj + , python3-dbus , python3-django (>= 1.11) , python3-django-axes (>= 3.0.3) , python3-django-captcha diff --git a/plinth/modules/udiskie/__init__.py b/plinth/modules/udiskie/__init__.py new file mode 100644 index 000000000..70e41ddf5 --- /dev/null +++ b/plinth/modules/udiskie/__init__.py @@ -0,0 +1,147 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +""" +FreedomBox app for udiskie. +""" + +import dbus +from django.utils.translation import ugettext_lazy as _ + +from plinth import service as service_module +from plinth import action_utils, actions +from plinth.menu import main_menu +from plinth.modules.storage import format_bytes + +version = 1 + +managed_services = ['freedombox-udiskie'] + +managed_packages = ['udiskie'] + +name = _('udiskie') + +short_description = _('Removable Media') + +description = [ + _('udiskie allows automatic mounting of removable media, such as flash ' + 'drives.'), +] + +service = None + + +def init(): + """Intialize the module.""" + menu = main_menu.get('system') + menu.add_urlname(name, 'glyphicon-floppy-disk', 'udiskie:index', + short_description) + + global service + setup_helper = globals()['setup_helper'] + if setup_helper.get_state() != 'needs-setup': + service = service_module.Service( + managed_services[0], name, ports=[], is_external=False, + is_enabled=is_enabled, enable=enable, disable=disable, + is_running=is_running) + + +def setup(helper, old_version=None): + """Install and configure the module.""" + helper.install(managed_packages, skip_recommends=True) + helper.call('post', actions.superuser_run, 'udiskie', ['enable']) + global service + if service is None: + service = service_module.Service( + managed_services[0], name, ports=[], is_external=True, + is_enabled=is_enabled, enable=enable, disable=disable, + is_running=is_running) + helper.call('post', service.notify_enabled, None, True) + + +def is_running(): + """Return whether the service is running.""" + return action_utils.service_is_running('freedombox-udiskie') + + +def is_enabled(): + """Return whether the module is enabled.""" + return action_utils.service_is_enabled('freedombox-udiskie') + + +def enable(): + """Enable the module.""" + actions.superuser_run('udiskie', ['enable']) + + +def disable(): + """Disable the module.""" + actions.superuser_run('udiskie', ['disable']) + + +def list_devices(): + UDISKS2 = 'org.freedesktop.UDisks2' + UDISKS2_PATH = '/org/freedesktop/UDisks2' + BLOCK = UDISKS2 + '.Block' + PROPERTIES = 'org.freedesktop.DBus.Properties' + + devices = [] + bus = dbus.SystemBus() + udisks_obj = bus.get_object(UDISKS2, UDISKS2_PATH) + manager = dbus.Interface(udisks_obj, 'org.freedesktop.DBus.ObjectManager') + for k, v in manager.GetManagedObjects().items(): + drive_info = v.get(BLOCK, {}) + if drive_info.get('IdUsage') == "filesystem" \ + and not drive_info.get('HintSystem') \ + and not drive_info.get('ReadOnly'): + device_name = drive_info.get('Device') + if device_name: + device_name = bytearray(device_name).replace( + b'\x00', b'').decode('utf-8') + short_name = device_name.replace('/dev', '', 1) + bd = bus.get_object( + UDISKS2, UDISKS2_PATH + '/block_devices%s' % short_name) + drive_name = bd.Get(BLOCK, 'Drive', dbus_interface=PROPERTIES) + drive = bus.get_object(UDISKS2, drive_name) + ejectable = drive.Get(UDISKS2 + '.Drive', 'Ejectable', + dbus_interface=PROPERTIES) + if ejectable: + label = bd.Get(BLOCK, 'IdLabel', dbus_interface=PROPERTIES) + size = bd.Get(BLOCK, 'Size', dbus_interface=PROPERTIES) + file_system = bd.Get(BLOCK, 'IdType', + dbus_interface=PROPERTIES) + try: + mount_points = bd.Get(UDISKS2 + '.Filesystem', + 'MountPoints', + dbus_interface=PROPERTIES) + mount_point = mount_points[0] + except: + mount_point = None + + devices.append({ + 'device': + device_name, + 'label': + str(label), + 'size': + format_bytes(size), + 'file_system': + str(file_system), + 'mount_point': + ''.join([chr(ch) for ch in mount_point]), + }) + + return devices diff --git a/plinth/modules/udiskie/templates/udiskie.html b/plinth/modules/udiskie/templates/udiskie.html new file mode 100644 index 000000000..fd3270bc8 --- /dev/null +++ b/plinth/modules/udiskie/templates/udiskie.html @@ -0,0 +1,98 @@ +{% extends "base.html" %} +{% comment %} +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +{% endcomment %} + +{% load bootstrap %} +{% load i18n %} +{% load static %} + +{% block content %} + +

{{ service.name }}

+ + {% for paragraph in description %} +

{{ paragraph|safe }}

+ {% endfor %} + + {% if manual_page %} +

+ + {% trans 'Learn more...' %} + +

+ {% endif %} + +

{% trans "Devices" %}

+
+
+ + + + + + + + + + + + {% for device in devices %} + + + + + + + + {% endfor %} + +
{% trans "Device" %}{% trans "Label" %}{% trans "Size" %}{% trans "File System" %}{% trans "Mount Point" %}
{{ device.device }}{{ device.label }}{{ device.size }}{{ device.file_system }}{{ device.mount_point }}
+
+
+ + {% if show_status_block %} +

{% trans "Status" %}

+

+ {% with service_name=service.name %} + {% if service.is_running %} + + {% blocktrans trimmed %} + Service {{ service_name }} is running. + {% endblocktrans %} + {% else %} + + {% blocktrans trimmed %} + Service {{ service_name }} is not running. + {% endblocktrans %} + {% endif %} + {% endwith %} +

+ {% endif %} + +

{% trans "Configuration" %}

+ +
+ {% csrf_token %} + + {{ form|bootstrap }} + + +
+{% endblock %} diff --git a/plinth/modules/udiskie/urls.py b/plinth/modules/udiskie/urls.py new file mode 100644 index 000000000..c1570fbe4 --- /dev/null +++ b/plinth/modules/udiskie/urls.py @@ -0,0 +1,31 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +""" +URLs for the udiskie module. +""" + +from django.conf.urls import url + +from .views import UdiskieView +from plinth.modules import udiskie + +urlpatterns = [ + url(r'^sys/udiskie/$', + UdiskieView.as_view(service_id=udiskie.managed_services[0], + description=udiskie.description, + show_status_block=True), name='index'), +] diff --git a/plinth/modules/udiskie/views.py b/plinth/modules/udiskie/views.py new file mode 100644 index 000000000..5d146a6e3 --- /dev/null +++ b/plinth/modules/udiskie/views.py @@ -0,0 +1,32 @@ +# +# This file is part of FreedomBox. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +""" +Views for udiskie module. +""" + +from plinth.modules import udiskie +from plinth.views import ServiceView + + +class UdiskieView(ServiceView): + template_name = 'udiskie.html' + + def get_context_data(self, **kwargs): + """Return the context data rendering the template.""" + context = super().get_context_data(**kwargs) + context['devices'] = udiskie.list_devices() + return context diff --git a/setup.py b/setup.py index 525fb1e37..e30d4c1e4 100755 --- a/setup.py +++ b/setup.py @@ -241,7 +241,7 @@ setuptools.setup( ['data/etc/NetworkManager/dispatcher.d/10-freedombox-batman']), ('/etc/sudoers.d', ['data/etc/sudoers.d/plinth']), ('/lib/systemd/system', - ['data/lib/systemd/system/plinth.service']), + glob.glob('data/lib/systemd/system/*.service')), ('/usr/share/plinth/actions', glob.glob(os.path.join('actions', '*'))), ('/usr/share/polkit-1/rules.d',