diff options
-rw-r--r-- | configure.in | 1 | ||||
-rw-r--r-- | doc/man/Makefile.am | 4 | ||||
-rw-r--r-- | doc/man/hal-disable-polling.1.in | 137 | ||||
-rw-r--r-- | tools/Makefile.am | 6 | ||||
-rw-r--r-- | tools/hal-disable-polling.c | 276 |
5 files changed, 421 insertions, 3 deletions
diff --git a/configure.in b/configure.in index 349102a3..d3dd295e 100644 --- a/configure.in +++ b/configure.in @@ -893,6 +893,7 @@ doc/man/hal-find-by-property.1 doc/man/hal-find-by-capability.1 doc/man/hal-is-caller-locked-out.1 doc/man/hal-lock.1 +doc/man/hal-disable-polling.1 po/Makefile.in ]) diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am index 4f22641c..5f5416dd 100644 --- a/doc/man/Makefile.am +++ b/doc/man/Makefile.am @@ -1,9 +1,9 @@ if MAN_PAGES_ENABLED -MAN_IN_FILES = hald.1.in lshal.1.in hal-get-property.1.in hal-set-property.1.in hal-find-by-property.1.in hal-find-by-capability.1.in hal-is-caller-locked-out.1.in hal-lock.1.in +MAN_IN_FILES = hald.1.in lshal.1.in hal-get-property.1.in hal-set-property.1.in hal-find-by-property.1.in hal-find-by-capability.1.in hal-is-caller-locked-out.1.in hal-lock.1.in hal-disable-polling.1.in -man_MANS = hald.1 lshal.1 hal-get-property.1 hal-set-property.1 hal-find-by-property.1 hal-find-by-capability.1 hal-is-caller-locked-out.1 hal-lock.1 +man_MANS = hald.1 lshal.1 hal-get-property.1 hal-set-property.1 hal-find-by-property.1 hal-find-by-capability.1 hal-is-caller-locked-out.1 hal-lock.1 hal-disable-polling.1 endif # MAN_PAGES_ENABLED diff --git a/doc/man/hal-disable-polling.1.in b/doc/man/hal-disable-polling.1.in new file mode 100644 index 00000000..26f27697 --- /dev/null +++ b/doc/man/hal-disable-polling.1.in @@ -0,0 +1,137 @@ +.\" +.\" hal-disable-polling manual page. +.\" Copyright (C) 2007 David Zeuthen <david@fubar.dk> +.\" +.TH HAL-DISABLE-POLLING 1 +.SH NAME +hal-disable-polling \- disable polling on drives with removable media +.SH SYNOPSIS +.PP +.B hal-disable-polling +[options] + +.SH DESCRIPTION + +\fIhal-disable-polling\fP can be used to to disable and enable media +detection on drives with removable storage. For more information about +both the big picture and specific +.B HAL +properties, refer to the \fIHAL spec\fP which can be found in +.I "/usr/share/doc/hal-@VERSION@/spec/hal-spec.html" +depending on the distribution. + +.SH OPTIONS +The following options are supported: +.TP +.I "--udi" +The UDI (\fIUnique Device Identifier\fP) of the device object. +.TP +.I "--device" +The device file of the drive. +.TP +.I "--enable-polling" +Enable polling instead of disabling it. +.TP +.I "--help" +Print out usage. +.TP +.I "--version" +Print the version. + +.SH NOTES +.PP +This program requires super user privileges. + +.SH RETURN VALUE +.PP +If the requested operation was successful, this program will exit with +exit code 0. + +.SH HISTORY +.PP +Polling a storage drive is a necessary evil to detect when the user +inserts or removes media. Human computer interaction studies have +shown that a broad class of users expect their system to react within +a few seconds of this. Thus, the +.I hald +daemon polls through the +.I hald-addon-storage +addon (one instance for each drive with removable media). + +The purpose of the +.I hald-addon-storage +addon is simply to open the special device file at a regular interval +(either every 2 or every 16 seconds) to check for new media. This +program tries to open the device file using the +.B O_EXCL +option which means that programs like \&\fIcdrecord\fR\|(1) that uses +.B O_EXCL +automatically prevents the +.I hald-addon-storage +for interferring by continously opening the device file. In addition, +if the drive is locked using HAL (see \&\fIhal-lock\fR\|(1)) the addon +also stops polling. + +Unfortunately, polling a storage drive can have adverse side effects +if the hardware and/or device driver for the hardware is +malfunctioning. Additionally, the operating system kernel itself may +offer multiple interfaces for the same device (e.g. \&\fI/dev/sg0\fR\| +and \&\fI/dev/scd0\fR\|) so even +.B O_EXCL +won't work. Also, polling a drive may decrease throughput in certain +(odd and/or broken) configurations; for example, if two IDE drives +shares the same host (master/slave), bus traffic and contention caused +by polling e.g. the optical drive (slave) can reduce throughput to the +hard disk (master) and/or interfere with CD burning on another optical +drive (master). Finally, polling a drive incurs an overhead both in +the host system (processes get woken up often, preventing the CPU to +stay in a deep power saving states) and it may prevent the actual +drive from reaching deep power states as well. As a result, more power +is consumed and this affects battery life for laptops. + +Despite the existence of support for asynchronous media change +notification in recent MMC (Multi-Media Commands) specifications, +virtually no optical drives are compliant with the +specification. Fortunately newer SATA ATAPI hardware seems to support +Asynchronous Notification (AN) and at this time of writing (March +2007) work is underway to make both the +.I Linux +operating system kernel and +.I HAL +take advantage of this. + +It is the position of the +.I HAL +team that polling should be avoided at all costs as long as it doesn't +heavily impact the user experience in a negative way. This tool is +provided as a stop-gap measure to use if a system is rendered useless +due to bugs in drivers and/or hardware that is provoked by HAL polling +the drive. If such a bug is encountered it should be reported (see the +.B BUGS +section below) so it can be fixed - historically +.B hald +have triggered a number of bugs in +.I Linux +storage drivers and related subsystems (such as USB) that have later +been fixed. + +.SH BUGS +.PP +Please send bug reports to either the distribution or the HAL +mailing list, see +.I "http://lists.freedesktop.org/mailman/listinfo/hal" +on how to subscribe. + +.SH SEE ALSO +.PP +\&\fIhald\fR\|(1), +\&\fIlshal\fR\|(1), +\&\fIhal-lock\fR\|(1), +\&\fIopen\fR\|(2), +\&\fIhttp://www.t10.org/scsi-3.htm\fR\|, +\&\fIhttps://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=halpolling\fR\| + +.SH AUTHOR +Written by David Zeuthen <david@fubar.dk> with a lot of help from many +others. + diff --git a/tools/Makefile.am b/tools/Makefile.am index 0f73f61c..1a8347b6 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -31,7 +31,8 @@ bin_PROGRAMS = \ hal-find-by-property \ hal-device \ hal-is-caller-locked-out \ - hal-lock + hal-lock \ + hal-disable-polling lshal_SOURCES = lshal.c lshal_LDADD = @GLIB_LIBS@ $(top_builddir)/libhal/libhal.la @@ -57,6 +58,9 @@ hal_is_caller_locked_out_LDADD = @DBUS_LIBS@ $(top_builddir)/libhal/libhal.la hal_lock_SOURCES = hal-lock.c hal_lock_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ $(top_builddir)/libhal/libhal.la +hal_disable_polling_SOURCES = hal-disable-polling.c +hal_disable_polling_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ $(top_builddir)/libhal/libhal.la + libexec_PROGRAMS = \ hal-storage-mount \ hal-storage-unmount \ diff --git a/tools/hal-disable-polling.c b/tools/hal-disable-polling.c new file mode 100644 index 00000000..44d886cf --- /dev/null +++ b/tools/hal-disable-polling.c @@ -0,0 +1,276 @@ +/*************************************************************************** + * CVSID: $Id$ + * + * hal-disable-polling.c : Disable polling on a drive + * + * Copyright (C) 2007 David Zeuthen, <david@fubar.dk> + * + * Licensed under the Academic Free License version 2.1 + * + * This program 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + **************************************************************************/ + + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <getopt.h> +#include <glib.h> +#include <libhal.h> + +/** + * usage: + * @argc: Number of arguments given to program + * @argv: Arguments given to program + * + * Print out program usage. + */ +static void +usage (int argc, char *argv[]) +{ + fprintf (stderr, + "\n" + "usage : hal-disable-polling [--udi <udi> | --device <device-file>]\n" + " [--enable-polling]\n" + " [--help] [--version]\n"); + fprintf (stderr, + "\n" + " --udi Unique Device Id\n" + " --device Device file\n" + " --enable-polling Enable polling instead of disabling it\n" + " --version Show version and exit\n" + " --help Show this information and exit\n" + "\n" + "This program is provided to make HAL stop polling a drive. Please read.\n" + "the entire manual page before using this program.\n" + "\n"); +} + +/** + * main: + * @argc: Number of arguments given to program + * @argv: Arguments given to program + * + * Returns: Return code + * + * Main entry point + */ +int +main (int argc, char *argv[]) +{ + char *udi = NULL; + char *device = NULL; + dbus_bool_t is_version = FALSE; + dbus_bool_t enable_polling = FALSE; + DBusError error; + LibHalContext *hal_ctx; + FILE *f; + char *filename; + + if (argc <= 1) { + usage (argc, argv); + return 1; + } + + while (1) { + int c; + int option_index = 0; + const char *opt; + static struct option long_options[] = { + {"udi", 1, NULL, 0}, + {"device", 1, NULL, 0}, + {"enable-polling", 0, NULL, 0}, + {"version", 0, NULL, 0}, + {"help", 0, NULL, 0}, + {NULL, 0, NULL, 0} + }; + + c = getopt_long (argc, argv, "", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 0: + opt = long_options[option_index].name; + + if (strcmp (opt, "help") == 0) { + usage (argc, argv); + return 0; + } else if (strcmp (opt, "version") == 0) { + is_version = TRUE; + } else if (strcmp (opt, "udi") == 0) { + udi = strdup (optarg); + } else if (strcmp (opt, "device") == 0) { + device = strdup (optarg); + } else if (strcmp (opt, "enable-polling") == 0) { + enable_polling = TRUE; + } + break; + + default: + usage (argc, argv); + return 1; + break; + } + } + + if (is_version) { + printf ("hal-disable-polling " PACKAGE_VERSION "\n"); + return 0; + } + + if (udi == NULL && device == NULL) { + usage (argc, argv); + return 1; + } + + if (udi != NULL && device != NULL) { + usage (argc, argv); + return 1; + } + + dbus_error_init (&error); + if ((hal_ctx = libhal_ctx_new ()) == NULL) { + fprintf (stderr, "error: libhal_ctx_new\n"); + return 1; + } + if (!libhal_ctx_set_dbus_connection (hal_ctx, dbus_bus_get (DBUS_BUS_SYSTEM, &error))) { + fprintf (stderr, "error: libhal_ctx_set_dbus_connection: %s: %s\n", error.name, error.message); + LIBHAL_FREE_DBUS_ERROR (&error); + return 1; + } + if (!libhal_ctx_init (hal_ctx, &error)) { + if (dbus_error_is_set(&error)) { + fprintf (stderr, "error: libhal_ctx_init: %s: %s\n", error.name, error.message); + dbus_error_free (&error); + } + fprintf (stderr, "Could not initialise connection to hald.\n" + "Normally this means the HAL daemon (hald) is not running or not ready.\n"); + return 1; + } + + if (getuid () != 0) { + fprintf (stderr, "This program requires super user (root) privileges.\n"); + return 1; + } + + if (device != NULL) { + char **devices; + int num_devices; + int n; + + devices = libhal_manager_find_device_string_match (hal_ctx, "block.device", device, &num_devices, NULL); + if (devices == NULL) { + fprintf (stderr, "Cannot find device %s.\n", device); + return 1; + } + + for (n = 0; devices[n] != NULL; n++) { + if (libhal_device_query_capability (hal_ctx, devices[n], "storage", NULL)) { + udi = devices[n]; + break; + } + } + + if (udi == NULL) { + fprintf (stderr, "Cannot find storage device %s.\n", device); + return 1; + } + + /* mmmkay, we don't care about leaking the variable devices... mmkay? mmkay! */ + } else { + if (!libhal_device_exists (hal_ctx, udi, &error)) { + fprintf (stderr, "Cannot find device with udi %s.\n", udi); + return 1; + } + if (!libhal_device_query_capability (hal_ctx, udi, "storage", NULL)) { + fprintf (stderr, "Device with udi %s is not a storage device.\n", udi); + return 1; + } + device = libhal_device_get_property_string (hal_ctx, udi, "block.device", NULL); + if (device == NULL) { + fprintf (stderr, "Device with udi %s does not have block.device set.\n", udi); + return 1; + } + } + + if (!libhal_device_get_property_bool (hal_ctx, udi, "storage.removable", NULL)) { + fprintf (stderr, "The given drive don't use removable media so it's not polled anyway.\n"); + return 1; + } + + filename = g_strdup_printf (PACKAGE_SYSCONF_DIR "/hal/fdi/information/media-check-disable-%s.fdi", + g_basename (udi)); + + if (enable_polling) { + if (libhal_device_get_property_bool (hal_ctx, udi, "storage.media_check_enabled", NULL)) { + fprintf (stderr, "Polling is already enabled on the given drive.\n"); + return 1; + } + + if (!g_file_test (filename, G_FILE_TEST_EXISTS)) { + fprintf (stderr, "Cannot find fdi file %s. Perhaps polling wasn't disabled using this tool?\n", filename); + return 1; + } + if (unlink (filename) != 0) { + fprintf (stderr, "Cannot delete fdi file %s.\n", filename); + return 1; + } + } else { + if (!libhal_device_get_property_bool (hal_ctx, udi, "storage.media_check_enabled", NULL)) { + fprintf (stderr, "Polling is already disabled on the given drive.\n"); + return 1; + } + + if (g_file_test (filename, G_FILE_TEST_EXISTS)) { + fprintf (stderr, "The fdi file %s already exist. Cowardly refusing to overwrite it.\n", filename); + return 1; + } + + f = fopen (filename, "w"); + if (f == NULL) { + fprintf (stderr, "Cannot open %s for writing.\n", filename); + return 1; + } + fprintf (f, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "\n" + "<deviceinfo version=\"0.2\">\n" + " <device>\n" + " <match key=\"info.udi\" string=\"%s\">\n" + " <merge key=\"storage.media_check_enabled\" type=\"bool\">false</merge>\n" + " </match>\n" + " </device>\n" + "</deviceinfo>\n" + "\n", udi); + fclose (f); + } + + libhal_device_reprobe (hal_ctx, udi, &error); + + if (enable_polling) + printf ("Polling for drive %s have been enabled. The fdi file deleted was\n" + " %s\n", device, filename); + else + printf ("Polling for drive %s have been disabled. The fdi file written was\n" + " %s\n", device, filename); + + return 0; +} |