diff options
Diffstat (limited to 'extra/udisks2')
-rw-r--r-- | extra/udisks2/PKGBUILD | 22 | ||||
-rw-r--r-- | extra/udisks2/git-fixes.patch | 777 |
2 files changed, 792 insertions, 7 deletions
diff --git a/extra/udisks2/PKGBUILD b/extra/udisks2/PKGBUILD index e1b1528a8..03dbb40dd 100644 --- a/extra/udisks2/PKGBUILD +++ b/extra/udisks2/PKGBUILD @@ -1,9 +1,9 @@ -# $Id: PKGBUILD 198468 2013-10-30 15:03:29Z allan $ +# $Id: PKGBUILD 206043 2014-02-16 10:13:00Z jgc $ # Maintainer: Ionut Biru <ibiru@archlinux.org> pkgname=udisks2 -pkgver=2.1.1 -pkgrel=3 +pkgver=2.1.2 +pkgrel=1 pkgdesc="Disk Management Service, version 2" arch=('i686' 'x86_64') url="http://www.freedesktop.org/wiki/Software/udisks" @@ -14,11 +14,19 @@ optdepends=('parted: partition management' 'gptfdisk: GUID partition table support' 'ntfs-3g: NTFS filesystem management support' 'dosfstools: VFAT filesystem management support') -source=(http://udisks.freedesktop.org/releases/udisks-$pkgver.tar.bz2) -sha256sums=('013b09ff38aa256b89c01525771f4565cb088724d5c8c79b32e9f811e88086fb') +source=(http://udisks.freedesktop.org/releases/udisks-$pkgver.tar.bz2{,.sign} + git-fixes.patch) +sha256sums=('afe928fe7f44086b835257efc3fcbe503e2da7c988a773c997d0291bbab4a637' + 'SKIP' + '52a9b9f039f9f115414715375f2b5874b79b5b67fbe2bdf3105b46d3a35f673f') + +prepare() { + cd udisks-$pkgver + patch -Np1 -i ../git-fixes.patch +} build() { - cd "udisks-$pkgver" + cd udisks-$pkgver ./configure --prefix=/usr --sysconfdir=/etc \ --sbindir=/usr/bin \ --with-systemdsystemunitdir=/usr/lib/systemd/system \ @@ -27,7 +35,7 @@ build() { } package() { - cd "udisks-$pkgver" + cd udisks-$pkgver make DESTDIR="$pkgdir" install \ bash_completiondir=/usr/share/bash-completion/completions } diff --git a/extra/udisks2/git-fixes.patch b/extra/udisks2/git-fixes.patch new file mode 100644 index 000000000..43cc53b05 --- /dev/null +++ b/extra/udisks2/git-fixes.patch @@ -0,0 +1,777 @@ +diff --git a/data/80-udisks2.rules b/data/80-udisks2.rules +index ed093ee..7112db8 100644 +--- a/data/80-udisks2.rules ++++ b/data/80-udisks2.rules +@@ -94,6 +94,9 @@ SUBSYSTEMS=="usb", ENV{ID_VENDOR}=="*SanDisk*", ENV{ID_MODEL}=="*Cruzer*", ENV{I + SUBSYSTEMS=="usb", ENV{ID_VENDOR}=="HP", ENV{ID_MODEL}=="*v125w*", ENV{ID_DRIVE_THUMB}="1" + SUBSYSTEMS=="usb", ENV{ID_VENDOR_ID}=="13fe", ENV{ID_MODEL}=="*Patriot*", ENV{ID_DRIVE_THUMB}="1" + ++# SD-Card reader in Chromebook Pixel ++SUBSYSTEMS=="usb", ENV{ID_VENDOR_ID}=="05e3", ENV{ID_MODEL_ID}=="0727", ENV{ID_DRIVE_FLASH_SD}="1" ++ + # ------------------------------------------------------------------------ + # ------------------------------------------------------------------------ + # ------------------------------------------------------------------------ +diff --git a/doc/man/udisksctl.xml b/doc/man/udisksctl.xml +index 8f38479..434a8fb 100644 +--- a/doc/man/udisksctl.xml ++++ b/doc/man/udisksctl.xml +@@ -104,6 +104,16 @@ + + <cmdsynopsis> + <command>udisksctl</command> ++ <arg choice="plain">power-off </arg> ++ <group choice="req"> ++ <arg choice="plain">--object-path <replaceable>OBJECT</replaceable></arg> ++ <arg choice="plain">--block-device <replaceable>DEVICE</replaceable></arg> ++ </group> ++ <arg choice="opt">--no-user-interaction</arg> ++ </cmdsynopsis> ++ ++ <cmdsynopsis> ++ <command>udisksctl</command> + <arg choice="plain">smart-simulate </arg> + <arg choice="plain">--file <replaceable>PATH</replaceable></arg> + <group choice="req"> +@@ -238,6 +248,31 @@ + </varlistentry> + + <varlistentry> ++ <term><option>power-off</option></term> ++ <listitem> ++ <para> ++ Arranges for the drive to be safely removed and powered ++ off. On the OS side this includes ensuring that no process ++ is using the drive, then requesting that in-flight buffers ++ and caches are committed to stable storage. The exact ++ steps for powering off the drive depends on the drive ++ itself and the interconnect used. For drives connected ++ through USB, the effect is that the USB device will be ++ deconfigured followed by disabling the upstream hub port ++ it is connected to. ++ </para> ++ <para> ++ Note that as some physical devices contain multiple drives ++ (for example 4-in-1 flash card reader USB devices) ++ powering off one drive may affect other drives. As such ++ there are not a lot of guarantees associated with ++ performing this action. Usually the effect is that the ++ drive disappears as if it was unplugged. ++ </para> ++ </listitem> ++ </varlistentry> ++ ++ <varlistentry> + <term><option>smart-simulate</option></term> + <listitem> + <para> +diff --git a/src/udisksdaemonutil.c b/src/udisksdaemonutil.c +index 574bf2c..a588580 100644 +--- a/src/udisksdaemonutil.c ++++ b/src/udisksdaemonutil.c +@@ -830,7 +830,7 @@ udisks_daemon_util_get_caller_uid_sync (UDisksDaemon *daemon, + { + struct passwd pwstruct; + gchar pwbuf[8192]; +- static struct passwd *pw; ++ struct passwd *pw = NULL; + int rc; + + rc = getpwuid_r (uid, &pwstruct, pwbuf, sizeof pwbuf, &pw); +@@ -840,6 +840,7 @@ udisks_daemon_util_get_caller_uid_sync (UDisksDaemon *daemon, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "User with uid %d does not exist", (gint) uid); ++ goto out; + } + else if (pw == NULL) + { +diff --git a/src/udiskslinuxblock.c b/src/udiskslinuxblock.c +index d619850..22bcfd0 100644 +--- a/src/udiskslinuxblock.c ++++ b/src/udiskslinuxblock.c +@@ -804,12 +804,23 @@ udisks_linux_block_update (UDisksLinuxBlock *block, + gchar *dm_name_dev_file = NULL; + const gchar *dm_name_dev_file_as_symlink = NULL; + ++ const gchar *dm_vg_name; ++ const gchar *dm_lv_name; ++ gchar *dm_lvm_dev_file = NULL; ++ + dm_name = g_udev_device_get_property (device->udev_device, "DM_NAME"); + if (dm_name != NULL) + dm_name_dev_file = g_strdup_printf ("/dev/mapper/%s", dm_name); ++ ++ dm_vg_name = g_udev_device_get_property (device->udev_device, "DM_VG_NAME"); ++ dm_lv_name = g_udev_device_get_property (device->udev_device, "DM_LV_NAME"); ++ if (dm_vg_name != NULL && dm_lv_name != NULL) ++ dm_lvm_dev_file = g_strdup_printf ("/dev/%s/%s", dm_vg_name, dm_lv_name); ++ + for (n = 0; symlinks != NULL && symlinks[n] != NULL; n++) + { +- if (g_str_has_prefix (symlinks[n], "/dev/vg_")) ++ if (g_str_has_prefix (symlinks[n], "/dev/vg_") ++ || g_strcmp0 (symlinks[n], dm_lvm_dev_file) == 0) + { + /* LVM2 */ + preferred_device_file = symlinks[n]; +@@ -824,6 +835,7 @@ udisks_linux_block_update (UDisksLinuxBlock *block, + if (preferred_device_file == NULL && dm_name_dev_file_as_symlink != NULL) + preferred_device_file = dm_name_dev_file_as_symlink; + g_free (dm_name_dev_file); ++ g_free (dm_lvm_dev_file); + } + else if (g_str_has_prefix (device_file, "/dev/md")) + { +diff --git a/src/udiskslinuxdevice.c b/src/udiskslinuxdevice.c +index 0b65a69..8c4a3ed 100644 +--- a/src/udiskslinuxdevice.c ++++ b/src/udiskslinuxdevice.c +@@ -199,6 +199,7 @@ probe_ata (UDisksLinuxDevice *device, + { + /* ATA8: 7.16 IDENTIFY DEVICE - ECh, PIO Data-In */ + input.command = 0xec; ++ input.count = 1; + output.buffer = g_new0 (guchar, 512); + output.buffer_size = 512; + if (!udisks_ata_send_command_sync (fd, +@@ -221,6 +222,7 @@ probe_ata (UDisksLinuxDevice *device, + { + /* ATA8: 7.17 IDENTIFY PACKET DEVICE - A1h, PIO Data-In */ + input.command = 0xa1; ++ input.count = 1; + output.buffer = g_new0 (guchar, 512); + output.buffer_size = 512; + if (!udisks_ata_send_command_sync (fd, +diff --git a/src/udiskslinuxdrive.c b/src/udiskslinuxdrive.c +index 170ba27..ed541ff 100644 +--- a/src/udiskslinuxdrive.c ++++ b/src/udiskslinuxdrive.c +@@ -25,6 +25,12 @@ + #include <sys/stat.h> + #include <sys/ioctl.h> + #include <fcntl.h> ++#include <inttypes.h> ++#include <errno.h> ++#include <linux/bsg.h> ++#include <scsi/scsi.h> ++#include <scsi/sg.h> ++#include <scsi/scsi_ioctl.h> + + #include <pwd.h> + #include <grp.h> +@@ -1192,6 +1198,122 @@ handle_set_configuration (UDisksDrive *_drive, + + /* ---------------------------------------------------------------------------------------------------- */ + ++/* TODO: move to udisksscsi.[ch] similar what we do for ATA with udisksata.[ch] */ ++ ++static gboolean ++send_scsi_command_sync (gint fd, ++ guint8 *cdb, ++ gsize cdb_len, ++ GError **error) ++{ ++ struct sg_io_v4 io_v4; ++ uint8_t sense[32]; ++ gboolean ret = FALSE; ++ gint rc; ++ gint timeout_msec = 30000; /* 30 seconds */ ++ ++ g_return_val_if_fail (fd != -1, FALSE); ++ g_return_val_if_fail (error == NULL || *error == NULL, FALSE); ++ ++ /* See http://sg.danny.cz/sg/sg_io.html and http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO/index.html ++ * for detailed information about how the SG_IO ioctl work ++ */ ++ ++ memset (sense, 0, sizeof (sense)); ++ memset (&io_v4, 0, sizeof (io_v4)); ++ io_v4.guard = 'Q'; ++ io_v4.protocol = BSG_PROTOCOL_SCSI; ++ io_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD; ++ io_v4.request_len = cdb_len; ++ io_v4.request = (uintptr_t) cdb; ++ io_v4.max_response_len = sizeof (sense); ++ io_v4.response = (uintptr_t) sense; ++ io_v4.timeout = timeout_msec; ++ ++ rc = ioctl (fd, SG_IO, &io_v4); ++ if (rc != 0) ++ { ++ /* could be that the driver doesn't do version 4, try version 3 */ ++ if (errno == EINVAL) ++ { ++ struct sg_io_hdr io_hdr; ++ memset (&io_hdr, 0, sizeof (struct sg_io_hdr)); ++ io_hdr.interface_id = 'S'; ++ io_hdr.cmdp = (unsigned char*) cdb; ++ io_hdr.cmd_len = cdb_len; ++ io_hdr.dxfer_direction = SG_DXFER_NONE; ++ io_hdr.sbp = sense; ++ io_hdr.mx_sb_len = sizeof (sense); ++ io_hdr.timeout = timeout_msec; ++ ++ rc = ioctl (fd, SG_IO, &io_hdr); ++ if (rc != 0) ++ { ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "SGIO v3 ioctl failed (v4 not supported): %m"); ++ goto out; ++ } ++ else ++ { ++ if (!(io_hdr.status == 0 && ++ io_hdr.host_status == 0 && ++ io_hdr.driver_status == 0)) ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Non-GOOD SCSI status from SGIO v3 ioctl: " ++ "status=%d host_status=%d driver_status=%d", ++ io_hdr.status, ++ io_hdr.host_status, ++ io_hdr.driver_status); ++ goto out; ++ } ++ } ++ } ++ else ++ { ++ g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), ++ "SGIO v4 ioctl failed: %m"); ++ goto out; ++ } ++ } ++ else ++ { ++ if (!(io_v4.device_status == 0 && ++ io_v4.transport_status == 0 && ++ io_v4.driver_status == 0)) ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Non-GOOD SCSI status from SGIO v4 ioctl: " ++ "device_status=%d transport_status=%d driver_status=%d", ++ io_v4.device_status, ++ io_v4.transport_status, ++ io_v4.driver_status); ++ goto out; ++ } ++ } ++ ++ ret = TRUE; ++ ++ out: ++ return ret; ++} ++ ++static gboolean ++send_scsi_start_stop_command_sync (gint fd, ++ GError **error) ++{ ++ uint8_t cdb[6]; ++ ++ /* SBC3 (SCSI Block Commands), 5.20 START STOP UNIT command ++ */ ++ memset (cdb, 0, sizeof cdb); ++ cdb[0] = 0x1b; /* OPERATION CODE: START STOP UNIT */ ++ ++ return send_scsi_command_sync (fd, cdb, sizeof cdb, error); ++} ++ ++/* ---------------------------------------------------------------------------------------------------- */ ++ + static gboolean + handle_power_off (UDisksDrive *_drive, + GDBusMethodInvocation *invocation, +@@ -1216,6 +1338,7 @@ handle_power_off (UDisksDrive *_drive, + gid_t caller_gid; + pid_t caller_pid; + GList *sibling_objects = NULL, *l; ++ gint fd = -1; + + object = udisks_daemon_util_dup_object (drive, &error); + if (object == NULL) +@@ -1324,10 +1447,10 @@ handle_power_off (UDisksDrive *_drive, + { + UDisksBlock *block_to_sync = UDISKS_BLOCK (l->data); + const gchar *device_file; +- gint fd; ++ gint device_fd; + device_file = udisks_block_get_device (block_to_sync); +- fd = open (device_file, O_RDONLY|O_NONBLOCK|O_EXCL); +- if (fd == -1) ++ device_fd = open (device_file, O_RDONLY|O_NONBLOCK|O_EXCL); ++ if (device_fd == -1) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, +@@ -1336,7 +1459,7 @@ handle_power_off (UDisksDrive *_drive, + device_file); + goto out; + } +- if (fsync (fd) != 0) ++ if (fsync (device_fd) != 0) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, +@@ -1345,7 +1468,7 @@ handle_power_off (UDisksDrive *_drive, + device_file); + goto out; + } +- if (close (fd) != 0) ++ if (close (device_fd) != 0) + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, +@@ -1356,9 +1479,45 @@ handle_power_off (UDisksDrive *_drive, + } + } + +- escaped_device = udisks_daemon_util_escape_and_quote (udisks_block_get_device (block)); ++ /* Send the "SCSI START STOP UNIT" command to request that the unit ++ * be stopped but don't treat failure as fatal. In fact some ++ * USB-attached hard-disks fails with this command, probably due to ++ * the SCSI/SATA translation layer. ++ */ ++ fd = open (udisks_block_get_device (block), O_RDONLY|O_NONBLOCK|O_EXCL); ++ if (fd == -1) ++ { ++ g_dbus_method_invocation_return_error (invocation, ++ UDISKS_ERROR, ++ UDISKS_ERROR_FAILED, ++ "Error opening %s: %m", ++ udisks_block_get_device (block)); ++ goto out; ++ } ++ if (!send_scsi_start_stop_command_sync (fd, &error)) ++ { ++ udisks_warning ("Ignoring SCSI command START STOP UNIT failure (%s) on %s", ++ error->message, ++ udisks_block_get_device (block)); ++ g_clear_error (&error); ++ } ++ else ++ { ++ udisks_notice ("Powering off %s - successfully sent SCSI command START STOP UNIT", ++ udisks_block_get_device (block)); ++ } ++ if (close (fd) != 0) ++ { ++ g_dbus_method_invocation_return_error (invocation, ++ UDISKS_ERROR, ++ UDISKS_ERROR_FAILED, ++ "Error closing %s: %m", ++ udisks_block_get_device (block)); ++ goto out; ++ } ++ fd = -1; + +- /* TODO: Send the eject? Send SCSI START STOP UNIT? */ ++ escaped_device = udisks_daemon_util_escape_and_quote (udisks_block_get_device (block)); + device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */); + if (device == NULL) + { +@@ -1405,10 +1564,20 @@ handle_power_off (UDisksDrive *_drive, + } + } + fclose (f); ++ udisks_notice ("Powered off %s - successfully wrote to sysfs path %s", ++ udisks_block_get_device (block), ++ remove_path); + + udisks_drive_complete_power_off (UDISKS_DRIVE (drive), invocation); + + out: ++ if (fd != -1) ++ { ++ if (close (fd) != 0) ++ { ++ udisks_warning ("Error closing device: %m"); ++ } ++ } + g_list_free_full (blocks_to_sync, g_object_unref); + g_list_free_full (sibling_objects, g_object_unref); + g_free (remove_path); +diff --git a/src/udiskslinuxdriveata.c b/src/udiskslinuxdriveata.c +index 48cc6e6..534ef4d 100644 +--- a/src/udiskslinuxdriveata.c ++++ b/src/udiskslinuxdriveata.c +@@ -1943,7 +1943,7 @@ udisks_linux_drive_ata_secure_erase_sync (UDisksLinuxDriveAta *drive, + /* First get the IDENTIFY data directly from the drive, for sanity checks */ + { + /* ATA8: 7.16 IDENTIFY DEVICE - ECh, PIO Data-In */ +- UDisksAtaCommandInput input = {.command = 0xec}; ++ UDisksAtaCommandInput input = {.command = 0xec, .count = 1}; + UDisksAtaCommandOutput output = {.buffer = identify.buf, .buffer_size = sizeof (identify.buf)}; + if (!udisks_ata_send_command_sync (fd, + -1, +diff --git a/src/udiskslinuxfilesystem.c b/src/udiskslinuxfilesystem.c +index 4c8d8aa..f243046 100644 +--- a/src/udiskslinuxfilesystem.c ++++ b/src/udiskslinuxfilesystem.c +@@ -348,13 +348,16 @@ find_mount_options_for_fs (const gchar *fstype) + static gid_t + find_primary_gid (uid_t uid) + { +- struct passwd *pw; ++ struct passwd *pw = NULL; ++ struct passwd pwstruct; ++ gchar pwbuf[8192]; ++ int rc; + gid_t gid; + + gid = (gid_t) - 1; + +- pw = getpwuid (uid); +- if (pw == NULL) ++ rc = getpwuid_r (uid, &pwstruct, pwbuf, sizeof pwbuf, &pw); ++ if (rc != 0 || pw == NULL) + { + udisks_warning ("Error looking up uid %d: %m", uid); + goto out; +@@ -370,7 +373,10 @@ is_uid_in_gid (uid_t uid, + gid_t gid) + { + gboolean ret; +- struct passwd *pw; ++ struct passwd *pw = NULL; ++ struct passwd pwstruct; ++ gchar pwbuf[8192]; ++ int rc; + static gid_t supplementary_groups[128]; + int num_supplementary_groups = 128; + int n; +@@ -379,8 +385,8 @@ is_uid_in_gid (uid_t uid, + + ret = FALSE; + +- pw = getpwuid (uid); +- if (pw == NULL) ++ rc = getpwuid_r (uid, &pwstruct, pwbuf, sizeof pwbuf, &pw); ++ if (rc != 0 || pw == NULL) + { + udisks_warning ("Error looking up uid %d: %m", uid); + goto out; +diff --git a/src/udisksspawnedjob.c b/src/udisksspawnedjob.c +index 802551f..b181933 100644 +--- a/src/udisksspawnedjob.c ++++ b/src/udisksspawnedjob.c +@@ -371,22 +371,25 @@ static void + child_setup (gpointer user_data) + { + UDisksSpawnedJob *job = UDISKS_SPAWNED_JOB (user_data); +- struct passwd *pw; ++ struct passwd pwstruct; ++ gchar pwbuf[8192]; ++ struct passwd *pw = NULL; ++ int rc; + gid_t egid; + + if (job->run_as_uid == getuid () && job->run_as_euid == geteuid ()) + goto out; + +- pw = getpwuid (job->run_as_euid); +- if (pw == NULL) ++ rc = getpwuid_r (job->run_as_euid, &pwstruct, pwbuf, sizeof pwbuf, &pw); ++ if (rc != 0 || pw == NULL) + { + g_printerr ("No password record for uid %d: %m\n", (gint) job->run_as_euid); + abort (); + } + egid = pw->pw_gid; + +- pw = getpwuid (job->run_as_uid); +- if (pw == NULL) ++ rc = getpwuid_r (job->run_as_uid, &pwstruct, pwbuf, sizeof pwbuf, &pw); ++ if (rc != 0 || pw == NULL) + { + g_printerr ("No password record for uid %d: %m\n", (gint) job->run_as_uid); + abort (); +diff --git a/tools/udisksctl.c b/tools/udisksctl.c +index 97b0f17..c87fe9f 100644 +--- a/tools/udisksctl.c ++++ b/tools/udisksctl.c +@@ -1691,6 +1691,12 @@ handle_command_loop (gint *argc, + goto out; + } + ++ if (udisks_object_peek_loop (object) == NULL) ++ { ++ g_printerr ("Error: specified object is not a loop device\n"); ++ goto out; ++ } ++ + delete_try_again: + error = NULL; + if (!udisks_loop_call_delete_sync (udisks_object_peek_loop (object), +@@ -2009,6 +2015,238 @@ handle_command_smart_simulate (gint *argc, + + /* ---------------------------------------------------------------------------------------------------- */ + ++static gchar *opt_power_off_object_path = NULL; ++static gchar *opt_power_off_device = NULL; ++static gboolean opt_power_off_no_user_interaction = FALSE; ++ ++static const GOptionEntry command_power_off_entries[] = ++{ ++ { ++ "object-path", ++ 'p', ++ 0, ++ G_OPTION_ARG_STRING, ++ &opt_power_off_object_path, ++ "Object path for ATA device", ++ NULL ++ }, ++ { ++ "block-device", ++ 'b', ++ 0, ++ G_OPTION_ARG_STRING, ++ &opt_power_off_device, ++ "Device file for ATA device", ++ NULL ++ }, ++ { ++ "no-user-interaction", ++ 0, /* no short option */ ++ 0, ++ G_OPTION_ARG_NONE, ++ &opt_power_off_no_user_interaction, ++ "Do not authenticate the user if needed", ++ NULL ++ }, ++ { ++ NULL ++ } ++}; ++ ++static gint ++handle_command_power_off (gint *argc, ++ gchar **argv[], ++ gboolean request_completion, ++ const gchar *completion_cur, ++ const gchar *completion_prev) ++{ ++ gint ret; ++ GOptionContext *o; ++ gchar *s; ++ gboolean complete_objects; ++ gboolean complete_devices; ++ GList *l; ++ GList *objects; ++ UDisksObject *object; ++ UDisksDriveAta *ata; ++ guint n; ++ GVariant *options; ++ GVariantBuilder builder; ++ GError *error; ++ ++ ret = 1; ++ opt_power_off_object_path = NULL; ++ opt_power_off_device = NULL; ++ object = NULL; ++ options = NULL; ++ ++ modify_argv0_for_command (argc, argv, "power-off"); ++ ++ o = g_option_context_new (NULL); ++ if (request_completion) ++ g_option_context_set_ignore_unknown_options (o, TRUE); ++ g_option_context_set_help_enabled (o, FALSE); ++ g_option_context_set_summary (o, "Safely power off a drive."); ++ g_option_context_add_main_entries (o, ++ command_power_off_entries, ++ NULL /* GETTEXT_PACKAGE*/); ++ ++ complete_objects = FALSE; ++ if (request_completion && (g_strcmp0 (completion_prev, "--object-path") == 0 || g_strcmp0 (completion_prev, "-p") == 0)) ++ { ++ complete_objects = TRUE; ++ remove_arg ((*argc) - 1, argc, argv); ++ } ++ ++ complete_devices = FALSE; ++ if (request_completion && (g_strcmp0 (completion_prev, "--block-device") == 0 || g_strcmp0 (completion_prev, "-b") == 0)) ++ { ++ complete_devices = TRUE; ++ remove_arg ((*argc) - 1, argc, argv); ++ } ++ ++ if (!g_option_context_parse (o, argc, argv, NULL)) ++ { ++ if (!request_completion) ++ { ++ s = g_option_context_get_help (o, FALSE, NULL); ++ g_printerr ("%s", s); ++ g_free (s); ++ goto out; ++ } ++ } ++ ++ if (request_completion) ++ { ++ if ((opt_power_off_object_path == NULL && !complete_objects) && ++ (opt_power_off_device == NULL && !complete_devices)) ++ { ++ g_print ("--object-path \n" ++ "--block-device \n"); ++ } ++ ++ if (complete_objects) ++ { ++ const gchar *object_path; ++ objects = g_dbus_object_manager_get_objects (udisks_client_get_object_manager (client)); ++ for (l = objects; l != NULL; l = l->next) ++ { ++ object = UDISKS_OBJECT (l->data); ++ ata = udisks_object_peek_drive_ata (object); ++ if (ata != NULL) ++ { ++ object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (object)); ++ g_assert (g_str_has_prefix (object_path, "/org/freedesktop/UDisks2/")); ++ g_print ("%s \n", object_path + sizeof ("/org/freedesktop/UDisks2/") - 1); ++ } ++ } ++ g_list_foreach (objects, (GFunc) g_object_unref, NULL); ++ g_list_free (objects); ++ } ++ ++ if (complete_devices) ++ { ++ objects = g_dbus_object_manager_get_objects (udisks_client_get_object_manager (client)); ++ for (l = objects; l != NULL; l = l->next) ++ { ++ object = UDISKS_OBJECT (l->data); ++ ata = udisks_object_peek_drive_ata (object); ++ if (ata != NULL) ++ { ++ const gchar * const *symlinks; ++ UDisksBlock *block; ++ block = udisks_client_get_block_for_drive (client, udisks_object_peek_drive (object), TRUE); ++ g_print ("%s \n", udisks_block_get_device (block)); ++ symlinks = udisks_block_get_symlinks (block); ++ for (n = 0; symlinks != NULL && symlinks[n] != NULL; n++) ++ g_print ("%s \n", symlinks[n]); ++ } ++ } ++ g_list_foreach (objects, (GFunc) g_object_unref, NULL); ++ g_list_free (objects); ++ } ++ goto out; ++ } ++ ++ g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); ++ if (opt_power_off_no_user_interaction) ++ { ++ g_variant_builder_add (&builder, ++ "{sv}", ++ "auth.no_user_interaction", g_variant_new_boolean (TRUE)); ++ } ++ options = g_variant_builder_end (&builder); ++ g_variant_ref_sink (options); ++ ++ if (opt_power_off_object_path != NULL) ++ { ++ object = lookup_object_by_path (opt_power_off_object_path); ++ if (object == NULL) ++ { ++ g_printerr ("Error looking up object with path %s\n", opt_power_off_object_path); ++ goto out; ++ } ++ } ++ else if (opt_power_off_device != NULL) ++ { ++ UDisksObject *block_object; ++ UDisksDrive *drive; ++ block_object = lookup_object_by_device (opt_power_off_device); ++ if (block_object == NULL) ++ { ++ g_printerr ("Error looking up object for device %s\n", opt_power_off_device); ++ goto out; ++ } ++ drive = udisks_client_get_drive_for_block (client, udisks_object_peek_block (block_object)); ++ object = (UDisksObject *) g_dbus_interface_dup_object (G_DBUS_INTERFACE (drive)); ++ g_object_unref (block_object); ++ } ++ else ++ { ++ s = g_option_context_get_help (o, FALSE, NULL); ++ g_printerr ("%s", s); ++ g_free (s); ++ goto out; ++ } ++ ++ try_again: ++ error = NULL; ++ if (!udisks_drive_call_power_off_sync (udisks_object_peek_drive (object), ++ options, ++ NULL, /* GCancellable */ ++ &error)) ++ { ++ if (error->domain == UDISKS_ERROR && ++ error->code == UDISKS_ERROR_NOT_AUTHORIZED_CAN_OBTAIN && ++ setup_local_polkit_agent ()) ++ { ++ g_error_free (error); ++ goto try_again; ++ } ++ g_dbus_error_strip_remote_error (error); ++ g_printerr ("Error powering off drive: %s (%s, %d)\n", ++ error->message, g_quark_to_string (error->domain), error->code); ++ g_clear_error (&error); ++ g_object_unref (object); ++ goto out; ++ } ++ ++ g_object_unref (object); ++ ++ ++ ret = 0; ++ ++ out: ++ if (options != NULL) ++ g_variant_unref (options); ++ g_option_context_free (o); ++ g_free (opt_power_off_object_path); ++ g_free (opt_power_off_device); ++ return ret; ++} ++ ++/* ---------------------------------------------------------------------------------------------------- */ ++ + static gchar *opt_info_object = NULL; + static gchar *opt_info_device = NULL; + static gchar *opt_info_drive = NULL; +@@ -2855,6 +3093,7 @@ usage (gint *argc, gchar **argv[], gboolean use_stdout) + " lock Lock an encrypted device\n" + " loop-setup Set-up a loop device\n" + " loop-delete Delete a loop device\n" ++ " power-off Safely power off a drive\n" + " smart-simulate Set SMART data for a drive\n" + "\n" + "Use \"%s COMMAND --help\" to get help on each command.\n", +@@ -3053,6 +3292,15 @@ main (int argc, + completion_prev); + goto out; + } ++ else if (g_strcmp0 (command, "power-off") == 0) ++ { ++ ret = handle_command_power_off (&argc, ++ &argv, ++ request_completion, ++ completion_cur, ++ completion_prev); ++ goto out; ++ } + else if (g_strcmp0 (command, "dump") == 0) + { + ret = handle_command_dump (&argc, +@@ -3156,6 +3404,7 @@ main (int argc, + "unlock \n" + "loop-setup \n" + "loop-delete \n" ++ "power-off \n" + "smart-simulate \n" + ); + ret = 0; |