Xfce Forum

Sub domains
 

You are not logged in.

#1 2017-04-04 19:14:19

GNUser
Member
Registered: 2017-03-13
Posts: 8

[SOLVED] "Eject Volume" unmounts but doesn't eject external HDD

I have a usb drive that I use often. It is a regular HDD with moving parts, not a SSD. When I plug it in , it automatically mounts and a little icon shows up both on my desktop as well as in the side pane of Thunar, no problems. (I think the automount is made possible by a package called gvfs-backends, which is installed on my system.)

The problem is that when I right-click on the desktop icon and choose "Eject Volume", the device is unmounted but I can feel that its disk continues to spin, making me very nervous about unplugging it. In contrast, right-clicking on the device's icon in Thunar's side pane and choosing "Eject" both unmounts *and* spins down the device, making it clear to me that it is safe to unplug the drive.

Please, how do I make the desktop's "Eject Volume" command both unmount *and* spin down the device (similar to Thunar side pane's "Eject" command)?

P.S. It won't kill me to always open up an instance of Thunar when I'm done with my device so that I can choose "Eject" in the side pane, but doing it via the desktop icon would be much more convenient.

Last edited by GNUser (2017-04-07 23:00:57)

Offline

#2 2017-04-04 21:58:28

ozjd
Member
From: Hawkesbury NSW Australia
Registered: 2012-02-05
Posts: 560
Website

Re: [SOLVED] "Eject Volume" unmounts but doesn't eject external HDD

I recently filed a bug report against the places plugin which I believe is related, https://bugzilla.xfce.org/show_bug.cgi?id=13432. It is failure to power down an external drive from the plugin. It does power down in Thunar though.

Offline

#3 2017-04-04 23:14:53

MountainDewManiac
Member
From: Where Mr. Bankruptcy is Prez
Registered: 2013-03-24
Posts: 1,115

Re: [SOLVED] "Eject Volume" unmounts but doesn't eject external HDD

GNUser wrote:

right-clicking on the device's icon in Thunar's side pane and choosing "Eject" both unmounts *and* spins down the device, making it clear to me that it is safe to unplug the drive.

I cannot help with your issue, but wanted to thank you for the above. I was unaware that Thunar was capable of properly(? one assumes ?) shutting down externally-mounted drives/devices. Thanks!

Regards,
MDM


Mountain Dew Maniac

How to Ask for Help <=== Click on this link

Offline

#4 2017-04-05 00:06:11

GNUser
Member
Registered: 2017-03-13
Posts: 8

Re: [SOLVED] "Eject Volume" unmounts but doesn't eject external HDD

Thank you for your help so far. I don't think xfce4-places-plugin is the culprit on my system, because I don't have that package installed. If I can at least find the package that contains the "Eject Volume" function/command, I think I'd be able to patch it so that it powers down the device after unmounting.

I'm on Devuan Jessie. These are all the packages installed on my system that contain the string "xfce" or "thunar" in their name:

gtk3-engines-xfce 3.0.1-2
libxfce4ui-1-0 4.10.0-6
libxfce4ui-utils 4.10.0-6
libxfce4util-common 4.10.1-2
libxfce4util6 4.10.1-2
xfce-keyboard-shortcuts 4.10.0-6
xfce4-appfinder 4.10.1-1
xfce4-battery-plugin 1.0.5-4
xfce4-mixer 4.10.0-3
xfce4-notifyd 0.2.4-3
xfce4-panel 4.10.1-1
xfce4-power-manager 1.4.1-1
xfce4-power-manager-data 1.4.1-1
xfce4-screenshooter 1.8.1-5
xfce4-session 4.10.1-10+devuan1
xfce4-settings 4.10.1-2
xfce4-taskmanager 1.0.1-1
xfce4-terminal 0.6.3-1+b1
libthunarx-2-0 1.6.3-2
thunar 1.6.3-2
thunar-archive-plugin 0.3.1-3
thunar-data 1.6.3-2
thunar-media-tags-plugin 0.2.1-1
thunar-volman 0.8.0-4

I already searched the source code of thunar, thunar-archive-plugin, and thunar-volman for the string "Eject Volume" and didn't get any hits.

Does anybody know which package in Devuan Jessie (or Debian Jessie) contains the "Eject Volume" function/command?

Last edited by GNUser (2017-04-05 00:10:43)

Offline

#5 2017-04-05 02:25:28

ToZ
Administrator
From: Canada
Registered: 2011-06-02
Posts: 10,948

Re: [SOLVED] "Eject Volume" unmounts but doesn't eject external HDD

If the issue is on the desktop, then it won't be in thunar's code, it would be in xfdesktop's code. You didn't list xfdesktop in your list of packages, but based on what's listed there, I'll assume that it's also a 4.10.x package version. The source file that adds the "eject" option is xfdesktop-volume-icon.c and the code to do the mounting/unmounting/ejecting is in the same file.


Please remember to mark your thread [SOLVED] to make it easier for others to find
--- How To Ask For Help | FAQ | Developer Wiki  |  Community | Contribute ---

Offline

#6 2017-04-05 12:30:20

GNUser
Member
Registered: 2017-03-13
Posts: 8

Re: [SOLVED] "Eject Volume" unmounts but doesn't eject external HDD

Thank you, ToZ. I'm migrating from MATE in which the file manager takes care of desktop functions. Interesting that in XFCE the desktop functions are completely separate from the file manager.

Well, you made this too easy! smile There are only two packages on my system with "xfdesktop" in the name:

xfdesktop4 4.10.2-3
xfdesktop4-data 4.10.2-3

Turns out that xfdesktop-volume-icon.c is part of the xfdesktop4 package. I'm going to hack on this baby until it does what I want--if it's the last thing I do! I know very little C so it will be ugly, but there will be a patch. I'll post it here once I get it working.

Offline

#7 2017-04-05 19:19:11

GNUser
Member
Registered: 2017-03-13
Posts: 8

Re: [SOLVED] "Eject Volume" unmounts but doesn't eject external HDD

Here's a workaround until I work out a proper patch. Map this script to a keyboard shortcut to unmount+eject the connected external device (if more than one is connected, a menu pops up).

#!/usr/bin/python3
# Dependencies: $ sudo apt-get install python3 pip3 python3-tk && sudo pip3 install notify2 easygui
# Purpose: Running this script ejects the mounted usb device. If there's more than one, user gets a menu.
# Terms:
#   media_dir is where external devices are mounted. on debian/devuan, that directory is /media/$USER/
#   label example: PATRIOT   
#   media_path example: /media/bruno/PATRIOT
#   dev_path example: /dev/sdb1
#   target is the device to eject. it should be a dev_path string 

import os
import getpass
import notify2
import easygui

def notify(message):
    """Displays a notification. String argument is message to be displayed."""
    notify2.init('Device Ejector')
    notify2.Notification('Device Ejector', message, 'dialog-warning').show()
    
def get_dev_path_from_label(label):
    """Returns dev_path from mounted volume labels."""
    media_path = media_dir + label
    dev_path = os.popen("df %s | tail -1 | awk '{ print $1 }'" % (media_path)).read().strip()
    return dev_path
    
def get_user_choice(devices):
    """Build menu from dictionary, return chosen dev_path."""
    menu_choices = []
    for label, dev_path in devices.items():
        menu_choices.append(label + ': ' + dev_path)
    choice = easygui.choicebox('Choose a device to eject:', 'Device Ejector', menu_choices)
    chosen_dev_path = choice.split(': ')[1]
    return chosen_dev_path
    
def unmount_and_eject(target):
    """Unmount and eject the dev_path argument."""
    exit_code = os.system('udisksctl unmount -b %s && udisksctl power-off -b %s' % (target, target))
    if exit_code != 0:
        notify("Couldn't eject device. Target busy?")
        
media_dir = '/media/%s/' % getpass.getuser()
labels = os.listdir(media_dir)
n_labels = len(labels)
if n_labels > 0:
    devices = {}    # let's build a dictionary of 'label': 'dev_path' items.
                    # example: devices = {'PATRIOT': '/dev/sdb1', 'SEAGULL': '/dev/sdc1'}
    for label in labels:
        devices[label] = get_dev_path_from_label(label)

if n_labels == 0:
    notify("Nothing to eject")
elif n_labels == 1:
    target = devices[labels[0]] # or list(devices.values())[0] 
    unmount_and_eject(target)
elif n_labels > 1:
    target = get_user_choice(devices)
    unmount_and_eject(target)

EDIT (5/6/17): I re-wrote the script in Python. It's much easier to understand now.

Last edited by GNUser (2017-05-08 12:20:27)

Offline

#8 2017-04-07 22:59:16

GNUser
Member
Registered: 2017-03-13
Posts: 8

Re: [SOLVED] "Eject Volume" unmounts but doesn't eject external HDD

Here's my patch:

--- a/xfdesktop4-4.10.2/src/xfdesktop-volume-icon.c	2013-03-02 11:42:19.000000000 -0500
+++ b/xfdesktop4-4.10.2/src/xfdesktop-volume-icon.c	2017-04-14 13:48:25.000000000 -0400
@@ -20,6 +20,8 @@
  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+#include <stdlib.h>
+
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
@@ -540,6 +542,10 @@
 #endif
 
     g_object_unref(icon);
+
+	system("mount >/tmp/mounted-after");
+	system("comm -23 /tmp/mounted-before /tmp/mounted-after | cut -d ' ' -f 1 >/tmp/target");
+	system("udisksctl power-off -b $(cat /tmp/target)");
 }
 
 static void
@@ -643,6 +649,7 @@
 #endif
             /* TODO: GMountOperation could be used to show what processes
              *       are preventing an unmount. */
+			system("mount >/tmp/mounted-before");
             g_mount_unmount_with_operation(mount, G_MOUNT_UNMOUNT_NONE,
                                            NULL, NULL,
                                            xfdesktop_volume_icon_unmount_finish,

I found that after installing the patched xfdesktop4 I had to reboot my system in order for it to kick in.

EDIT (4/14/17): I edited the patch so that it no longer requires an external helper shell script.

Last edited by GNUser (2017-05-01 13:35:40)

Offline

#9 2017-05-08 13:08:21

GNUser
Member
Registered: 2017-03-13
Posts: 8

Re: [SOLVED] "Eject Volume" unmounts but doesn't eject external HDD

Here's the same Python script as in #7 above, but using objects instead of a dictionary. (Can you tell this problem was bugging me? Now it's been solved three times over!)

#!/usr/bin/python3
# Dependencies: $ sudo apt-get install python3 pip3 python3-tk && sudo pip3 install notify2 easygui
# Purpose: Running this script ejects the mounted usb device. If there's more than one, user gets a menu.
# Terms:
#   media_dir is where external devices are mounted. on debian/devuan, that directory is /media/$USER/
#   label example: PATRIOT   
#   media_path example: /media/bruno/PATRIOT
#   dev_path example: /dev/sdb1

import os
import getpass
import notify2
import easygui

def notify(message):
    """Displays a notification. String argument is message to be displayed."""
    notify2.init('Device Ejector')
    notify2.Notification('Device Ejector', message, 'dialog-warning').show()
    
def get_dev_path_from_label(label):
    """Returns dev_path from volume label."""
    media_path = media_dir + label
    dev_path = os.popen("df %s | tail -1 | awk '{ print $1 }'" % (media_path)).read().strip()
    return dev_path
    
def get_user_choice(devices):
    """Build menu from device attributes, return chosen device."""
    menu_choices = []
    for device in devices:
        menu_choices.append(device.label + ': ' + device.dev_path)
    choice = easygui.choicebox('Choose a device to eject:', 'Device Ejector', menu_choices)
    chosen_label = choice.split(': ')[0]
    for device in devices:
        if device.label == chosen_label:
            return device
    
class Device():
    """A simple representation of an external usb device."""
    
    def __init__(self, label, dev_path):
        """Initialize attributes to describe external usb device."""
        self.label = label
        self.dev_path = dev_path
    
    def unmount_and_eject(self):
        """Self-unmount and eject."""
        exit_code = os.system('udisksctl unmount -b %s && udisksctl power-off -b %s' % (self.dev_path, self.dev_path))
        if exit_code != 0:
            notify("Couldn't eject device. Target busy?")
        
media_dir = '/media/%s/' % getpass.getuser()
labels = os.listdir(media_dir)
n_labels = len(labels)
if n_labels > 0:
    devices = []    # let's build a list of Device objects
    for label in labels:
        dev_path = get_dev_path_from_label(label)
        device = Device(label, dev_path)
        devices.append(device)

if n_labels == 0:
    notify("Nothing to eject")
elif n_labels == 1:
    devices[0].unmount_and_eject()
elif n_labels > 1:
    target_device = get_user_choice(devices)
    target_device.unmount_and_eject()

Offline

Board footer

Powered by FluxBB