Submitted By: Joe Ciccone Date: 2003-08-23 Initial Package Version: 2.10.1 Origin: Fedora Core CVS Description: Without this patch nautilus-cd-burner has problems compiling with some versions of hal diff -Naur nautilus-cd-burner-2.10.1.orig/nautilus-burn-drive.c nautilus-cd-burner-2.10.1/nautilus-burn-drive.c --- nautilus-cd-burner-2.10.1.orig/nautilus-burn-drive.c 2005-08-23 22:05:12.000000000 +0000 +++ nautilus-cd-burner-2.10.1/nautilus-burn-drive.c 2005-08-23 22:05:29.000000000 +0000 @@ -145,18 +145,38 @@ get_hal_context (void) { static LibHalContext *ctx = NULL; - LibHalFunctions hal_functions = { - NULL, /* mainloop integration */ - NULL, /* device_added */ - NULL, /* device_removed */ - NULL, /* device_new_capability */ - NULL, /* device_lost_capability */ - NULL, /* property_modified */ - NULL, /* device_condition */ - }; + DBusError error; + DBusConnection *dbus_conn; + + if (ctx == NULL) { + ctx = libhal_ctx_new (); + if (ctx == NULL) { + g_warning ("Could not create a HAL context\n"); + } else { + dbus_error_init (&error); + dbus_conn = dbus_bus_get (DBUS_BUS_SYSTEM, &error); + + if (dbus_error_is_set (&error)) { + g_warning ("Could not connect to system bus: %s\n", error.message); + dbus_error_free (&error); + return NULL; + } + + libhal_ctx_set_dbus_connection (ctx, dbus_conn); + + if (!libhal_ctx_init (ctx, &error)) { + g_warning ("Could not initalize " + "the HAL context: %s\n", + error.message); + + if (dbus_error_is_set (&error)) + dbus_error_free (&error); - if (ctx == NULL) - ctx = hal_initialize (&hal_functions, FALSE); + libhal_ctx_free (ctx); + ctx = NULL; + } + } + } return ctx; } @@ -527,43 +547,66 @@ int num_devices; NautilusBurnMediaType type; char *hal_type; + DBusError error; ctx = get_hal_context (); + + dbus_error_init (&error); if (ctx != NULL) { - device_names = hal_manager_find_device_string_match (ctx, - "info.parent", - drive->priv->udi, - &num_devices); - if (num_devices == 0) { + device_names = libhal_manager_find_device_string_match (ctx, + "info.parent", + drive->priv->udi, + &num_devices, + &error); + + if (dbus_error_is_set (&error)) { + g_warning ("%s\n", error.message); + dbus_error_free (&error); return NAUTILUS_BURN_MEDIA_TYPE_ERROR; } + if (num_devices == 0) + return NAUTILUS_BURN_MEDIA_TYPE_ERROR; + + /* just look at the first child */ - if (hal_device_get_property_bool (ctx, - device_names [0], - "volume.is_mounted")) { + if (libhal_device_get_property_bool (ctx, + device_names [0], + "volume.is_mounted", + NULL)) { type = NAUTILUS_BURN_MEDIA_TYPE_BUSY; - } else { + } else { + if (is_rewritable) - *is_rewritable = hal_device_get_property_bool (ctx, - device_names [0], - "volume.disc.is_rewritable"); + *is_rewritable = libhal_device_get_property_bool (ctx, + device_names [0], + "volume.disc.is_rewritable", + NULL); + if (is_blank) - *is_blank = hal_device_get_property_bool (ctx, - device_names [0], - "volume.disc.is_blank"); + *is_blank = libhal_device_get_property_bool (ctx, + device_names [0], + "volume.disc.is_blank", + NULL); + if (has_data) - *has_data = hal_device_get_property_bool (ctx, - device_names [0], - "volume.disc.has_data"); + *has_data = libhal_device_get_property_bool (ctx, + device_names [0], + "volume.disc.has_data", + NULL); + if (has_audio) - *has_audio = hal_device_get_property_bool (ctx, - device_names [0], - "volume.disc.has_audio"); + *has_audio = libhal_device_get_property_bool (ctx, + device_names [0], + "volume.disc.has_audio", + NULL); + type = NAUTILUS_BURN_MEDIA_TYPE_BUSY; - hal_type = hal_device_get_property_string (ctx, - device_names [0], - "volume.disc.type"); + hal_type = libhal_device_get_property_string (ctx, + device_names [0], + "volume.disc.type", + NULL); + if (hal_type == NULL || strcmp (hal_type, "unknown") == 0) { type = NAUTILUS_BURN_MEDIA_TYPE_UNKNOWN; } else if (strcmp (hal_type, "cd_rom") == 0) { @@ -589,10 +632,10 @@ } if (hal_type != NULL) - hal_free_string (hal_type); + libhal_free_string (hal_type); } - hal_free_string_array (device_names); + libhal_free_string_array (device_names); return type; } @@ -895,7 +938,7 @@ #ifdef USE_HAL -#define GET_BOOL_PROP(x) (hal_device_property_exists (ctx, device_names [i], x) && hal_device_get_property_bool (ctx, device_names [i], x)) +#define GET_BOOL_PROP(x) (libhal_device_property_exists (ctx, device_names [i], x, NULL) && libhal_device_get_property_bool (ctx, device_names [i], x, NULL)) static GList * hal_scan (gboolean recorder_only) @@ -911,8 +954,10 @@ return NULL; } - device_names = hal_find_device_by_capability (ctx, - "storage.cdrom", &num_devices); + device_names = libhal_find_device_by_capability (ctx, + "storage.cdrom", + &num_devices, + NULL); if (device_names == NULL) return NULL; @@ -933,47 +978,61 @@ if (GET_BOOL_PROP ("storage.cdrom.cdrw")) { drive->type |= NAUTILUS_BURN_DRIVE_TYPE_CDRW_RECORDER; } + if (GET_BOOL_PROP ("storage.cdrom.dvd")) { drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_DRIVE; if (GET_BOOL_PROP ("storage.cdrom.dvdram")) { drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_RAM_RECORDER; } + if (GET_BOOL_PROP ("storage.cdrom.dvdr")) { drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_RW_RECORDER; } + if (GET_BOOL_PROP ("storage.cdrom.dvd")) { drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_DRIVE; } + if (GET_BOOL_PROP ("storage.cdrom.dvdplusr")) { drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_R_RECORDER; } + if (GET_BOOL_PROP ("storage.cdrom.dvdplusrw")) { drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_RW_RECORDER; } + } - drive->device = hal_device_get_property_string (ctx, - device_names [i], "block.device"); + drive->device = libhal_device_get_property_string (ctx, + device_names [i], + "block.device", + NULL); + drive->cdrecord_id = g_strdup (drive->device); - string = hal_device_get_property_string (ctx, - device_names [i], "storage.model"); + string = libhal_device_get_property_string (ctx, + device_names [i], + "storage.model", + NULL); + if (string != NULL) { drive->display_name = string; } else { drive->display_name = g_strdup_printf ("Unnamed Drive (%s)", drive->device); } - drive->max_speed_read = hal_device_get_property_int - (ctx, device_names [i], "storage.cdrom.read_speed") + drive->max_speed_read = libhal_device_get_property_int + (ctx, device_names [i], "storage.cdrom.read_speed", NULL) / CD_ROM_SPEED; - if (hal_device_property_exists (ctx, device_names [i], "storage.cdrom.write_speed")) { - drive->max_speed_write = hal_device_get_property_int + if (libhal_device_property_exists (ctx, device_names [i], "storage.cdrom.write_speed", NULL)) { + drive->max_speed_write = libhal_device_get_property_int (ctx, device_names [i], - "storage.cdrom.write_speed") + "storage.cdrom.write_speed", + NULL) / CD_ROM_SPEED; + } add_whitelist (drive); @@ -987,7 +1046,7 @@ drive->priv->udi = g_strdup (device_names [i]); } - hal_free_string_array (device_names); + libhal_free_string_array (device_names); drives = g_list_reverse (drives); @@ -1904,13 +1963,20 @@ if (drive->priv->udi != NULL) { LibHalContext *ctx; char *dbus_reason; + DBusError error; + dbus_error_init (&error); ctx = get_hal_context (); if (ctx != NULL) { - res = hal_device_lock (ctx, + res = libhal_device_lock (ctx, drive->priv->udi, reason, - &dbus_reason); + &dbus_reason, + &error); + + if (dbus_error_is_set (&error)) + dbus_error_free (&error); + if (dbus_reason != NULL && reason_for_failure != NULL) *reason_for_failure = g_strdup (dbus_reason); @@ -1941,11 +2007,17 @@ #ifdef USE_HAL if (drive->priv->udi != NULL) { LibHalContext *ctx; + DBusError error; + dbus_error_init (&error); ctx = get_hal_context (); if (ctx != NULL) { - res = hal_device_unlock (ctx, - drive->priv->udi); + res = libhal_device_unlock (ctx, + drive->priv->udi, + &error); + + if (dbus_error_is_set (&error)) + dbus_error_free (&error); } } #endif diff -Naur nautilus-cd-burner-2.10.1.orig/nautilus-burn-drive.c~ nautilus-cd-burner-2.10.1/nautilus-burn-drive.c~ --- nautilus-cd-burner-2.10.1.orig/nautilus-burn-drive.c~ 1970-01-01 00:00:00.000000000 +0000 +++ nautilus-cd-burner-2.10.1/nautilus-burn-drive.c~ 2005-03-29 01:09:59.000000000 +0000 @@ -0,0 +1,2027 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- + * + * cd-drive.c: easy to use cd burner software + * + * Copyright (C) 2002-2004 Red Hat, Inc. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: Alexander Larsson + * Bastien Nocera + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef INVALID_HANDLE +#define INVALID_HANDLE (GINT_TO_POINTER(-1)) +#endif + +#ifdef USE_HAL +#include +#endif /* USE_HAL */ + +#ifdef __linux__ +#include +#include +#include +#endif /* __linux__ */ + +#ifdef __FreeBSD__ +#include +#include +#include +#endif /* __FreeBSD__ */ + +#include "nautilus-burn-drive.h" + +/* For dvd_plus_rw_utils.cpp */ +int get_dvd_r_rw_profile (const char *name); +int get_mmc_profile (int fd); +int get_disc_size_cd (int fd); +gint64 get_disc_size_dvd (int fd, int mmc_profile); +int get_read_write_speed (int fd, int *read_speed, int *write_speed); +int get_disc_status (int fd, int *empty, int *is_rewritable, int *is_blank); + +#define CD_ROM_SPEED 176 + +static struct { + const char *name; + gboolean can_write_cdr; + gboolean can_write_cdrw; + gboolean can_write_dvdr; + gboolean can_write_dvdram; +} recorder_whitelist [] = { + { "IOMEGA - CDRW9602EXT-B", TRUE, TRUE, FALSE, FALSE }, + { "SONY - CD-R CDU948S", TRUE, FALSE, FALSE, FALSE }, +}; + +typedef enum { + NAUTILUS_BURN_DRIVE_PROTOCOL_IDE, + NAUTILUS_BURN_DRIVE_PROTOCOL_SCSI, +} NautilusBurnDriveProtocolType; + +struct NautilusBurnDrivePriv { + NautilusBurnDriveProtocolType protocol; + char *udi; +}; + +#ifdef __FreeBSD__ + +#define get_ioctl_handle_fd(x) (((struct cam_device *)x)->fd) + +static gpointer +open_ioctl_handle (const char *device) +{ + struct cam_device *cam; + + cam = cam_open_device (device, O_RDWR); + + return (cam ? (gpointer)cam : INVALID_HANDLE); +} + +static void +close_ioctl_handle (gpointer handle) +{ + cam_close_device ((struct cam_device *)handle); +} + +#else + +#define get_ioctl_handle_fd(x) (GPOINTER_TO_INT(x)) + +static gpointer +open_ioctl_handle (const char *device) +{ + int fd; + + if ((fd = open (device, O_RDWR | O_EXCL | O_NONBLOCK)) < 0 + && (fd = open (device, O_RDONLY | O_EXCL | O_NONBLOCK)) < 0) { + return INVALID_HANDLE; + } + + return GINT_TO_POINTER (fd); +} + +static void +close_ioctl_handle (gpointer handle) +{ + close (GPOINTER_TO_INT (handle)); +} + +#endif + +#ifdef USE_HAL +static LibHalContext * +get_hal_context (void) +{ + static LibHalContext *ctx = NULL; + LibHalFunctions hal_functions = { + NULL, /* mainloop integration */ + NULL, /* device_added */ + NULL, /* device_removed */ + NULL, /* device_new_capability */ + NULL, /* device_lost_capability */ + NULL, /* property_modified */ + NULL, /* device_condition */ + }; + + if (ctx == NULL) + ctx = hal_initialize (&hal_functions, FALSE); + + return ctx; +} +#endif /* USE_HAL */ + +static int +get_device_max_write_speed (char *device) +{ + gpointer ioctl_handle; + int fd; + int max_speed; + int read_speed, write_speed; + + max_speed = -1; + + ioctl_handle = open_ioctl_handle (device); + if (ioctl_handle == INVALID_HANDLE) { + return -1; + } + + fd = get_ioctl_handle_fd (ioctl_handle); + get_read_write_speed (fd, &read_speed, &write_speed); + close_ioctl_handle (ioctl_handle); + max_speed = (int)floor (write_speed) / CD_ROM_SPEED; + + return max_speed; +} + +/* Utility functions, be careful to have a match with what's use in the + * different bits of code */ +#if !defined (__linux__) + +static int +get_device_max_read_speed (char *device) +{ + gpointer ioctl_handle; + int fd; + int max_speed; + int read_speed, write_speed; + + max_speed = -1; + + ioctl_handle = open_ioctl_handle (device); + if (ioctl_handle == INVALID_HANDLE) { + return -1; + } + + fd = get_ioctl_handle_fd (ioctl_handle); + get_read_write_speed (fd, &read_speed, &write_speed); + close_ioctl_handle (ioctl_handle); + max_speed = (int)floor (read_speed) / CD_ROM_SPEED; + + return max_speed; +} + +static char * +cdrecord_get_stdout_for_id (char *id) +{ + int max_speed, i; + const char *argv [20]; /* Shouldn't need more than 20 arguments */ + char *dev_str, *stdout_data; + + max_speed = -1; + + i = 0; + argv [i++] = "cdrecord"; + argv [i++] = "-prcap"; + dev_str = g_strdup_printf ("dev=%s", id); + argv [i++] = dev_str; + argv [i++] = NULL; + + if (g_spawn_sync (NULL, + (char **)argv, + NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL, + NULL, NULL, + &stdout_data, + NULL, + NULL, + NULL)) { + g_free (dev_str); + return stdout_data; + } + + g_free (dev_str); + return NULL; +} + +static void +get_cd_properties (char *device, + char *id, + int *max_rd_speed, + int *max_wr_speed, + NautilusBurnDriveType *type) +{ + char *stdout_data, *drive_cap; + + *max_rd_speed = -1; + *max_wr_speed = -1; + *type = 0; + + *max_rd_speed = get_device_max_read_speed (device); + *max_wr_speed = get_device_max_write_speed (device); + + stdout_data = cdrecord_get_stdout_for_id (id); + if (stdout_data == NULL) { + return; + } + drive_cap = strstr (stdout_data, "Does write DVD-RAM media"); + if (drive_cap != NULL) { + *type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_RAM_RECORDER; + } + drive_cap = strstr (stdout_data, "Does read DVD-R media"); + if (drive_cap != NULL) { + *type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_RW_RECORDER; + } + drive_cap = strstr (stdout_data, "Does read DVD-ROM media"); + if (drive_cap != NULL) { + *type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_DRIVE; + } + drive_cap = strstr (stdout_data, "Does write CD-RW media"); + if (drive_cap != NULL) { + *type |= NAUTILUS_BURN_DRIVE_TYPE_CDRW_RECORDER; + } + drive_cap = strstr (stdout_data, "Does write CD-R media"); + if (drive_cap != NULL) { + *type |= NAUTILUS_BURN_DRIVE_TYPE_CD_RECORDER; + } + drive_cap = strstr (stdout_data, "Does read CD-R media"); + if (drive_cap != NULL) { + *type |= NAUTILUS_BURN_DRIVE_TYPE_CD_DRIVE; + } + g_free (stdout_data); +} +#endif /* !__linux__ */ + +#if !(defined (__linux__)) || (defined (__linux__) && (defined (USE_HAL))) +static void +add_whitelist (NautilusBurnDrive *drive) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (recorder_whitelist); i++) { + if (!strcmp (drive->display_name, recorder_whitelist [i].name)) { + if (recorder_whitelist [i].can_write_cdr) { + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_CD_RECORDER; + } + if (recorder_whitelist [i].can_write_cdrw) { + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_CDRW_RECORDER; + } + if (recorder_whitelist [i].can_write_dvdr) { + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_RW_RECORDER; + } + if (recorder_whitelist [i].can_write_dvdram) { + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_RAM_RECORDER; + } + } + } +} +#endif /* !__linux__ */ + +static void +add_dvd_plus (NautilusBurnDrive *drive) +{ + int caps; + + caps = get_dvd_r_rw_profile (drive->device); + + if (caps == -1) { + return; + } + + if (caps == 2) { + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_RW_RECORDER; + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_R_RECORDER; + } else if (caps == 0) { + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_R_RECORDER; + } else if (caps == 1) { + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_RW_RECORDER; + } +} + +static gboolean +nautilus_burn_drive_door_open (gboolean mmc_profile, + int fd) +{ +#ifdef __linux__ + { + int status; + + status = ioctl (fd, CDROM_DRIVE_STATUS, CDSL_CURRENT); + if (status < 0) { + return FALSE; + } + + return status == CDS_TRAY_OPEN; + } +#else + if (mmc_profile == 0) { + return TRUE; + } + + return FALSE; +#endif +} + +static NautilusBurnMediaType +nautilus_burn_drive_get_media_type_from_path_full (const char *device, + gboolean *is_rewritable, + gboolean *is_blank, + gboolean *has_data, + gboolean *has_audio) +{ + gpointer ioctl_handle; + int fd; + int mmc_profile; + + g_return_val_if_fail (device != NULL, NAUTILUS_BURN_MEDIA_TYPE_ERROR); + + if (is_rewritable) *is_rewritable = FALSE; + if (is_blank) *is_blank = FALSE; + if (has_data) *has_data = FALSE; + if (has_audio) *has_audio = FALSE; + + ioctl_handle = open_ioctl_handle (device); + if (ioctl_handle == INVALID_HANDLE) { + if (errno == EBUSY) { + return NAUTILUS_BURN_MEDIA_TYPE_BUSY; + } + return NAUTILUS_BURN_MEDIA_TYPE_ERROR; + } + + fd = get_ioctl_handle_fd (ioctl_handle); + mmc_profile = get_mmc_profile (fd); + + /* Couldn't get the data about the media */ + if (mmc_profile < 0) { + gboolean opened; + + opened = nautilus_burn_drive_door_open (mmc_profile, fd); + + if (opened != FALSE) { + close_ioctl_handle (ioctl_handle); + return NAUTILUS_BURN_MEDIA_TYPE_ERROR; + } else { + int blank, rewrite, empty; + if (get_disc_status (fd, &empty, &rewrite, &blank) == 0) { + close_ioctl_handle (ioctl_handle); + + if (is_rewritable) + *is_rewritable = rewrite; + if (is_blank) + *is_blank = blank; + if (has_data) + *has_data = !blank; + if (has_audio) { + /* FIXME */ + *has_audio = FALSE; + } + if (empty) + return NAUTILUS_BURN_MEDIA_TYPE_ERROR; + else + return NAUTILUS_BURN_MEDIA_TYPE_UNKNOWN; + } + + close_ioctl_handle (ioctl_handle); + + return NAUTILUS_BURN_MEDIA_TYPE_UNKNOWN; + } + } + + close_ioctl_handle (ioctl_handle); + + if (is_blank) + *is_blank = mmc_profile & 0x10000; + if (has_data) + *has_data = !(mmc_profile & 0x10000); + if (has_audio) { + /* FIXME */ + *has_audio = FALSE; + } + + switch (mmc_profile & 0xFFFF) { + case -1: + g_assert_not_reached (); + case 0: /* No Media */ + return NAUTILUS_BURN_MEDIA_TYPE_ERROR; + case 0x8: /* Commercial CDs and Audio CD */ + return NAUTILUS_BURN_MEDIA_TYPE_CD; + case 0x9: /* CD-R */ + return NAUTILUS_BURN_MEDIA_TYPE_CDR; + case 0xa: /* CD-RW */ + *is_rewritable = TRUE; + return NAUTILUS_BURN_MEDIA_TYPE_CDRW; + case 0x10: /* Commercial DVDs */ + return NAUTILUS_BURN_MEDIA_TYPE_DVD; + case 0x11: /* DVD-R */ + return NAUTILUS_BURN_MEDIA_TYPE_DVDR; + case 0x13: /* DVD-RW Restricted Overwrite */ + case 0x14: /* DVD-RW Sequential */ + *is_rewritable = TRUE; + return NAUTILUS_BURN_MEDIA_TYPE_DVDRW; + case 0x1B: /* DVD+R */ + return NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_R; + case 0x1A: /* DVD+RW */ + *is_rewritable = TRUE; + return NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_RW; + case 0x12: /* DVD-RAM */ + return NAUTILUS_BURN_MEDIA_TYPE_DVD_RAM; + default: + return NAUTILUS_BURN_MEDIA_TYPE_UNKNOWN; + } +} + +/** + * nautilus_burn_drive_get_media_size_from_path: + * @device: Filename of drive device (eg. "/dev/hdb") + * + * Determine the capacity of the media in the drive at device @device. + * + * Return value: The capcity in bytes of the media in the drive or the + * following special values: + * + * %NAUTILUS_BURN_MEDIA_SIZE_UNKNOWN if the size can not be determined + * %NAUTILUS_BURN_MEDIA_SIZE_BUSY if the device is busy + * %NAUTILUS_BURN_MEDIA_SIZE_NA if the device is unknown + **/ +NautilusBurnMediaType +nautilus_burn_drive_get_media_type_from_path (const char *device) +{ + g_return_val_if_fail (device != NULL, NAUTILUS_BURN_MEDIA_TYPE_ERROR); + + return nautilus_burn_drive_get_media_type_from_path_full (device, NULL, NULL, NULL, NULL); +} + +/** + * nautilus_burn_drive_get_media_type: + * @drive: #NautilusBurnDrive + * + * Get the type of the specified drive. + * + * Return value: See nautilus_burn_drive_get_media_type_from_path() for details. + **/ +NautilusBurnMediaType +nautilus_burn_drive_get_media_type (NautilusBurnDrive *drive) +{ + g_return_val_if_fail (drive != NULL, NAUTILUS_BURN_MEDIA_TYPE_ERROR); + + return nautilus_burn_drive_get_media_type_full (drive, NULL, NULL, NULL, NULL); +} + +#ifdef USE_HAL +static NautilusBurnMediaType +nautilus_burn_drive_hal_get_media_type_full (NautilusBurnDrive *drive, + gboolean *is_rewritable, + gboolean *is_blank, + gboolean *has_data, + gboolean *has_audio) +{ + if (is_rewritable) *is_rewritable = FALSE; + if (is_blank) *is_blank = FALSE; + if (has_data) *has_data = FALSE; + if (has_audio) *has_audio = FALSE; + + if (drive->priv != NULL && drive->priv->udi != NULL) { + LibHalContext *ctx; + char **device_names; + int num_devices; + NautilusBurnMediaType type; + char *hal_type; + + ctx = get_hal_context (); + if (ctx != NULL) { + device_names = hal_manager_find_device_string_match (ctx, + "info.parent", + drive->priv->udi, + &num_devices); + if (num_devices == 0) { + return NAUTILUS_BURN_MEDIA_TYPE_ERROR; + } + + /* just look at the first child */ + if (hal_device_get_property_bool (ctx, + device_names [0], + "volume.is_mounted")) { + type = NAUTILUS_BURN_MEDIA_TYPE_BUSY; + } else { + if (is_rewritable) + *is_rewritable = hal_device_get_property_bool (ctx, + device_names [0], + "volume.disc.is_rewritable"); + if (is_blank) + *is_blank = hal_device_get_property_bool (ctx, + device_names [0], + "volume.disc.is_blank"); + if (has_data) + *has_data = hal_device_get_property_bool (ctx, + device_names [0], + "volume.disc.has_data"); + if (has_audio) + *has_audio = hal_device_get_property_bool (ctx, + device_names [0], + "volume.disc.has_audio"); + type = NAUTILUS_BURN_MEDIA_TYPE_BUSY; + hal_type = hal_device_get_property_string (ctx, + device_names [0], + "volume.disc.type"); + if (hal_type == NULL || strcmp (hal_type, "unknown") == 0) { + type = NAUTILUS_BURN_MEDIA_TYPE_UNKNOWN; + } else if (strcmp (hal_type, "cd_rom") == 0) { + type = NAUTILUS_BURN_MEDIA_TYPE_CD; + } else if (strcmp (hal_type, "cd_r") == 0) { + type = NAUTILUS_BURN_MEDIA_TYPE_CDR; + } else if (strcmp (hal_type, "cd_rw") == 0) { + type = NAUTILUS_BURN_MEDIA_TYPE_CDRW; + } else if (strcmp (hal_type, "dvd_rom") == 0) { + type = NAUTILUS_BURN_MEDIA_TYPE_DVD; + } else if (strcmp (hal_type, "dvd_r") == 0) { + type = NAUTILUS_BURN_MEDIA_TYPE_DVDR; + } else if (strcmp (hal_type, "dvd_ram") == 0) { + type = NAUTILUS_BURN_MEDIA_TYPE_DVD_RAM; + } else if (strcmp (hal_type, "dvd_rw") == 0) { + type = NAUTILUS_BURN_MEDIA_TYPE_DVDRW; + } else if (strcmp (hal_type, "dvd_plus_rw") == 0) { + type = NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_RW; + } else if (strcmp (hal_type, "dvd_plus_r") == 0) { + type = NAUTILUS_BURN_MEDIA_TYPE_DVD_PLUS_R; + } else { + type = NAUTILUS_BURN_MEDIA_TYPE_UNKNOWN; + } + + if (hal_type != NULL) + hal_free_string (hal_type); + } + + hal_free_string_array (device_names); + + return type; + } + } + + return nautilus_burn_drive_get_media_type_from_path_full (drive->device, + is_rewritable, + is_blank, + has_data, + has_audio); +} +#endif + +/** + * nautilus_burn_drive_get_media_type_full: + * @drive: #NautilusBurnDrive + * @is_rewritable: set to TRUE if media is rewritable + * @is_blank: set to TRUE if media is blank + * @has_data: set to TRUE if media has data + * @has_audio: set to TRUE if media has audio + * + * Get the type of the specified drive. + * + * Return value: See nautilus_burn_drive_get_media_type_from_path() for details. + **/ +NautilusBurnMediaType +nautilus_burn_drive_get_media_type_full (NautilusBurnDrive *drive, + gboolean *is_rewritable, + gboolean *is_blank, + gboolean *has_data, + gboolean *has_audio) +{ + g_return_val_if_fail (drive != NULL, NAUTILUS_BURN_MEDIA_TYPE_ERROR); + +#ifdef USE_HAL + return nautilus_burn_drive_hal_get_media_type_full (drive, + is_rewritable, + is_blank, + has_data, + has_audio); +#else + return nautilus_burn_drive_get_media_type_from_path_full (drive->device, + is_rewritable, + is_blank, + has_data, + has_audio); +#endif +} + +/** + * nautilus_burn_drive_get_media_type_and_rewritable: + * @drive: #NautilusBurnDrive + * @is_rewritable: set to TRUE if drive is rewritable + * + * Get the type of the specified drive. + * + * Return value: See nautilus_burn_drive_get_media_type_from_path() for details. + **/ +NautilusBurnMediaType +nautilus_burn_drive_get_media_type_and_rewritable (NautilusBurnDrive *drive, + gboolean *is_rewritable) +{ + g_return_val_if_fail (drive != NULL, NAUTILUS_BURN_MEDIA_TYPE_ERROR); + + return nautilus_burn_drive_get_media_type_full (drive, is_rewritable, NULL, NULL, NULL); +} + +gint64 +nautilus_burn_drive_get_media_size_from_path (const char *device) +{ + gpointer ioctl_handle; + int fd; + int secs; + int mmc_profile; + gint64 size; + + g_return_val_if_fail (device != NULL, NAUTILUS_BURN_MEDIA_SIZE_UNKNOWN); + + secs = 0; + + ioctl_handle = open_ioctl_handle (device); + if (ioctl_handle == INVALID_HANDLE) { + if (errno == EBUSY) { + return NAUTILUS_BURN_MEDIA_SIZE_BUSY; + } + return NAUTILUS_BURN_MEDIA_SIZE_UNKNOWN; + } + + fd = get_ioctl_handle_fd (ioctl_handle); + mmc_profile = get_mmc_profile (fd); + + /* See nautilus_burn_drive_get_media_type_from_path for details */ + switch (mmc_profile & 0xFFFF) { + case 0x9: + case 0xa: + secs = get_disc_size_cd (fd); + size = (1 + secs * 7 / 48) * 1024 * 1024; + break; + case 0x11: + case 0x13: + case 0x14: + case 0x1B: + case 0x1A: + case 0x12: + size = get_disc_size_dvd (fd, mmc_profile); + break; + default: + size = NAUTILUS_BURN_MEDIA_SIZE_NA; + } + + close_ioctl_handle (ioctl_handle); + + return size; +} + +/** + * nautilus_burn_drive_get_media_size: + * @drive: #NautilusBurnDrive + * + * Determine the capacity of the media in the specified drive. + * + * Return value: See #nautilus_burn_drive_get_media_size_from_path for details. + **/ +gint64 +nautilus_burn_drive_get_media_size (NautilusBurnDrive *drive) +{ + g_return_val_if_fail (drive != NULL, NAUTILUS_BURN_MEDIA_SIZE_UNKNOWN); + + return nautilus_burn_drive_get_media_size_from_path (drive->device); +} + +typedef struct { + gboolean timeout; + gboolean unmount_ok; + guint timeout_tag; + GMainLoop *loop; + char *device; + const char *command; +} UnmountData; + +static const char *umount_known_locations [] = { + "/sbin/umount", + "/bin/umount", + "/usr/sbin/umount", + "/usr/bin/umount", + NULL +}; + +static void +free_unmount_data (UnmountData *unmount_data) +{ + g_free (unmount_data->device); + g_free (unmount_data); +} + +static gboolean +unmount_done (gpointer data) +{ + UnmountData *unmount_data; + unmount_data = data; + + if (unmount_data->timeout_tag != 0) { + g_source_remove (unmount_data->timeout_tag); + } + + if (unmount_data->loop != NULL && + g_main_loop_is_running (unmount_data->loop)) { + g_main_loop_quit (unmount_data->loop); + } + + if (unmount_data->timeout) { + /* We timed out, so unmount_data wasn't freed + at mainloop exit. */ + free_unmount_data (unmount_data); + } + + return FALSE; +} + +static gboolean +unmount_timeout (gpointer data) +{ + UnmountData *unmount_data; + unmount_data = data; + + /* We're sure, the callback hasn't been run, so just say + we were interrupted and return from the mainloop */ + + unmount_data->unmount_ok = FALSE; + unmount_data->timeout_tag = 0; + unmount_data->timeout = TRUE; + + if (g_main_loop_is_running (unmount_data->loop)) { + g_main_loop_quit (unmount_data->loop); + } + + return FALSE; +} + +/* Returns the full path to the queried command */ +static const char * +find_command (const char **known_locations) +{ + int i; + + for (i = 0; known_locations [i]; i++){ + if (g_file_test (known_locations [i], G_FILE_TEST_EXISTS)) + return known_locations [i]; + } + return NULL; +} + +static void * +unmount_thread_start (void *arg) +{ + UnmountData *data; + gint exit_status; + char *argv [5]; + int i; + char *envp [] = { + "LC_ALL=C", + NULL + }; + + data = arg; + + data->unmount_ok = TRUE; + + i = 0; + argv [i++] = (char *)data->command; + argv [i++] = data->device; + argv [i++] = NULL; + + if (g_spawn_sync (NULL, + argv, + envp, + G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL, + NULL, NULL, + NULL, + NULL, + &exit_status, + NULL)) { + if (exit_status == 0) { + data->unmount_ok = TRUE; + } else { + data->unmount_ok = FALSE; + } + + /* Delay a bit to make sure unmount finishes */ + sleep (2); + } else { + /* spawn failure */ + data->unmount_ok = FALSE; + } + + g_idle_add (unmount_done, data); + + g_thread_exit (NULL); + + return NULL; +} + +gboolean +nautilus_burn_drive_unmount (NautilusBurnDrive *drive) +{ + UnmountData *data; + gboolean unmount_ok; + + if (drive->device == NULL) + return FALSE; + + unmount_ok = FALSE; + + data = g_new0 (UnmountData, 1); + data->loop = g_main_loop_new (NULL, FALSE); + + data->timeout_tag = g_timeout_add (5*1000, + unmount_timeout, + data); + data->command = find_command (umount_known_locations); + data->device = g_strdup (drive->device); + g_thread_create (unmount_thread_start, data, FALSE, NULL); + + GDK_THREADS_LEAVE (); + g_main_loop_run (data->loop); + GDK_THREADS_ENTER (); + + g_main_loop_unref (data->loop); + data->loop = NULL; + + unmount_ok = data->unmount_ok; + + if (!data->timeout) { + /* Don't free data if mount operation still running. */ + free_unmount_data (data); + } + + return unmount_ok; +} + +#ifdef USE_HAL + +#define GET_BOOL_PROP(x) (hal_device_property_exists (ctx, device_names [i], x) && hal_device_get_property_bool (ctx, device_names [i], x)) + +static GList * +hal_scan (gboolean recorder_only) +{ + GList *drives = NULL; + int i; + int num_devices; + char** device_names; + LibHalContext *ctx; + + ctx = get_hal_context (); + if (ctx == NULL) { + return NULL; + } + + device_names = hal_find_device_by_capability (ctx, + "storage.cdrom", &num_devices); + + if (device_names == NULL) + return NULL; + + for (i = 0; i < num_devices; i++) { + NautilusBurnDrive *drive; + char *string; + gboolean is_cdr; + + /* Is it a CD burner? */ + is_cdr = GET_BOOL_PROP ("storage.cdrom.cdr"); + + drive = nautilus_burn_drive_new (); + drive->type = NAUTILUS_BURN_DRIVE_TYPE_CD_DRIVE; + if (is_cdr != FALSE) { + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_CD_RECORDER; + } + if (GET_BOOL_PROP ("storage.cdrom.cdrw")) { + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_CDRW_RECORDER; + } + if (GET_BOOL_PROP ("storage.cdrom.dvd")) { + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_DRIVE; + + if (GET_BOOL_PROP ("storage.cdrom.dvdram")) { + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_RAM_RECORDER; + } + if (GET_BOOL_PROP ("storage.cdrom.dvdr")) { + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_RW_RECORDER; + } + if (GET_BOOL_PROP ("storage.cdrom.dvd")) { + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_DRIVE; + } + if (GET_BOOL_PROP ("storage.cdrom.dvdplusr")) { + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_R_RECORDER; + } + if (GET_BOOL_PROP ("storage.cdrom.dvdplusrw")) { + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_PLUS_RW_RECORDER; + } + } + + drive->device = hal_device_get_property_string (ctx, + device_names [i], "block.device"); + drive->cdrecord_id = g_strdup (drive->device); + + string = hal_device_get_property_string (ctx, + device_names [i], "storage.model"); + if (string != NULL) { + drive->display_name = string; + } else { + drive->display_name = g_strdup_printf ("Unnamed Drive (%s)", drive->device); + } + + drive->max_speed_read = hal_device_get_property_int + (ctx, device_names [i], "storage.cdrom.read_speed") + / CD_ROM_SPEED; + + if (hal_device_property_exists (ctx, device_names [i], "storage.cdrom.write_speed")) { + drive->max_speed_write = hal_device_get_property_int + (ctx, device_names [i], + "storage.cdrom.write_speed") + / CD_ROM_SPEED; + } + + add_whitelist (drive); + + if (recorder_only && !(drive->type & NAUTILUS_BURN_DRIVE_TYPE_CD_RECORDER)) { + nautilus_burn_drive_free (drive); + } else { + drives = g_list_prepend (drives, drive); + } + + drive->priv->udi = g_strdup (device_names [i]); + } + + hal_free_string_array (device_names); + + drives = g_list_reverse (drives); + + return drives; +} +#endif /* USE_HAL */ + +#if defined (__linux__) + +static char ** +read_lines (char *filename) +{ + char *contents; + gsize len; + char *p, *n; + GPtrArray *array; + + if (g_file_get_contents (filename, + &contents, + &len, NULL)) { + + array = g_ptr_array_new (); + + p = contents; + while ((n = memchr (p, '\n', len - (p - contents))) != NULL) { + *n = 0; + g_ptr_array_add (array, g_strdup (p)); + p = n + 1; + } + if ((gsize)(p - contents) < len) { + g_ptr_array_add (array, g_strndup (p, len - (p - contents))); + } + + g_ptr_array_add (array, NULL); + + g_free (contents); + return (char **)g_ptr_array_free (array, FALSE); + } + return NULL; +} + +struct scsi_unit { + char *vendor; + char *model; + char *rev; + int bus; + int id; + int lun; + int type; +}; + +struct drive_unit { + NautilusBurnDriveProtocolType protocol; + char *device; + char *display_name; + int speed; + gboolean can_write_cdr; + gboolean can_write_cdrw; + gboolean can_write_dvdr; + gboolean can_write_dvdram; + gboolean can_read_dvd; +}; + +static char *drive_get_name (struct drive_unit *drive, struct scsi_unit *scsi_units, int n_scsi_units); + +static void +linux_add_whitelist (struct drive_unit *drive_s, + struct scsi_unit *scsi_units, + int n_scsi_units) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (recorder_whitelist); i++) { + if (drive_s->display_name == NULL) { + continue; + } + + if (!strcmp (drive_s->display_name, recorder_whitelist [i].name)) { + drive_s->can_write_cdr = + recorder_whitelist [i].can_write_cdr; + drive_s->can_write_cdrw = + recorder_whitelist [i].can_write_cdrw; + drive_s->can_write_dvdr = + recorder_whitelist [i].can_write_dvdr; + drive_s->can_write_dvdram = + recorder_whitelist [i].can_write_dvdram; + } + } +} + +static void +get_scsi_units (char **device_str, + char **devices, + struct scsi_unit *scsi_units) +{ + char vendor [9], model [17], rev [5]; + int host_no, access_count, queue_depth, device_busy, online, channel; + int scsi_id, scsi_lun, scsi_type; + int i, j; + + j = 0; + + for (i = 0; device_str [i] != NULL && devices[i] != NULL; i++) { + if (strcmp (device_str [i], "") == 0) { + continue; + } + if (sscanf (devices [i], "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d", + &host_no, + &channel, &scsi_id, &scsi_lun, &scsi_type, &access_count, &queue_depth, + &device_busy, &online) != 9) { + + g_warning ("Couldn't match line in /proc/scsi/sg/devices\n"); + continue; + } + if (scsi_type == 5) { /* TYPE_ROM (include/scsi/scsi.h) */ + if (sscanf (device_str [i], "%8c\t%16c\t%4c", + vendor, model, rev) != 3) { + g_warning ("Couldn't match line /proc/scsi/sg/device_strs\n"); + continue; + } + vendor [8] = '\0'; model [16] = '\0'; rev [4] = '\0'; + + scsi_units [j].vendor = g_strdup (g_strstrip (vendor)); + scsi_units [j].model = g_strdup (g_strstrip (model)); + scsi_units [j].rev = g_strdup (g_strstrip (rev)); + scsi_units [j].bus = host_no; + scsi_units [j].id = scsi_id; + scsi_units [j].lun = scsi_lun; + scsi_units [j].type = scsi_type; + + j++; + } + } +} + +static int +count_strings (char *p) +{ + int n_strings; + + n_strings = 0; + while (*p != 0) { + n_strings++; + while (*p != '\t' && *p != 0) { + p++; + } + if (*p == '\t') { + p++; + } + } + return n_strings; +} + +static int +get_cd_scsi_id (const char *dev, + int *bus, + int *id, + int *lun) +{ + int fd; + char *devfile; + struct { + long mux4; + long hostUniqueId; + } m_idlun; + + devfile = g_strdup_printf ("/dev/%s", dev); + fd = open (devfile, O_RDWR | O_NONBLOCK); + if (fd < 0) + fd = open (devfile, O_RDONLY | O_NONBLOCK); + + g_free (devfile); + + /* Avoid problems with Valgrind */ + memset (&m_idlun, 0, sizeof (m_idlun)); + *bus = *id = *lun = -1; + + if (fd < 0) { + g_warning ("Failed to open cd device %s\n", dev); + return 0; + } + + if (ioctl (fd, SCSI_IOCTL_GET_BUS_NUMBER, bus) < 0 || *bus < 0) { + g_warning ("Failed to get scsi bus nr\n"); + close (fd); + return 0; + } + + if (ioctl (fd, SCSI_IOCTL_GET_IDLUN, &m_idlun) < 0) { + g_warning ("Failed to get scsi id and lun\n"); + close(fd); + return 0; + } + *id = m_idlun.mux4 & 0xFF; + *lun = (m_idlun.mux4 >> 8) & 0xFF; + + close(fd); + return 1; +} + +static struct scsi_unit * +lookup_scsi_unit (int bus, + int id, + int lun, + struct scsi_unit *scsi_units, + int n_scsi_units) +{ + int i; + + for (i = 0; i < n_scsi_units; i++) { + if (scsi_units [i].bus == bus && + scsi_units [i].id == id && + scsi_units [i].lun == lun) { + return &scsi_units [i]; + } + } + return NULL; +} + +static char * +get_scsi_cd_name (int bus, + int id, + int lun, + const char *dev, + struct scsi_unit *scsi_units, + int n_scsi_units) +{ + struct scsi_unit *scsi_unit; + + scsi_unit = lookup_scsi_unit (bus, id, lun, scsi_units, n_scsi_units); + if (scsi_unit == NULL) { + return g_strdup_printf (_("Unnamed SCSI Drive (%s)"), dev); + } + + return g_strdup_printf ("%s - %s", + scsi_unit->vendor, + scsi_unit->model); +} + +static char * +drive_get_name (struct drive_unit *drive, + struct scsi_unit *scsi_units, + int n_scsi_units) +{ + char *filename, *line, *retval; + char stdname [4], devfsname [15]; + int bus, id, lun, i; + + g_return_val_if_fail (drive != NULL, FALSE); + + /* clean up the string again if we have devfs */ + i = sscanf(drive->device, "%4s %14s", stdname, devfsname); + if (i < 1) { /* should never happen */ + g_warning("drive_get_name: drive->device string broken!"); + return NULL; + } + if (i == 2) { + g_free (drive->device); + drive->device = g_strdup(devfsname); + } + stdname [3] = '\0'; devfsname [14] = '\0'; /* just in case */ + + if (drive->protocol == NAUTILUS_BURN_DRIVE_PROTOCOL_SCSI) { + get_cd_scsi_id (drive->device, &bus, &id, &lun); + retval = get_scsi_cd_name (bus, id, lun, drive->device, scsi_units, + n_scsi_units); + } else { + filename = g_strdup_printf ("/proc/ide/%s/model", stdname); + if (!g_file_get_contents (filename, &line, NULL, NULL) || + line == NULL) { + g_free (filename); + return NULL; + } + g_free (filename); + + i = strlen (line); + if (line [i-1] != '\n') { + retval = g_strdup (line); + } else { + retval = g_strndup (line, i - 1); + } + + g_free (line); + } + + return retval; +} + +static GList * +add_linux_cd_recorder (GList *drives, + gboolean recorder_only, + struct drive_unit *drive_s, + struct scsi_unit *scsi_units, + int n_scsi_units) +{ + int bus, id, lun; + NautilusBurnDrive *drive; + + drive = nautilus_burn_drive_new (); + + drive->type = NAUTILUS_BURN_DRIVE_TYPE_CD_DRIVE; + drive->display_name = g_strdup (drive_s->display_name); + + if (drive_s->protocol == NAUTILUS_BURN_DRIVE_PROTOCOL_SCSI) { + drive->priv->protocol = NAUTILUS_BURN_DRIVE_PROTOCOL_SCSI; + if (!get_cd_scsi_id (drive_s->device, &bus, &id, &lun)) { + g_free (drive->display_name); + g_free (drive); + return drives; + } + drive->cdrecord_id = g_strdup_printf ("%d,%d,%d", + bus, id, lun); + } else { + drive->priv->protocol = NAUTILUS_BURN_DRIVE_PROTOCOL_IDE; + /* kernel >=2.5 can write cd w/o ide-scsi */ + drive->cdrecord_id = g_strdup_printf ("/dev/%s", + drive_s->device); + } + + if (recorder_only) { + drive->max_speed_write = get_device_max_write_speed + (drive->device); + if (drive->max_speed_write == -1) { + drive->max_speed_write = drive_s->speed; + } + } else { + /* Have a wild guess, the drive should actually correct us */ + drive->max_speed_write = drive_s->speed; + } + + drive->device = g_strdup_printf ("/dev/%s", drive_s->device); + drive->max_speed_read = drive_s->speed; + if (drive_s->can_write_dvdr) { + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_RW_RECORDER; + } + + if (drive_s->can_write_dvdram) { + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_RAM_RECORDER; + } + + if (drive_s->can_write_cdr) { + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_CD_RECORDER; + } + if (drive_s->can_write_cdrw) { + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_CDRW_RECORDER; + } + if (drive_s->can_read_dvd) { + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_DRIVE; + add_dvd_plus (drive); + } + + return g_list_append (drives, drive); +} + +static GList * +add_linux_cd_drive (GList *drives, + struct drive_unit *drive_s, + struct scsi_unit *scsi_units, + int n_scsi_units) +{ + NautilusBurnDrive *drive; + + drive = nautilus_burn_drive_new (); + drive->type = NAUTILUS_BURN_DRIVE_TYPE_CD_DRIVE; + drive->cdrecord_id = NULL; + drive->display_name = g_strdup (drive_s->display_name); + drive->device = g_strdup_printf ("/dev/%s", drive_s->device); + drive->max_speed_write = 0; /* Can't write */ + drive->max_speed_read = drive_s->speed; + if (drive_s->can_read_dvd) { + drive->type |= NAUTILUS_BURN_DRIVE_TYPE_DVD_DRIVE; + } + + return g_list_append (drives, drive); +} + +static char * +get_cd_device_file (const char *str) +{ + char *devname; + + if (str [0] == 's') { + devname = g_strdup_printf ("/dev/scd%c", str [2]); + if (g_file_test (devname, G_FILE_TEST_EXISTS)) { + g_free (devname); + return g_strdup_printf ("scd%c", str [2]); + } + g_free (devname); + } + return g_strdup (str); +} + +static GList * +linux_scan (gboolean recorder_only) +{ + char **device_str, **devices; + char **drive_info; + struct scsi_unit *scsi_units; + struct drive_unit *drives; + char *p, *t; + int n_drives, maj, min, i, j; + int n_scsi_units; + int fd; + FILE *file; + GList *drives_list; + gboolean have_devfs; + + /* devfs creates and populates the /dev/cdroms directory when its mounted + * the 'old style names' are matched with devfs names below. + * The cdroms.device string gets cleaned up again in drive_get_name() + * we need the oldstyle name to get device->display_name for ide. + */ + have_devfs = FALSE; + if (g_file_test ("/dev/.devfsd", G_FILE_TEST_EXISTS)) { + have_devfs = TRUE; + } + + drive_info = read_lines ("/proc/sys/dev/cdrom/info"); + if (drive_info == NULL || drive_info [0] == NULL || drive_info [1] == NULL) { + g_warning ("Couldn't read /proc/sys/dev/cdrom/info"); + return NULL; + } + if (!g_str_has_prefix (drive_info [2], "drive name:\t")) { + return NULL; + } + p = drive_info [2] + strlen ("drive name:\t"); + while (*p == '\t') { + p++; + } + n_drives = count_strings (p); + drives = g_new0 (struct drive_unit, n_drives); + + for (j = 0; j < n_drives; j++) { + t = strchr (p, '\t'); + if (t != NULL) { + *t = 0; + } + drives [j].device = get_cd_device_file (p); + /* Assume its an IDE device for now */ + drives [j].protocol = NAUTILUS_BURN_DRIVE_PROTOCOL_IDE; + if (t != NULL) { + p = t + 1; + } + } + + /* we only have to check the first char, since only ide or scsi + * devices are listed in /proc/sys/dev/cdrom/info. It will always + * be 'h' or 's' + */ + n_scsi_units = 0; + for (i = 0; i < n_drives; i++) { + if (drives [i].device[0] == 's') { + drives [i].protocol = NAUTILUS_BURN_DRIVE_PROTOCOL_SCSI; + n_scsi_units++; + } + } + + if (n_scsi_units > 0) { + /* open /dev/sg0 to force loading of the sg module if not loaded yet */ + fd = open ("/dev/sg0", O_RDWR); + if (fd >= 0) { + close (fd); + } + + devices = read_lines ("/proc/scsi/sg/devices"); + device_str = read_lines ("/proc/scsi/sg/device_strs"); + if (device_str == NULL) { + g_warning ("Can't read /proc/scsi/sg/device_strs"); + g_strfreev (devices); + return NULL; + } + + scsi_units = g_new0 (struct scsi_unit, n_scsi_units); + get_scsi_units (device_str, devices, scsi_units); + + g_strfreev (device_str); + g_strfreev (devices); + } else { + scsi_units = NULL; + } + + for (i = 3; drive_info [i] != NULL; i++) { + if (g_str_has_prefix (drive_info [i], "Can write CD-R:")) { + p = drive_info [i] + strlen ("Can write CD-R:"); + while (*p == '\t') { + p++; + } + for (j = 0; j < n_drives; j++) { + drives [j].can_write_cdr = *p++ == '1'; + + /* Skip tab */ + p++; + } + } + if (g_str_has_prefix (drive_info [i], "Can write CD-RW:")) { + p = drive_info [i] + strlen ("Can write CD-RW:"); + while (*p == '\t') { + p++; + } + for (j = 0; j < n_drives; j++) { + drives [j].can_write_cdrw = *p++ == '1'; + + /* Skip tab */ + p++; + } + } + if (g_str_has_prefix (drive_info [i], "Can write DVD-R:")) { + p = drive_info [i] + strlen ("Can write DVD-R:"); + while (*p == '\t') { + p++; + } + for (j = 0; j < n_drives; j++) { + drives [j].can_write_dvdr = *p++ == '1'; + + /* Skip tab */ + p++; + } + } + if (g_str_has_prefix (drive_info [i], "Can write DVD-RAM:")) { + p = drive_info [i] + strlen ("Can write DVD-RAM:"); + while (*p == '\t') { + p++; + } + for (j = 0; j < n_drives; j++) { + drives [j].can_write_dvdram = *p++ == '1'; + + /* Skip tab */ + p++; + } + } + if (g_str_has_prefix (drive_info [i], "Can read DVD:")) { + p = drive_info [i] + strlen ("Can read DVD:"); + while (*p == '\t') { + p++; + } + for (j = 0; j < n_drives; j++) { + drives [j].can_read_dvd = *p++ == '1'; + + /* Skip tab */ + p++; + } + } + if (g_str_has_prefix (drive_info [i], "drive speed:")) { + p = drive_info [i] + strlen ("drive speed:"); + while (*p == '\t') { + p++; + } + for (j = 0; j < n_drives; j++) { + drives [j].speed = atoi (p); + + /* Skip tab */ + p++; + } + } + } + g_strfreev (drive_info); + + /* get kernel major.minor version */ + file = fopen("/proc/sys/kernel/osrelease", "r"); + if (file == NULL || fscanf(file, "%d.%d", &maj, &min) != 2) { + g_warning("Could not get kernel version."); + maj = min = 0; + } + fclose(file); + + drives_list = NULL; + for (i = n_drives - 1, j = 0; i >= 0; i--, j++) { + if (have_devfs) { + char *s; + s = g_strdup_printf("%s cdroms/cdrom%d", + drives [i].device, j); + g_free (drives [i].device); + drives [i].device = s; + } + drives [i].display_name = drive_get_name (&drives [i], + scsi_units, n_scsi_units); + linux_add_whitelist (&drives [i], scsi_units, n_scsi_units); + + if ((drives [i].can_write_cdr || + drives [i].can_write_cdrw || + drives [i].can_write_dvdr || + drives [i].can_write_dvdram) && + (drives [i].protocol == NAUTILUS_BURN_DRIVE_PROTOCOL_SCSI || + (maj > 2) || (maj == 2 && min >= 5))) { + drives_list = add_linux_cd_recorder (drives_list, + recorder_only, &drives [i], + scsi_units, n_scsi_units); + } else if (!recorder_only) { + drives_list = add_linux_cd_drive (drives_list, + &drives [i], scsi_units, n_scsi_units); + } + } + + for (i = n_drives - 1; i >= 0; i--) { + g_free (drives [i].display_name); + g_free (drives [i].device); + } + g_free (drives); + + for (i = n_scsi_units - 1; i >= 0; i--) { + g_free (scsi_units [i].vendor); + g_free (scsi_units [i].model); + g_free (scsi_units [i].rev); + } + g_free (scsi_units); + + return drives_list; +} + +#elif defined (__FreeBSD__) + +static GList * +freebsd_scan (gboolean recorder_only) +{ + GList *drives_list = NULL; + const char *dev_type = "cd"; + int speed = 16; /* XXX Hardcode the write speed for now. */ + int i = 0; + int cnode = 1; /* Use the CD device's 'c' node. */ + + while (1) { + NautilusBurnDrive *drive; + gchar *cam_path; + struct cam_device *cam_dev; + + cam_path = g_strdup_printf ("/dev/%s%dc", dev_type, i); + + if (!g_file_test (cam_path, G_FILE_TEST_EXISTS)) { + g_free (cam_path); + cam_path = g_strdup_printf ("/dev/%s%d", dev_type, i); + cnode = 0; + if (!g_file_test (cam_path, G_FILE_TEST_EXISTS)) { + g_free (cam_path); + break; + } + } + + if ((cam_dev = cam_open_spec_device (dev_type, i, O_RDWR, NULL)) == NULL) { + i++; + g_free (cam_path); + continue; + } + + drive = nautilus_burn_drive_new (); + drive->display_name = g_strdup_printf ("%s %s", cam_dev->inq_data.vendor, cam_dev->inq_data.revision); + drive->device = g_strdup (cam_path); + drive->cdrecord_id = g_strdup_printf ("%d,%d,%d", cam_dev->path_id, cam_dev->target_id, cam_dev->target_lun); + /* Attempt to get more specific information from + * this drive by using cdrecord. + */ + get_cd_properties (drive->device, drive->cdrecord_id, + &(drive->max_speed_read), + &(drive->max_speed_write), + &(drive->type)); + if (drive->type & NAUTILUS_BURN_DRIVE_TYPE_CD_RECORDER + || drive->type & NAUTILUS_BURN_DRIVE_TYPE_CDRW_RECORDER + || drive->type & NAUTILUS_BURN_DRIVE_TYPE_DVD_RAM_RECORDER + || drive->type & NAUTILUS_BURN_DRIVE_TYPE_DVD_RW_RECORDER + || !recorder_only) { + + if (drive->max_speed_read == -1) { + drive->max_speed_read = speed; + } + if (drive->max_speed_write == -1) { + drive->max_speed_write = speed; + } + + if (drive->type & NAUTILUS_BURN_DRIVE_TYPE_DVD_DRIVE) { + add_dvd_plus (drive); + } + + drives_list = g_list_append (drives_list, drive); + } else { + nautilus_burn_drive_free (drive); + } + + g_free (cam_path); + free (cam_dev); + + i++; + } + + return drives_list; +} + +#else + +static char * +cdrecord_scan_get_stdout (void) +{ + int max_speed, i; + const char *argv [20]; /* Shouldn't need more than 20 arguments */ + char *stdout_data; + + max_speed = -1; + + i = 0; + argv [i++] = "cdrecord"; + argv [i++] = "-scanbus"; + argv [i++] = NULL; + + if (g_spawn_sync (NULL, + (char **)argv, + NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL, + NULL, NULL, + &stdout_data, + NULL, + NULL, + NULL)) { + return stdout_data; + } + + return NULL; +} + +#define DEFAULT_SPEED 2 + +static GList * +cdrecord_scan (gboolean recorder_only) +{ + GList *drives_list; + NautilusBurnDrive *drive; + char *stdout_data, **lines, vendor [9], model [17]; + int i, bus, id, lun, index; + + drives_list = NULL; + + stdout_data = cdrecord_scan_get_stdout (); + if (stdout_data == NULL) { + return drives_list; + } + + lines = g_strsplit (stdout_data, "\n", 0); + g_free (stdout_data); + + for (i = 0; lines [i] != NULL; i++) { + if (sscanf (lines [i], "\t%d,%d,%d\t %d) '%8c' '%16c'", + &bus, &id, &lun, &index, + vendor, model) != 6) { + continue; + } + + vendor [8] = '\0'; model [16] = '\0'; + + drive = nautilus_burn_drive_new (); + drive->display_name = g_strdup_printf ("%s - %s", + g_strstrip (vendor), g_strstrip (model)); + drive->cdrecord_id = g_strdup_printf ("%d,%d,%d", bus, id, lun); + /* FIXME we don't have any way to guess the real device + * from the info we get from CDRecord */ + drive->device = g_strdup_printf ("/dev/pg%d", index); + get_cd_properties (drive->device, drive->cdrecord_id, + &(drive->max_speed_read), + &(drive->max_speed_write), + &(drive->type)); + add_whitelist (drive); + if (drive->type & NAUTILUS_BURN_DRIVE_TYPE_CD_RECORDER + || drive->type & NAUTILUS_BURN_DRIVE_TYPE_CDRW_RECORDER + || drive->type & NAUTILUS_BURN_DRIVE_TYPE_DVD_RAM_RECORDER + || drive->type & NAUTILUS_BURN_DRIVE_TYPE_DVD_RW_RECORDER + || !recorder_only) { + + if (drive->max_speed_read == -1) { + drive->max_speed_read = DEFAULT_SPEED; + } + if (drive->max_speed_write == -1) { + drive->max_speed_write = DEFAULT_SPEED; + } + + if (drive->type & NAUTILUS_BURN_DRIVE_TYPE_DVD_DRIVE) { + add_dvd_plus (drive); + } + + drives_list = g_list_append (drives_list, drive); + } else { + nautilus_burn_drive_free (drive); + } + } + + g_strfreev (lines); + + return drives_list; +} + +#endif + +/** + * nautilus_burn_drive_get_file_image: + * + * Create a new %NAUTILUS_BURN_DRIVE_TYPE_FILE #NautilusBurnDrive. + * + * Return value: A new drive. + **/ +NautilusBurnDrive * +nautilus_burn_drive_get_file_image (void) +{ + NautilusBurnDrive *drive; + + drive = nautilus_burn_drive_new (); + drive->display_name = g_strdup (_("File image")); + drive->max_speed_read = 0; + drive->max_speed_write = 0; + drive->type = NAUTILUS_BURN_DRIVE_TYPE_FILE; + + return drive; +} + +/* This is used for testing different configurations */ +#if 0 +static GList* +test_cdroms (void) +{ + GList *list = NULL; + CDDrive *drive = g_new0 (CDDrive, 1); + + drive->type = CDDRIVE_TYPE_CD_DRIVE | CDDRIVE_TYPE_DVD_DRIVE; + drive->display_name = g_strdup ("HL-DT-STDVD-ROM"); + drive->device = g_strdup ("/dev/hdc"); + list = g_list_append (list, drive); + drive = g_new0 (CDDrive, 1); + drive->type = CDDRIVE_TYPE_CD_DRIVE | CDDRIVE_TYPE_DVD_DRIVE | CDDRIVE_TYPE_CD_RECORDER; + drive->display_name = g_strdup ("_NEC DVD_RW ND-2500A"); + drive->device = g_strdup ("/dev/hdd"); + list = g_list_append (list, drive); + return list; +} +#endif + +/** + * nautilus_burn_drive_get_list: + * @recorder_only: Include only devices capable of recording + * @add_image: Include a file image device + * + * Find media devices on the system. + * + * Return value: List of media drives available. + **/ +GList * +nautilus_burn_drive_get_list (gboolean recorder_only, + gboolean add_image) +{ + GList *drives = NULL; + +#ifdef USE_HAL + drives = hal_scan (recorder_only); +#endif + + if (drives == NULL) { +#if defined (__linux__) + drives = linux_scan (recorder_only); +#elif defined (__FreeBSD__) + drives = freebsd_scan (recorder_only); +#else + drives = cdrecord_scan (recorder_only); +#endif + } + + if (add_image) { + NautilusBurnDrive *drive; + drive = nautilus_burn_drive_get_file_image (); + drives = g_list_append (drives, drive); + } + + return drives; +} + +/** + * nautilus_burn_drive_free: + * @drive: #NautilusBurnDrive to be freed + * + * Free @drive. + **/ +void +nautilus_burn_drive_free (NautilusBurnDrive *drive) +{ + g_return_if_fail (drive != NULL); + + if (drive->priv) { + g_free (drive->priv->udi); + g_free (drive->priv); + } + + g_free (drive->display_name); + g_free (drive->cdrecord_id); + g_free (drive->device); + g_free (drive); +} + +/** + * nautilus_burn_drive_lock: + * @drive: Pointer to a #NautilusBurnDrive + * @reason: + * @reason_for_failure: + * + * Lock a #NautilusBurnDrive + * + * Return value: %TRUE if the drive was sucessfully locked, %FALSE otherwise. + * + * Since: 2.8 + **/ +gboolean +nautilus_burn_drive_lock (NautilusBurnDrive *drive, + const char *reason, + char **reason_for_failure) +{ + gboolean res; + + if (reason_for_failure != NULL) + *reason_for_failure = NULL; + + res = TRUE; +#ifdef USE_HAL + if (drive->priv->udi != NULL) { + LibHalContext *ctx; + char *dbus_reason; + + ctx = get_hal_context (); + if (ctx != NULL) { + res = hal_device_lock (ctx, + drive->priv->udi, + reason, + &dbus_reason); + if (dbus_reason != NULL && + reason_for_failure != NULL) + *reason_for_failure = g_strdup (dbus_reason); + if (dbus_reason != NULL) + dbus_free (dbus_reason); + } + } +#endif + return res; +} + +/** + * nautilus_burn_drive_unlock: + * @drive: Pointer to a #NautilusBurnDrive + * + * Unlock a #NautilusBurnDrive + * + * Return value: %TRUE if the drive was sucessfully unlocked, %FALSE otherwise. + * + * Since: 2.8 + **/ +gboolean +nautilus_burn_drive_unlock (NautilusBurnDrive *drive) +{ + gboolean res; + + res = TRUE; +#ifdef USE_HAL + if (drive->priv->udi != NULL) { + LibHalContext *ctx; + + ctx = get_hal_context (); + if (ctx != NULL) { + res = hal_device_unlock (ctx, + drive->priv->udi); + } + } +#endif + return res; +} + +/** + * nautilus_burn_drive_new: + * + * Create a new #NautilusBurnDrive. + * + * Return value: The new drive. + * + * Since: 2.8 + **/ +NautilusBurnDrive * +nautilus_burn_drive_new (void) +{ + NautilusBurnDrive *drive; + + drive = g_new0 (NautilusBurnDrive, 1); + drive->priv = g_new0 (NautilusBurnDrivePriv, 1); + return drive; +} + +/** + * nautilus_burn_drive_copy: + * @drive: Pointer to the #NautilusBurnDrive to be copied + * + * Return vale: Copy of specified #NautilusBurnDrive. + * + * Since: 2.8 + **/ +NautilusBurnDrive * +nautilus_burn_drive_copy (NautilusBurnDrive *drive) +{ + NautilusBurnDrive *drive_copy; + + drive_copy = nautilus_burn_drive_new (); + drive_copy->type = drive->type; + drive_copy->display_name = g_strdup (drive->display_name); + drive_copy->max_speed_write = drive->max_speed_write; + drive_copy->max_speed_read = drive->max_speed_read; + drive_copy->cdrecord_id = g_strdup (drive->cdrecord_id); + drive_copy->device = g_strdup (drive->device); + drive_copy->priv->protocol = drive->priv->protocol; + drive_copy->priv->udi = g_strdup (drive->priv->udi); + + return drive_copy; +} + +/** + * nautilus_burn_drive_equal: + * @a: First #NautilusBurnDrive struct to compare + * @b: Second #NautilusBurnDrive struct to compare + * + * Compare the two cd drives, return %TRUE if they match exactly + * the same drive. + * + * Returns: %TRUE if the two #NautilusBurnDrives are equal, otherwise return %FALSE. + * + * Since: 2.8 + **/ +gboolean +nautilus_burn_drive_equal (NautilusBurnDrive *a, + NautilusBurnDrive *b) +{ + if (!a || !b) + return FALSE; + + if ((a->type & NAUTILUS_BURN_DRIVE_TYPE_FILE) + && (b->type & NAUTILUS_BURN_DRIVE_TYPE_FILE)) + return TRUE; + + if (!a->device || !b->device) + return FALSE; + + return strcmp (a->device, b->device) == 0; +}