lintian doesn't like orphan packages with uploaders...
[debian/amanda] / device-src / device.c
index ddb357cc8acf468262c3d2f5abd39cc917255689..6eed5238e43fa14f973c55ebd9b540996a90685c 100644 (file)
@@ -1,21 +1,22 @@
 /*
- * Copyright (c) 2005-2008 Zmanda Inc.  All Rights Reserved.
- * 
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License version 2.1 as 
- * published by the Free Software Foundation.
- * 
- * This library is distributed in the hope that it will be useful, but
+ * Copyright (c) 2007-2012 Zmanda, Inc.  All Rights Reserved.
+ *
+ * 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 Lesser General Public
- * License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
- * 
- * Contact information: Zmanda Inc., 465 S Mathlida Ave, Suite 300
- * Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
+ * 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
+ *
+ * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
+ * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
  */
 
 /* The Device API abstracts device workings, interaction, properties, and
@@ -28,8 +29,6 @@
 #include <regex.h>
 
 #include "device.h"
-#include "queueing.h"
-#include "device-queueing.h"
 #include "property.h"
 
 #include "timestamp.h"
@@ -48,6 +47,12 @@ void    s3_device_register    (void);
 void    tape_device_register    (void);
 #endif
 void    vfs_device_register     (void);
+#ifdef WANT_DVDRW_DEVICE
+void    dvdrw_device_register    (void);
+#endif
+#ifdef WANT_NDMP_DEVICE
+void    ndmp_device_register    (void);
+#endif
 
 /*
  * Registration infrastructure
@@ -70,6 +75,12 @@ void device_api_init(void) {
 #ifdef WANT_S3_DEVICE
     s3_device_register();
 #endif
+#ifdef WANT_DVDRW_DEVICE
+    dvdrw_device_register();
+#endif
+#ifdef WANT_NDMP_DEVICE
+    ndmp_device_register();
+#endif
 }
 
 void
@@ -172,9 +183,6 @@ static void simple_property_free(SimpleProperty *o);
 static void default_device_open_device(Device * self, char * device_name,
                                    char * device_type, char * device_node);
 static gboolean default_device_configure(Device *self, gboolean use_global_config);
-static gboolean default_device_write_from_fd(Device *self,
-                                            queue_fd_t *queue_fd);
-static gboolean default_device_read_to_fd(Device *self, queue_fd_t *queue_fd);
 static gboolean default_device_property_get_ex(Device * self, DevicePropertyId id,
                                               GValue * val,
                                               PropertySurety *surety,
@@ -228,11 +236,11 @@ device_get_type (void)
             (GInstanceInitFunc) device_init,
             NULL
         };
-        
+
         type = g_type_register_static (G_TYPE_OBJECT, "Device", &info,
                                        (GTypeFlags)G_TYPE_FLAG_ABSTRACT);
     }
-    
+
     return type;
 }
 
@@ -257,13 +265,14 @@ static void device_finalize(GObject *obj_self) {
     amfree(self->private);
 }
 
-static void 
+static void
 device_init (Device * self)
 {
     self->private = malloc(sizeof(DevicePrivate));
     self->device_name = NULL;
     self->access_mode = ACCESS_NULL;
     self->is_eof = FALSE;
+    self->is_eom = FALSE;
     self->file = -1;
     self->block = 0;
     self->in_file = FALSE;
@@ -283,17 +292,17 @@ device_init (Device * self)
                               (GDestroyNotify) simple_property_free);
 }
 
-static void 
+static void
 device_class_init (DeviceClass * device_class)
 {
     GObjectClass *g_object_class = (GObjectClass*) device_class;
-    
+
     parent_class = g_type_class_ref (G_TYPE_OBJECT);
-    
+
+    device_class->directtcp_supported = FALSE;
+
     device_class->open_device = default_device_open_device;
     device_class->configure = default_device_configure;
-    device_class->write_from_fd = default_device_write_from_fd;
-    device_class->read_to_fd = default_device_read_to_fd;
     device_class->property_get_ex = default_device_property_get_ex;
     device_class->property_set_ex = default_device_property_set_ex;
     g_object_class->finalize = device_finalize;
@@ -348,10 +357,25 @@ device_base_init (DeviceClass * device_class)
            device_simple_property_get_fn,
            device_simple_property_set_fn);
 
+    device_class_register_property(device_class, PROPERTY_FULL_DELETION,
+           PROPERTY_ACCESS_GET_MASK,
+           device_simple_property_get_fn,
+           device_simple_property_set_fn);
+
     device_class_register_property(device_class, PROPERTY_MEDIUM_ACCESS_TYPE,
            PROPERTY_ACCESS_GET_MASK,
            device_simple_property_get_fn,
            device_simple_property_set_fn);
+
+    device_class_register_property(device_class, PROPERTY_COMMENT,
+           PROPERTY_ACCESS_GET_MASK|PROPERTY_ACCESS_SET_MASK,
+           device_simple_property_get_fn,
+           device_simple_property_set_fn);
+
+    device_class_register_property(device_class, PROPERTY_LEOM,
+           PROPERTY_ACCESS_GET_MASK,
+           device_simple_property_get_fn,
+           device_simple_property_set_fn);
 }
 
 static void simple_property_free(SimpleProperty * resp) {
@@ -408,7 +432,7 @@ handle_device_regex(const char * user_name, char ** driver_name,
         *driver_name = stralloc("tape");
         *device = stralloc(user_name);
 #else /* !WANT_TAPE_DEVICE */
-       errmsg = newvstrallocf(errmsg, "\"%s\" is not a valid device name.\n", user_name);
+       *errmsg = newvstrallocf(*errmsg, "\"%s\" is not a valid device name.\n", user_name);
        regfree(&regex);
        return FALSE;
 #endif /* WANT_TAPE_DEVICE */
@@ -436,7 +460,27 @@ make_null_error(char *errmsg, DeviceStatusFlags status)
     return device;
 }
 
-Device* 
+char *
+device_unaliased_name(
+    char *device_name)
+{
+    device_config_t *dc;
+    char *unaliased_name;
+
+    /* look up the unaliased device name in the configuration */
+    if ((dc = lookup_device_config(device_name))) {
+       if (!(unaliased_name = device_config_get_tapedev(dc))
+           || unaliased_name[0] == '\0') {
+           return NULL;
+       }
+    } else {
+       unaliased_name = device_name;
+    }
+
+    return unaliased_name;
+}
+
+Device*
 device_open (char * device_name)
 {
     char *device_type = NULL;
@@ -445,7 +489,6 @@ device_open (char * device_name)
     char *unaliased_name = NULL;
     DeviceFactory factory;
     Device *device;
-    device_config_t *dc;
 
     g_assert(device_name != NULL);
 
@@ -457,16 +500,11 @@ device_open (char * device_name)
     if (device_name == NULL)
        return make_null_error(stralloc(_("No device name specified")), DEVICE_STATUS_DEVICE_ERROR);
 
-    /* look up the unaliased device name in the configuration */
-    if ((dc = lookup_device_config(device_name))) {
-       if (!(unaliased_name = device_config_get_tapedev(dc))
-           || unaliased_name[0] == '\0') {
-           return make_null_error(
+    unaliased_name = device_unaliased_name(device_name);
+    if (!unaliased_name) {
+       return make_null_error(
                vstrallocf(_("Device '%s' has no tapedev"), device_name),
                DEVICE_STATUS_DEVICE_ERROR);
-       }
-    } else {
-       unaliased_name = device_name;
     }
 
     if (!handle_device_regex(unaliased_name, &device_type, &device_node,
@@ -489,6 +527,7 @@ device_open (char * device_name)
     device = factory(device_name, device_type, device_node);
     g_assert(device != NULL); /* factories must always return a device */
 
+    device->device_mutex = g_mutex_new();
     amfree(device_type);
     amfree(device_node);
 
@@ -585,19 +624,8 @@ device_set_error(Device *self, char *errmsg, DeviceStatusFlags new_flags)
 }
 
 char * device_build_amanda_header(Device * self, const dumpfile_t * info,
-                                  int * size, gboolean * oneblock) {
-    char *amanda_header;
-    size_t min_header_length;
-    size_t header_buffer_size;
-
-    min_header_length = self->block_size;
-    amanda_header = build_header(info, min_header_length);
-    header_buffer_size = MAX(min_header_length, strlen(amanda_header)+1);
-    if (size != NULL)
-        *size = header_buffer_size;
-    if (oneblock != NULL)
-        *oneblock = (header_buffer_size <= self->block_size);
-    return amanda_header;
+                                  size_t *size) {
+    return build_header(info, size, self->block_size);
 }
 
 dumpfile_t * make_tapestart_header(Device * self, char * label,
@@ -622,8 +650,8 @@ dumpfile_t * make_tapestart_header(Device * self, char * label,
     } else {
         self->volume_time = g_strdup(timestamp);
     }
-    strncpy(rval->datestamp, self->volume_time, sizeof(rval->datestamp));
-    strncpy(rval->name, label, sizeof(rval->name));
+    g_strlcpy(rval->datestamp, self->volume_time, sizeof(rval->datestamp));
+    g_strlcpy(rval->name, label, sizeof(rval->name));
 
     return rval;
 }
@@ -635,7 +663,7 @@ dumpfile_t * make_tapeend_header(void) {
     rval = malloc(sizeof(*rval));
     rval->type = F_TAPEEND;
     timestamp = get_timestamp_from_time(time(NULL));
-    strncpy(rval->datestamp, timestamp, sizeof(rval->datestamp));
+    g_strlcpy(rval->datestamp, timestamp, sizeof(rval->datestamp));
     amfree(timestamp);
     return rval;
 }
@@ -696,15 +724,15 @@ static void set_device_property(gpointer key_p, gpointer value_p,
            DEVICE_STATUS_DEVICE_ERROR);
        return;
     }
-    
+
     bzero(&property_value, sizeof(property_value));
     g_value_init(&property_value, property_base->type);
     value = property->values->data;
     if (!g_value_set_from_string(&property_value, value)) {
         /* Value type could not be interpreted. */
        device_set_error(device,
-           vstrallocf(_("Could not parse property value '%s' for property '%s'"),
-                       value, g_type_name(property_base->type)),
+           vstrallocf(_("Could not parse property value '%s' for property '%s' (property type %s)"),
+                        value, property_base->name, g_type_name(property_base->type)),
            DEVICE_STATUS_DEVICE_ERROR);
         return;
     } else {
@@ -753,12 +781,12 @@ set_properties_from_global_config(Device * device) {
                 g_value_init(&val, G_TYPE_UINT);
                 g_value_set_uint(&val, blocksize_kb * 1024);
                 success = device_property_set(device,
-                                              PROPERTY_READ_BUFFER_SIZE,
+                                              PROPERTY_READ_BLOCK_SIZE,
                                               &val);
                 g_value_unset(&val);
                 if (!success) {
                    /* a non-fatal error */
-                    g_warning("Setting READ_BUFFER_SIZE to %ju not supported for device %s.",
+                    g_warning("Setting READ_BLOCK_SIZE to %ju not supported for device %s.",
                             1024*(uintmax_t)blocksize_kb, device->device_name);
                 }
             }
@@ -855,8 +883,12 @@ property_set_block_size_fn(
 
     g_assert(block_size >= 0); /* int -> gsize (unsigned) */
     if ((gsize)block_size < self->min_block_size
-       || (gsize)block_size > self->max_block_size)
+       || (gsize)block_size > self->max_block_size) {
+       device_set_error(self,
+           g_strdup_printf("Error setting BLOCK-SIZE property to '%zu', it must be between %zu and %zu", (gsize)block_size, self->min_block_size, self->max_block_size),
+           DEVICE_STATUS_DEVICE_ERROR);
        return FALSE;
+    }
 
     self->block_size = block_size;
     self->block_size_surety = surety;
@@ -967,9 +999,6 @@ default_device_property_get_ex(
     /* Most of this function's job is to sanity-check everything, then
      * call the relevant getter. */
 
-    if (device_in_error(self))
-       return FALSE;
-
     class_properties = DEVICE_GET_CLASS(self)->class_properties;
     if (id >= class_properties->len)
        return FALSE;
@@ -1046,60 +1075,6 @@ device_property_get_list (Device * self)
     return DEVICE_GET_CLASS(self)->class_properties_list;
 }
 
-static gboolean
-default_device_read_to_fd(Device *self, queue_fd_t *queue_fd) {
-    GValue val;
-    StreamingRequirement streaming_mode;
-
-    if (device_in_error(self)) return FALSE;
-
-    /* Get the device's parameters */
-    bzero(&val, sizeof(val));
-    if (!device_property_get(self, PROPERTY_STREAMING, &val)
-       || !G_VALUE_HOLDS(&val, STREAMING_REQUIREMENT_TYPE)) {
-       streaming_mode = STREAMING_REQUIREMENT_REQUIRED;
-    } else {
-       streaming_mode = g_value_get_enum(&val);
-    }
-
-    return QUEUE_SUCCESS ==
-       do_consumer_producer_queue_full(
-           device_read_producer,
-           self,
-           fd_write_consumer,
-           queue_fd,
-           self->block_size,
-           DEFAULT_MAX_BUFFER_MEMORY,
-           streaming_mode);
-}
-
-static gboolean
-default_device_write_from_fd(Device *self, queue_fd_t *queue_fd) {
-    GValue val;
-    StreamingRequirement streaming_mode;
-
-    if (device_in_error(self)) return FALSE;
-
-    /* Get the device's parameters */
-    bzero(&val, sizeof(val));
-    if (!device_property_get(self, PROPERTY_STREAMING, &val)
-       || !G_VALUE_HOLDS(&val, STREAMING_REQUIREMENT_TYPE)) {
-       streaming_mode = STREAMING_REQUIREMENT_REQUIRED;
-    } else {
-       streaming_mode = g_value_get_enum(&val);
-    }
-
-    return QUEUE_SUCCESS ==
-       do_consumer_producer_queue_full(
-           fd_read_producer,
-           queue_fd,
-           device_write_consumer,
-           self,
-           self->block_size,
-           DEFAULT_MAX_BUFFER_MEMORY,
-           streaming_mode);
-}
-
 /* XXX WARNING XXX
  * All the functions below this comment are stub functions that do nothing
  * but implement the virtual function table. Call these functions and they
@@ -1143,6 +1118,46 @@ device_finish (Device * self) {
     return (klass->finish)(self);
 }
 
+guint64
+device_get_bytes_read (Device * self) {
+    DeviceClass *klass;
+    guint64 bytes = 0;
+
+    g_assert(IS_DEVICE (self));
+
+    g_mutex_lock(self->device_mutex);
+    if (self->in_file) {
+       klass = DEVICE_GET_CLASS(self);
+       if (klass->get_bytes_read) {
+           bytes = (klass->get_bytes_read)(self);
+       } else {
+           bytes = self->bytes_read;
+       }
+    }
+    g_mutex_unlock(self->device_mutex);
+    return bytes;
+}
+
+guint64
+device_get_bytes_written (Device * self) {
+    DeviceClass *klass;
+    guint64 bytes = 0;
+
+    g_assert(IS_DEVICE (self));
+
+    g_mutex_lock(self->device_mutex);
+    if (self->in_file) {
+       klass = DEVICE_GET_CLASS(self);
+       if (klass->get_bytes_written) {
+           bytes = (klass->get_bytes_written)(self);
+       } else {
+           bytes = self->bytes_written;
+       }
+    }
+    g_mutex_unlock(self->device_mutex);
+    return bytes;
+}
+
 gboolean
 device_configure (Device * self, gboolean use_global_config)
 {
@@ -1162,7 +1177,7 @@ device_configure (Device * self, gboolean use_global_config)
     }
 }
 
-gboolean 
+gboolean
 device_start (Device * self, DeviceAccessMode mode,
               char * label, char * timestamp)
 {
@@ -1216,20 +1231,6 @@ device_write_block (Device * self, guint size, gpointer block)
     return (*klass->write_block)(self,size, block);
 }
 
-gboolean 
-device_write_from_fd (Device * self, queue_fd_t * queue_fd)
-{
-    DeviceClass *klass;
-
-    g_assert(IS_DEVICE (self));
-    g_assert(queue_fd->fd >= 0);
-    g_assert(IS_WRITABLE_ACCESS_MODE(self->access_mode));
-
-    klass = DEVICE_GET_CLASS(self);
-    g_assert(klass->write_from_fd);
-    return (klass->write_from_fd)(self,queue_fd);
-}
-
 gboolean
 device_start_file (Device * self, dumpfile_t * jobInfo) {
     DeviceClass * klass;
@@ -1245,7 +1246,7 @@ device_start_file (Device * self, dumpfile_t * jobInfo) {
     return (klass->start_file)(self, jobInfo );
 }
 
-gboolean 
+gboolean
 device_finish_file (Device * self)
 {
     DeviceClass *klass;
@@ -1272,7 +1273,7 @@ device_seek_file (Device * self, guint file)
     return (klass->seek_file)(self,file);
 }
 
-gboolean 
+gboolean
 device_seek_block (Device * self, guint64 block)
 {
     DeviceClass *klass;
@@ -1304,22 +1305,7 @@ device_read_block (Device * self, gpointer buffer, int * size)
     return (klass->read_block)(self,buffer,size);
 }
 
-gboolean 
-device_read_to_fd (Device * self, queue_fd_t *queue_fd)
-{
-    DeviceClass *klass;
-
-    g_assert(IS_DEVICE (self));
-    g_assert(queue_fd->fd >= 0);
-    g_assert(self->access_mode == ACCESS_READ);
-
-    klass = DEVICE_GET_CLASS(self);
-    g_assert(klass->read_to_fd);
-    return (klass->read_to_fd)(self,queue_fd);
-}
-
-
-gboolean 
+gboolean
 device_property_get_ex(
        Device * self,
        DevicePropertyId id,
@@ -1356,7 +1342,7 @@ device_property_set_ex(
     return (klass->property_set_ex)(self, id, val, surety, source);
 }
 
-gboolean 
+gboolean
 device_recycle_file (Device * self, guint filenum)
 {
     DeviceClass *klass;
@@ -1372,6 +1358,182 @@ device_recycle_file (Device * self, guint filenum)
     return (klass->recycle_file)(self,filenum);
 }
 
+gboolean
+device_erase (Device * self)
+{
+    DeviceClass *klass;
+
+    g_assert(IS_DEVICE (self));
+    g_assert(self->access_mode == ACCESS_NULL);
+    g_assert(!self->in_file);
+
+    klass = DEVICE_GET_CLASS(self);
+    if(klass->erase) {
+       return (klass->erase)(self);
+    } else {
+       device_set_error(self,
+           stralloc(_("Unimplemented method")),
+           DEVICE_STATUS_DEVICE_ERROR);
+       return FALSE;
+    }
+}
+
+gboolean
+device_eject (Device * self)
+{
+    DeviceClass *klass;
+
+    g_assert(IS_DEVICE (self));
+    g_assert(self->access_mode == ACCESS_NULL);
+    g_assert(!self->in_file);
+
+    klass = DEVICE_GET_CLASS(self);
+    if (klass->eject) {
+       return (klass->eject)(self);
+    } else {
+       return TRUE;
+    }
+}
+
+gboolean
+device_listen(
+    Device *self,
+    gboolean for_writing,
+    DirectTCPAddr **addrs)
+{
+    DeviceClass *klass;
+
+    klass = DEVICE_GET_CLASS(self);
+    if(klass->listen) {
+       return (klass->listen)(self, for_writing, addrs);
+    } else {
+       device_set_error(self,
+           stralloc(_("Unimplemented method")),
+           DEVICE_STATUS_DEVICE_ERROR);
+       return FALSE;
+    }
+}
+
+int
+device_accept(
+    Device *self,
+    DirectTCPConnection **conn,
+    int    *cancelled,
+    GMutex *abort_mutex,
+    GCond  *abort_cond)
+{
+    DeviceClass *klass;
+
+    klass = DEVICE_GET_CLASS(self);
+    if(klass->accept) {
+       return (klass->accept)(self, conn, cancelled, abort_mutex, abort_cond);
+    } else {
+       device_set_error(self,
+           g_strdup(_("Unimplemented method")),
+           DEVICE_STATUS_DEVICE_ERROR);
+       return 1;
+    }
+}
+
+
+int
+device_connect(
+    Device *self,
+    gboolean for_writing,
+    DirectTCPAddr *addrs,
+    DirectTCPConnection **conn,
+    int    *cancelled,
+    GMutex *abort_mutex,
+    GCond  *abort_cond)
+{
+    DeviceClass *klass;
+
+    klass = DEVICE_GET_CLASS(self);
+    if(klass->connect) {
+       return (klass->connect)(self, for_writing, addrs, conn, cancelled,
+                               abort_mutex, abort_cond);
+    } else {
+       device_set_error(self,
+           g_strdup(_("Unimplemented method")),
+           DEVICE_STATUS_DEVICE_ERROR);
+       return 1;
+    }
+}
+
+int
+device_write_from_connection(
+    Device *self,
+    guint64 size,
+    guint64 *actual_size,
+    int    *cancelled,
+    GMutex *abort_mutex,
+    GCond  *abort_cond)
+{
+    DeviceClass *klass;
+
+    klass = DEVICE_GET_CLASS(self);
+
+    g_assert(self->in_file);
+    g_assert(IS_WRITABLE_ACCESS_MODE(self->access_mode));
+
+    if(klass->write_from_connection) {
+       return (klass->write_from_connection)(self, size, actual_size,
+                                             cancelled,
+                                             abort_mutex, abort_cond);
+    } else {
+       device_set_error(self,
+           stralloc(_("Unimplemented method")),
+           DEVICE_STATUS_DEVICE_ERROR);
+       return 1;
+    }
+}
+
+int
+device_read_to_connection(
+    Device *self,
+    guint64 size,
+    guint64 *actual_size,
+    int    *cancelled,
+    GMutex *abort_mutex,
+    GCond  *abort_cond)
+{
+    DeviceClass *klass;
+
+    g_assert(self->in_file);
+    g_assert(self->access_mode == ACCESS_READ);
+
+    klass = DEVICE_GET_CLASS(self);
+    if(klass->read_to_connection) {
+       return (klass->read_to_connection)(self, size, actual_size,
+                                          cancelled, abort_mutex, abort_cond);
+    } else {
+       device_set_error(self,
+           stralloc(_("Unimplemented method")),
+           DEVICE_STATUS_DEVICE_ERROR);
+       return 1;
+    }
+}
+
+gboolean
+device_use_connection(
+    Device *self,
+    DirectTCPConnection *conn)
+{
+    DeviceClass *klass;
+
+    g_assert(self->access_mode == ACCESS_NULL);
+
+    klass = DEVICE_GET_CLASS(self);
+    if(klass->use_connection) {
+       return (klass->use_connection)(self, conn);
+    } else {
+       device_set_error(self,
+           stralloc(_("Unimplemented method")),
+           DEVICE_STATUS_DEVICE_ERROR);
+       return FALSE;
+    }
+}
+
 /* Property handling */
 
 void