Imported Upstream version 3.1.0
[debian/amanda] / device-src / device.c
1 /*
2  * Copyright (c) 2007, 2008, 2009, 2010 Zmanda, Inc.  All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License version 2 as published
6  * by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11  * for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16  *
17  * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
18  * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
19  */
20
21 /* The Device API abstracts device workings, interaction, properties, and
22  * capabilities from the rest of the Amanda code base. It supports
23  * pluggable modules for different kinds of devices. */
24
25 #include "amanda.h"
26 #include "conffile.h"
27
28 #include <regex.h>
29
30 #include "device.h"
31 #include "queueing.h"
32 #include "device-queueing.h"
33 #include "property.h"
34
35 #include "timestamp.h"
36 #include "util.h"
37
38 /*
39  * Prototypes for subclass registration functions
40  */
41
42 void    null_device_register    (void);
43 void    rait_device_register    (void);
44 #ifdef WANT_S3_DEVICE
45 void    s3_device_register    (void);
46 #endif
47 #ifdef WANT_TAPE_DEVICE
48 void    tape_device_register    (void);
49 #endif
50 void    vfs_device_register     (void);
51 #ifdef WANT_DVDRW_DEVICE
52 void    dvdrw_device_register    (void);
53 #endif
54 #ifdef WANT_NDMP_DEVICE
55 void    ndmp_device_register    (void);
56 #endif
57
58 /*
59  * Registration infrastructure
60  */
61
62 static GHashTable* driverList = NULL;
63
64 void device_api_init(void) {
65     glib_init();
66     device_property_init();
67     driverList = g_hash_table_new(g_str_hash, g_str_equal);
68
69     /* register other types and devices. */
70     null_device_register();
71     vfs_device_register();
72 #ifdef WANT_TAPE_DEVICE
73     tape_device_register();
74 #endif
75     rait_device_register();
76 #ifdef WANT_S3_DEVICE
77     s3_device_register();
78 #endif
79 #ifdef WANT_DVDRW_DEVICE
80     dvdrw_device_register();
81 #endif
82 #ifdef WANT_NDMP_DEVICE
83     ndmp_device_register();
84 #endif
85 }
86
87 void
88 register_device(
89     DeviceFactory factory,
90     const char ** device_prefix_list)
91 {
92     char ** tmp;
93
94     g_assert(driverList != NULL);
95     g_assert(factory != NULL);
96     g_return_if_fail(device_prefix_list != NULL);
97     g_return_if_fail(*device_prefix_list != NULL);
98
99     tmp = (char**)device_prefix_list;
100     while (*tmp != NULL) {
101         g_hash_table_insert(driverList, *tmp, (gpointer)factory);
102         tmp ++;
103     }
104 }
105
106 static DeviceFactory lookup_device_factory(const char *device_type) {
107     gpointer key, value;
108     g_assert(driverList != NULL);
109
110     if (g_hash_table_lookup_extended(driverList, device_type, &key, &value)) {
111         return (DeviceFactory)value;
112     } else {
113         return NULL;
114     }
115 }
116
117 static const GFlagsValue device_status_flags_values[] = {
118     { DEVICE_STATUS_SUCCESS,
119       "DEVICE_STATUS_SUCCESS",
120       "Success" },
121     { DEVICE_STATUS_DEVICE_ERROR,
122       "DEVICE_STATUS_DEVICE_ERROR",
123       "Device error" },
124     { DEVICE_STATUS_DEVICE_BUSY,
125       "DEVICE_STATUS_DEVICE_BUSY",
126       "Device busy" },
127     { DEVICE_STATUS_VOLUME_MISSING,
128       "DEVICE_STATUS_VOLUME_MISSING",
129       "Volume not found" },
130     { DEVICE_STATUS_VOLUME_UNLABELED,
131       "DEVICE_STATUS_VOLUME_UNLABELED",
132       "Volume not labeled" },
133     { DEVICE_STATUS_VOLUME_ERROR,
134       "DEVICE_STATUS_VOLUME_ERROR",
135       "Volume error" },
136     { 0, NULL, NULL }
137 };
138
139 GType device_status_flags_get_type(void) {
140     static GType type = 0;
141     if (G_UNLIKELY(type == 0)) {
142         type = g_flags_register_static("DeviceStatusFlags",
143                                        device_status_flags_values);
144     }
145     return type;
146 }
147
148 /* Device class definition starts here. */
149
150 struct DevicePrivate_s {
151     /* hash table mapping ID to SimpleProperty object */
152     GHashTable * simple_properties;
153
154     /* In writing mode, after a short block is written, no additional blocks
155      * are allowed the file is finished and a new file started. This is only
156      * used for assertions. */
157     gboolean wrote_short_block;
158
159     /* Holds an error message if the function returned an error. */
160     char * errmsg;
161
162     /* temporary holding place for device_status_error() */
163     char * statusmsg;
164     DeviceStatusFlags last_status;
165 };
166
167 /* This holds the default response to a particular property. */
168 typedef struct {
169     DeviceProperty *prop;
170     GValue response;
171     PropertySurety surety;
172     PropertySource source;
173 } SimpleProperty;
174
175 #define selfp (self->private)
176
177 /* here are local prototypes, so we can make function pointers. */
178 static void device_init (Device * o);
179 static void device_class_init (DeviceClass * c);
180 static void device_base_init (DeviceClass * c);
181
182 static void simple_property_free(SimpleProperty *o);
183
184 static void default_device_open_device(Device * self, char * device_name,
185                                     char * device_type, char * device_node);
186 static gboolean default_device_configure(Device *self, gboolean use_global_config);
187 static gboolean default_device_write_from_fd(Device *self,
188                                              queue_fd_t *queue_fd);
189 static gboolean default_device_read_to_fd(Device *self, queue_fd_t *queue_fd);
190 static gboolean default_device_property_get_ex(Device * self, DevicePropertyId id,
191                                                GValue * val,
192                                                PropertySurety *surety,
193                                                PropertySource *source);
194 static gboolean default_device_property_set_ex(Device *self,
195                                                DevicePropertyId id,
196                                                GValue * val,
197                                                PropertySurety surety,
198                                                PropertySource source);
199 static void set_properties_from_global_config(Device * device);
200 static void set_properties_from_device_config(Device * device, device_config_t *dc);
201
202 static gboolean property_get_block_size_fn(Device *self,
203     DevicePropertyBase *base, GValue *val,
204     PropertySurety *surety, PropertySource *source);
205
206 static gboolean property_set_block_size_fn(Device *self,
207     DevicePropertyBase *base, GValue *val,
208     PropertySurety surety, PropertySource source);
209
210 static gboolean property_get_min_block_size_fn(Device *self,
211     DevicePropertyBase *base, GValue *val,
212     PropertySurety *surety, PropertySource *source);
213
214 static gboolean property_get_max_block_size_fn(Device *self,
215     DevicePropertyBase *base, GValue *val,
216     PropertySurety *surety, PropertySource *source);
217
218 static gboolean property_get_canonical_name_fn(Device *self,
219     DevicePropertyBase *base, GValue *val,
220     PropertySurety *surety, PropertySource *source);
221
222 /* pointer to the class of our parent */
223 static GObjectClass *parent_class = NULL;
224
225 GType
226 device_get_type (void)
227 {
228     static GType type = 0;
229
230     if G_UNLIKELY(type == 0) {
231         static const GTypeInfo info = {
232             sizeof (DeviceClass),
233             (GBaseInitFunc) device_base_init,
234             (GBaseFinalizeFunc) NULL,
235             (GClassInitFunc) device_class_init,
236             (GClassFinalizeFunc) NULL,
237             NULL /* class_data */,
238             sizeof (Device),
239             0 /* n_preallocs */,
240             (GInstanceInitFunc) device_init,
241             NULL
242         };
243
244         type = g_type_register_static (G_TYPE_OBJECT, "Device", &info,
245                                        (GTypeFlags)G_TYPE_FLAG_ABSTRACT);
246     }
247
248     return type;
249 }
250
251 static void device_finalize(GObject *obj_self) {
252     Device *self G_GNUC_UNUSED = DEVICE (obj_self);
253     if(G_OBJECT_CLASS(parent_class)->finalize)
254         (* G_OBJECT_CLASS(parent_class)->finalize)(obj_self);
255
256     /* Here we call device_finish() if it hasn't been done
257        yet. Subclasses may need to do this same check earlier. */
258     if (self->access_mode != ACCESS_NULL) {
259         device_finish(self);
260     }
261
262     amfree(self->device_name);
263     amfree(self->volume_label);
264     amfree(self->volume_time);
265     amfree(self->volume_header);
266     amfree(selfp->errmsg);
267     amfree(selfp->statusmsg);
268     g_hash_table_destroy(selfp->simple_properties);
269     amfree(self->private);
270 }
271
272 static void
273 device_init (Device * self)
274 {
275     self->private = malloc(sizeof(DevicePrivate));
276     self->device_name = NULL;
277     self->access_mode = ACCESS_NULL;
278     self->is_eof = FALSE;
279     self->is_eom = FALSE;
280     self->file = -1;
281     self->block = 0;
282     self->in_file = FALSE;
283     self->volume_label = NULL;
284     self->volume_time = NULL;
285     self->status = DEVICE_STATUS_SUCCESS;
286     self->min_block_size = 1;
287     self->max_block_size = SIZE_MAX; /* subclasses *really* should choose something smaller */
288     self->block_size = DISK_BLOCK_BYTES;
289     selfp->errmsg = NULL;
290     selfp->statusmsg = NULL;
291     selfp->last_status = 0;
292     selfp->simple_properties =
293         g_hash_table_new_full(g_direct_hash,
294                               g_direct_equal,
295                               NULL,
296                               (GDestroyNotify) simple_property_free);
297 }
298
299 static void
300 device_class_init (DeviceClass * device_class)
301 {
302     GObjectClass *g_object_class = (GObjectClass*) device_class;
303
304     parent_class = g_type_class_ref (G_TYPE_OBJECT);
305
306     device_class->directtcp_supported = FALSE;
307
308     device_class->open_device = default_device_open_device;
309     device_class->configure = default_device_configure;
310     device_class->write_from_fd = default_device_write_from_fd;
311     device_class->read_to_fd = default_device_read_to_fd;
312     device_class->property_get_ex = default_device_property_get_ex;
313     device_class->property_set_ex = default_device_property_set_ex;
314     g_object_class->finalize = device_finalize;
315 }
316
317 static void
318 device_base_init (DeviceClass * device_class)
319 {
320     /* The base_init function is called once each time a child class is
321      * created, before the class_init functions (even our own) are called.  */
322
323     device_class->class_properties = g_array_new(FALSE, TRUE, sizeof(DeviceProperty));
324     device_class->class_properties_list = NULL;
325
326     device_class_register_property(device_class, PROPERTY_BLOCK_SIZE,
327             PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
328             property_get_block_size_fn,
329             property_set_block_size_fn);
330
331     device_class_register_property(device_class, PROPERTY_MIN_BLOCK_SIZE,
332             PROPERTY_ACCESS_GET_MASK,
333             property_get_min_block_size_fn,
334             NULL);
335
336     device_class_register_property(device_class, PROPERTY_MAX_BLOCK_SIZE,
337             PROPERTY_ACCESS_GET_MASK,
338             property_get_max_block_size_fn,
339             NULL);
340
341     device_class_register_property(device_class, PROPERTY_CANONICAL_NAME,
342             PROPERTY_ACCESS_GET_MASK,
343             property_get_canonical_name_fn,
344             NULL);
345
346     device_class_register_property(device_class, PROPERTY_CONCURRENCY,
347             PROPERTY_ACCESS_GET_MASK,
348             device_simple_property_get_fn,
349             device_simple_property_set_fn);
350
351     device_class_register_property(device_class, PROPERTY_STREAMING,
352             PROPERTY_ACCESS_GET_MASK,
353             device_simple_property_get_fn,
354             device_simple_property_set_fn);
355
356     device_class_register_property(device_class, PROPERTY_APPENDABLE,
357             PROPERTY_ACCESS_GET_MASK,
358             device_simple_property_get_fn,
359             device_simple_property_set_fn);
360
361     device_class_register_property(device_class, PROPERTY_PARTIAL_DELETION,
362             PROPERTY_ACCESS_GET_MASK,
363             device_simple_property_get_fn,
364             device_simple_property_set_fn);
365
366     device_class_register_property(device_class, PROPERTY_FULL_DELETION,
367             PROPERTY_ACCESS_GET_MASK,
368             device_simple_property_get_fn,
369             device_simple_property_set_fn);
370
371     device_class_register_property(device_class, PROPERTY_MEDIUM_ACCESS_TYPE,
372             PROPERTY_ACCESS_GET_MASK,
373             device_simple_property_get_fn,
374             device_simple_property_set_fn);
375
376     device_class_register_property(device_class, PROPERTY_COMMENT,
377             PROPERTY_ACCESS_GET_MASK|PROPERTY_ACCESS_SET_MASK,
378             device_simple_property_get_fn,
379             device_simple_property_set_fn);
380 }
381
382 static void simple_property_free(SimpleProperty * resp) {
383     g_value_unset(&(resp->response));
384     amfree(resp);
385 }
386
387 static char *
388 regex_message(int result, regex_t *regex) {
389     char * rval;
390     size_t size;
391
392     size = regerror(result, regex, NULL, 0);
393     rval = malloc(size);
394     regerror(result, regex, rval, size);
395
396     return rval;
397 }
398
399 static gboolean
400 handle_device_regex(const char * user_name, char ** driver_name,
401                     char ** device, char **errmsg) {
402     regex_t regex;
403     int reg_result;
404     regmatch_t pmatch[3];
405     static const char * regex_string = "^([a-z0-9]+):(.*)$";
406
407     bzero(&regex, sizeof(regex));
408
409     reg_result = regcomp(&regex, regex_string, REG_EXTENDED | REG_ICASE);
410     if (reg_result != 0) {
411         char * message = regex_message(reg_result, &regex);
412         *errmsg = newvstrallocf(*errmsg, "Error compiling regular expression \"%s\": %s\n",
413                               regex_string, message);
414         amfree(message);
415         return FALSE;
416     }
417
418     reg_result = regexec(&regex, user_name, 3, pmatch, 0);
419     if (reg_result != 0 && reg_result != REG_NOMATCH) {
420         char * message = regex_message(reg_result, &regex);
421         *errmsg = newvstrallocf(*errmsg,
422                         "Error applying regular expression \"%s\" to string \"%s\": %s\n",
423                         user_name, regex_string, message);
424         amfree(message);
425         regfree(&regex);
426         return FALSE;
427     } else if (reg_result == REG_NOMATCH) {
428 #ifdef WANT_TAPE_DEVICE
429         g_warning(
430                 "\"%s\" uses deprecated device naming convention; \n"
431                 "using \"tape:%s\" instead.\n",
432                 user_name, user_name);
433         *driver_name = stralloc("tape");
434         *device = stralloc(user_name);
435 #else /* !WANT_TAPE_DEVICE */
436         errmsg = newvstrallocf(errmsg, "\"%s\" is not a valid device name.\n", user_name);
437         regfree(&regex);
438         return FALSE;
439 #endif /* WANT_TAPE_DEVICE */
440     } else {
441         *driver_name = find_regex_substring(user_name, pmatch[1]);
442         *device = find_regex_substring(user_name, pmatch[2]);
443     }
444     regfree(&regex);
445     return TRUE;
446 }
447
448 /* helper function for device_open */
449 static Device *
450 make_null_error(char *errmsg, DeviceStatusFlags status)
451 {
452     DeviceFactory factory;
453     Device *device;
454
455     factory = lookup_device_factory("null");
456     g_assert(factory != NULL);
457
458     device = factory("null:", "null", "");
459     device_set_error(device, errmsg, status);
460
461     return device;
462 }
463
464 char *
465 device_unaliased_name(
466     char *device_name)
467 {
468     device_config_t *dc;
469     char *unaliased_name;
470
471     /* look up the unaliased device name in the configuration */
472     if ((dc = lookup_device_config(device_name))) {
473         if (!(unaliased_name = device_config_get_tapedev(dc))
474             || unaliased_name[0] == '\0') {
475             return NULL;
476         }
477     } else {
478         unaliased_name = device_name;
479     }
480
481     return unaliased_name;
482 }
483
484 Device*
485 device_open (char * device_name)
486 {
487     char *device_type = NULL;
488     char *device_node = NULL;
489     char *errmsg = NULL;
490     char *unaliased_name = NULL;
491     DeviceFactory factory;
492     Device *device;
493
494     g_assert(device_name != NULL);
495
496     if (driverList == NULL) {
497         g_critical("device_open() called without device_api_init()!");
498         g_assert_not_reached();
499     }
500
501     if (device_name == NULL)
502         return make_null_error(stralloc(_("No device name specified")), DEVICE_STATUS_DEVICE_ERROR);
503
504     unaliased_name = device_unaliased_name(device_name);
505     if (!unaliased_name) {
506         return make_null_error(
507                 vstrallocf(_("Device '%s' has no tapedev"), device_name),
508                 DEVICE_STATUS_DEVICE_ERROR);
509     }
510
511     if (!handle_device_regex(unaliased_name, &device_type, &device_node,
512                              &errmsg)) {
513         amfree(device_type);
514         amfree(device_node);
515         return make_null_error(errmsg, DEVICE_STATUS_DEVICE_ERROR);
516     }
517
518     factory = lookup_device_factory(device_type);
519
520     if (factory == NULL) {
521         Device *nulldev = make_null_error(vstrallocf(_("Device type %s is not known."),
522             device_type), DEVICE_STATUS_DEVICE_ERROR);
523         amfree(device_type);
524         amfree(device_node);
525         return nulldev;
526     }
527
528     device = factory(device_name, device_type, device_node);
529     g_assert(device != NULL); /* factories must always return a device */
530
531     amfree(device_type);
532     amfree(device_node);
533
534     return device;
535 }
536
537 char *
538 device_error(Device * self)
539 {
540     if (self == NULL) {
541         return device_error_or_status(self);
542     } else if (selfp->errmsg) {
543         return selfp->errmsg;
544     } else {
545         return "Unknown Device error";
546     }
547 }
548
549 char *
550 device_status_error(Device * self)
551 {
552     char **status_strv;
553     char *statusmsg;
554
555     if (self == NULL) {
556         return device_error_or_status(self);
557     }
558
559     /* reuse a previous statusmsg, if it was for the same status */
560     if (selfp->statusmsg && selfp->last_status == self->status)
561         return selfp->statusmsg;
562
563     amfree(selfp->statusmsg);
564
565     status_strv = g_flags_nick_to_strv(self->status, DEVICE_STATUS_FLAGS_TYPE);
566     g_assert(g_strv_length(status_strv) > 0);
567     if (g_strv_length(status_strv) == 1) {
568         statusmsg = stralloc(*status_strv);
569     } else {
570         char * status_list = g_english_strjoinv(status_strv, "or");
571         statusmsg = g_strdup_printf("one of %s", status_list);
572         amfree(status_list);
573     }
574     g_strfreev(status_strv);
575
576     selfp->statusmsg = statusmsg;
577     selfp->last_status = self->status;
578     return statusmsg;
579 }
580
581 char *
582 device_error_or_status(Device * self)
583 {
584     if (self == NULL) {
585         return "Device is NULL";
586     } else if (selfp->errmsg) {
587         return selfp->errmsg;
588     } else {
589         return device_status_error(self);
590     }
591 }
592
593 void
594 device_set_error(Device *self, char *errmsg, DeviceStatusFlags new_flags)
595 {
596     char **flags_strv;
597     char *flags_str;
598     char *device_name;
599
600     if (!self) {
601         g_warning("device_set_error called with a NULL device: '%s'", errmsg? errmsg:"(NULL)");
602         amfree(errmsg);
603         return;
604     }
605
606     device_name = self->device_name? self->device_name : "(unknown device)";
607
608     if (errmsg && (!selfp->errmsg || strcmp(errmsg, selfp->errmsg) != 0))
609         g_debug("Device %s error = '%s'", device_name, errmsg);
610
611     amfree(selfp->errmsg);
612     selfp->errmsg = errmsg;
613
614     if (new_flags != DEVICE_STATUS_SUCCESS) {
615         flags_strv = g_flags_name_to_strv(new_flags, DEVICE_STATUS_FLAGS_TYPE);
616         g_assert(g_strv_length(flags_strv) > 0);
617         flags_str = g_english_strjoinv(flags_strv, "and");
618         g_debug("Device %s setting status flag(s): %s", device_name, flags_str);
619         amfree(flags_str);
620         g_strfreev(flags_strv);
621     }
622
623     self->status = new_flags;
624 }
625
626 char * device_build_amanda_header(Device * self, const dumpfile_t * info,
627                                   size_t *size) {
628     return build_header(info, size, self->block_size);
629 }
630
631 dumpfile_t * make_tapestart_header(Device * self, char * label,
632                                    char * timestamp) {
633     dumpfile_t * rval;
634     GValue val;
635     bzero(&val, sizeof(val));
636
637     g_assert(label != NULL);
638
639     rval = malloc(sizeof(*rval));
640     fh_init(rval);
641     rval->type = F_TAPESTART;
642     if (device_property_get(self, PROPERTY_BLOCK_SIZE, &val)) {
643         rval->blocksize = g_value_get_int(&val);
644         g_value_unset(&val);
645     }
646
647     amfree(self->volume_time);
648     if (get_timestamp_state(timestamp) == TIME_STATE_REPLACE) {
649         self->volume_time = get_proper_stamp_from_time(time(NULL));
650     } else {
651         self->volume_time = g_strdup(timestamp);
652     }
653     strncpy(rval->datestamp, self->volume_time, sizeof(rval->datestamp));
654     strncpy(rval->name, label, sizeof(rval->name));
655
656     return rval;
657 }
658
659 dumpfile_t * make_tapeend_header(void) {
660     dumpfile_t * rval;
661     char * timestamp;
662
663     rval = malloc(sizeof(*rval));
664     rval->type = F_TAPEEND;
665     timestamp = get_timestamp_from_time(time(NULL));
666     strncpy(rval->datestamp, timestamp, sizeof(rval->datestamp));
667     amfree(timestamp);
668     return rval;
669 }
670
671 /* Try setting the blocksize on a device. Check results, fallback, and
672  * set error status for problems. */
673 static gboolean
674 try_set_blocksize(Device * device, guint blocksize) {
675     GValue val;
676     gboolean success;
677     bzero(&val, sizeof(val));
678
679     g_value_init(&val, G_TYPE_INT);
680     g_value_set_int(&val, blocksize);
681     success = device_property_set(device, PROPERTY_BLOCK_SIZE, &val);
682     g_value_unset(&val);
683
684     if (!success) {
685         device_set_error(device,
686             vstrallocf(_("Setting BLOCK_SIZE to %u "
687                     "not supported for device %s.\n"),
688                     blocksize, device->device_name),
689             DEVICE_STATUS_DEVICE_ERROR);
690     }
691
692     return success;
693 }
694
695 /* A GHFunc (callback for g_hash_table_foreach) */
696 static void set_device_property(gpointer key_p, gpointer value_p,
697                                    gpointer user_data_p) {
698     char   * property_s = key_p;
699     property_t * property = value_p;
700     Device * device = user_data_p;
701     const DevicePropertyBase* property_base;
702     GValue property_value;
703     char   * value;
704
705     g_return_if_fail(IS_DEVICE(device));
706     g_return_if_fail(property_s != NULL);
707     g_return_if_fail(property != NULL);
708     g_return_if_fail(property->values != NULL);
709
710     /* don't continue beating on a device that's already erroring */
711     if (device_in_error(device)) return;
712
713     property_base = device_property_get_by_name(property_s);
714     if (property_base == NULL) {
715         /* Nonexistant property name. */
716         device_set_error(device,
717             vstrallocf(_("unknown device property name '%s'"), property_s),
718             DEVICE_STATUS_DEVICE_ERROR);
719         return;
720     }
721     if (g_slist_length(property->values) > 1) {
722         device_set_error(device,
723             vstrallocf(_("multiple values for device property '%s'"), property_s),
724             DEVICE_STATUS_DEVICE_ERROR);
725         return;
726     }
727
728     bzero(&property_value, sizeof(property_value));
729     g_value_init(&property_value, property_base->type);
730     value = property->values->data;
731     if (!g_value_set_from_string(&property_value, value)) {
732         /* Value type could not be interpreted. */
733         device_set_error(device,
734             vstrallocf(_("Could not parse property value '%s' for property '%s' (property type %s)"),
735                         value, property_base->name, g_type_name(property_base->type)),
736             DEVICE_STATUS_DEVICE_ERROR);
737         return;
738     } else {
739         g_assert (G_VALUE_HOLDS(&property_value, property_base->type));
740     }
741
742     if (!device_property_set(device, property_base->ID, &property_value)) {
743         /* Device rejects property. */
744         if (!device_in_error(device)) {
745             device_set_error(device,
746                 vstrallocf(_("Could not set property '%s' to '%s' on %s"),
747                         property_base->name, value, device->device_name),
748                 DEVICE_STATUS_DEVICE_ERROR);
749         }
750         return;
751     }
752 }
753
754 /* Set up properties based on various taper-related configuration parameters
755  * and from the tapetype.
756  */
757 static void
758 set_properties_from_global_config(Device * device) {
759     char * tapetype_name = getconf_str(CNF_TAPETYPE);
760     if (tapetype_name != NULL) {
761         tapetype_t * tapetype = lookup_tapetype(tapetype_name);
762         if (tapetype != NULL) {
763             GValue val;
764             guint64 length;
765             guint blocksize_kb;
766             gboolean success;
767
768             bzero(&val, sizeof(GValue));
769
770             if (tapetype_seen(tapetype, TAPETYPE_LENGTH)) {
771                 length = tapetype_get_length(tapetype);
772                 g_value_init(&val, G_TYPE_UINT64);
773                 g_value_set_uint64(&val, length * 1024);
774                 /* If this fails, it's not really an error. */
775                 device_property_set(device, PROPERTY_MAX_VOLUME_USAGE, &val);
776                 g_value_unset(&val);
777             }
778
779             if (tapetype_seen(tapetype, TAPETYPE_READBLOCKSIZE)) {
780                 blocksize_kb = tapetype_get_readblocksize(tapetype);
781                 g_value_init(&val, G_TYPE_UINT);
782                 g_value_set_uint(&val, blocksize_kb * 1024);
783                 success = device_property_set(device,
784                                               PROPERTY_READ_BLOCK_SIZE,
785                                               &val);
786                 g_value_unset(&val);
787                 if (!success) {
788                     /* a non-fatal error */
789                     g_warning("Setting READ_BLOCK_SIZE to %ju not supported for device %s.",
790                             1024*(uintmax_t)blocksize_kb, device->device_name);
791                 }
792             }
793
794             if (tapetype_seen(tapetype, TAPETYPE_BLOCKSIZE)) {
795                 blocksize_kb = tapetype_get_blocksize(tapetype);
796                 /* TODO: handle errors */
797                 (void)try_set_blocksize(device, blocksize_kb * 1024);
798             }
799         }
800     }
801
802     g_hash_table_foreach(getconf_proplist(CNF_DEVICE_PROPERTY),
803                          set_device_property, device);
804 }
805
806 /* Set properties specified within a device definition */
807 static void
808 set_properties_from_device_config(Device * device, device_config_t *dc) {
809     g_hash_table_foreach(device_config_get_property(dc),
810                          set_device_property, device);
811 }
812
813 static gboolean
814 default_device_configure(Device *self, gboolean use_global_config)
815 {
816     device_config_t *dc;
817
818     if (device_in_error(self))
819         return FALSE;
820
821     if (use_global_config)
822         set_properties_from_global_config(self);
823
824     if (device_in_error(self))
825         return FALSE;
826
827     if ((dc = lookup_device_config(self->device_name)))
828         set_properties_from_device_config(self, dc);
829
830     return !device_in_error(self);
831 }
832
833 void device_clear_volume_details(Device * device) {
834     if (device == NULL || device->access_mode != ACCESS_NULL) {
835         return;
836     }
837
838     amfree(device->volume_label);
839     amfree(device->volume_time);
840 }
841
842 /* Here we put default implementations of virtual functions. Since
843    this class is virtual, many of these functions offer at best
844    incomplete functionality. But they do offer the useful commonality
845    that all devices can expect to need. */
846
847 static void default_device_open_device(Device * self, char * device_name,
848                     char * device_type G_GNUC_UNUSED, char * device_node G_GNUC_UNUSED) {
849     /* Set the device_name property */
850     self->device_name = stralloc(device_name);
851 }
852
853 static gboolean
854 property_get_block_size_fn(
855         Device *self,
856         DevicePropertyBase *base G_GNUC_UNUSED,
857         GValue *val,
858         PropertySurety *surety,
859         PropertySource *source)
860 {
861     g_value_unset_init(val, G_TYPE_INT);
862     g_assert(self->block_size < G_MAXINT); /* gsize -> gint */
863     g_value_set_int(val, (gint)self->block_size);
864
865     if (surety)
866         *surety = self->block_size_surety;
867
868     if (source)
869         *source = self->block_size_source;
870
871     return TRUE;
872 }
873
874 static gboolean
875 property_set_block_size_fn(
876         Device *self,
877         DevicePropertyBase *base G_GNUC_UNUSED,
878         GValue *val,
879         PropertySurety surety,
880         PropertySource source)
881 {
882     gint block_size = g_value_get_int(val);
883
884     g_assert(block_size >= 0); /* int -> gsize (unsigned) */
885     if ((gsize)block_size < self->min_block_size
886        || (gsize)block_size > self->max_block_size)
887         return FALSE;
888
889     self->block_size = block_size;
890     self->block_size_surety = surety;
891     self->block_size_source = source;
892
893     return TRUE;
894 }
895
896 static gboolean
897 property_get_min_block_size_fn(
898         Device *self,
899         DevicePropertyBase *base G_GNUC_UNUSED,
900         GValue *val,
901         PropertySurety *surety,
902         PropertySource *source)
903 {
904     g_value_unset_init(val, G_TYPE_UINT);
905     g_assert(self->block_size < G_MAXUINT); /* gsize -> guint */
906     g_value_set_uint(val, (guint)self->min_block_size);
907
908     if (surety)
909         *surety = PROPERTY_SURETY_GOOD;
910
911     if (source)
912         *source = PROPERTY_SOURCE_DEFAULT;
913
914     return TRUE;
915 }
916
917 static gboolean
918 property_get_max_block_size_fn(
919         Device *self,
920         DevicePropertyBase *base G_GNUC_UNUSED,
921         GValue *val,
922         PropertySurety *surety,
923         PropertySource *source)
924 {
925     g_value_unset_init(val, G_TYPE_UINT);
926     g_assert(self->block_size < G_MAXUINT); /* gsize -> guint */
927     g_value_set_uint(val, (guint)self->max_block_size);
928
929     if (surety)
930         *surety = PROPERTY_SURETY_GOOD;
931
932     if (source)
933         *source = PROPERTY_SOURCE_DEFAULT;
934
935     return TRUE;
936 }
937
938 static gboolean
939 property_get_canonical_name_fn(
940         Device *self,
941         DevicePropertyBase *base G_GNUC_UNUSED,
942         GValue *val,
943         PropertySurety *surety,
944         PropertySource *source)
945 {
946     g_value_unset_init(val, G_TYPE_STRING);
947     g_value_set_string(val, self->device_name);
948
949     if (surety)
950         *surety = PROPERTY_SURETY_GOOD;
951
952     if (source)
953         *source = PROPERTY_SOURCE_DEFAULT;
954
955     return TRUE;
956 }
957
958 /* util function */
959 static PropertyPhaseFlags
960 state_to_phase(
961     Device *self)
962 {
963     if (self->access_mode == ACCESS_NULL) {
964         return PROPERTY_PHASE_BEFORE_START;
965     } else if (IS_WRITABLE_ACCESS_MODE(self->access_mode)) {
966         if (self->in_file) {
967             return PROPERTY_PHASE_INSIDE_FILE_WRITE;
968         } else {
969             return PROPERTY_PHASE_BETWEEN_FILE_WRITE;
970         }
971     } else { /* read mode */
972         if (self->in_file) {
973             return PROPERTY_PHASE_INSIDE_FILE_READ;
974         } else {
975             return PROPERTY_PHASE_BETWEEN_FILE_READ;
976         }
977     }
978 }
979
980 /* This default implementation serves up static responses, and
981    implements a few default responses based on values from the Device
982    struct. */
983 static gboolean
984 default_device_property_get_ex(
985         Device * self,
986         DevicePropertyId id,
987         GValue * val,
988         PropertySurety *surety,
989         PropertySource *source)
990 {
991     DeviceProperty *prop;
992     GArray *class_properties;
993     PropertyPhaseFlags cur_phase;
994
995     /* Most of this function's job is to sanity-check everything, then
996      * call the relevant getter. */
997
998     class_properties = DEVICE_GET_CLASS(self)->class_properties;
999     if (id >= class_properties->len)
1000         return FALSE;
1001
1002     prop = &g_array_index(class_properties, DeviceProperty, id);
1003     if (prop->base == NULL)
1004         return FALSE;
1005
1006     if (val || surety || source) {
1007         /* check the phase */
1008         cur_phase = state_to_phase(self);
1009         if (!(prop->access & cur_phase))
1010             return FALSE;
1011
1012         if (prop->getter == NULL)
1013             return FALSE;
1014
1015         if (!prop->getter(self, prop->base, val, surety, source))
1016             return FALSE;
1017     }
1018
1019     return TRUE;
1020 }
1021
1022 static gboolean
1023 default_device_property_set_ex(
1024     Device *self,
1025     DevicePropertyId id,
1026     GValue * val,
1027     PropertySurety surety,
1028     PropertySource source)
1029 {
1030     DeviceProperty *prop;
1031     GArray *class_properties;
1032     PropertyPhaseFlags cur_phase;
1033
1034     /* Most of this function's job is to sanity-check everything, then
1035      * call the relevant setter. */
1036
1037     if (device_in_error(self))
1038         return FALSE;
1039
1040     class_properties = DEVICE_GET_CLASS(self)->class_properties;
1041     if (id >= class_properties->len)
1042         return FALSE;
1043
1044     prop = &g_array_index(class_properties, DeviceProperty, id);
1045     if (prop->base == NULL)
1046         return FALSE;
1047
1048     /* check that the type matches */
1049     if (!G_VALUE_HOLDS(val, prop->base->type))
1050         return FALSE;
1051
1052     /* check the phase */
1053     cur_phase = state_to_phase(self) << PROPERTY_PHASE_SHIFT;
1054     if (!(prop->access & cur_phase))
1055         return FALSE;
1056
1057     if (prop->setter == NULL)
1058         return FALSE;
1059
1060     if (!prop->setter(self, prop->base, val, surety, source))
1061         return FALSE;
1062
1063     return TRUE;
1064 }
1065
1066 const GSList *
1067 device_property_get_list (Device * self)
1068 {
1069     g_assert(IS_DEVICE(self));
1070
1071     return DEVICE_GET_CLASS(self)->class_properties_list;
1072 }
1073
1074 static gboolean
1075 default_device_read_to_fd(Device *self, queue_fd_t *queue_fd) {
1076     GValue val;
1077     StreamingRequirement streaming_mode;
1078
1079     if (device_in_error(self)) return FALSE;
1080
1081     /* Get the device's parameters */
1082     bzero(&val, sizeof(val));
1083     if (!device_property_get(self, PROPERTY_STREAMING, &val)
1084         || !G_VALUE_HOLDS(&val, STREAMING_REQUIREMENT_TYPE)) {
1085         streaming_mode = STREAMING_REQUIREMENT_REQUIRED;
1086     } else {
1087         streaming_mode = g_value_get_enum(&val);
1088     }
1089
1090     return QUEUE_SUCCESS ==
1091         do_consumer_producer_queue_full(
1092             device_read_producer,
1093             self,
1094             fd_write_consumer,
1095             queue_fd,
1096             self->block_size,
1097             DEFAULT_MAX_BUFFER_MEMORY,
1098             streaming_mode);
1099 }
1100
1101 static gboolean
1102 default_device_write_from_fd(Device *self, queue_fd_t *queue_fd) {
1103     GValue val;
1104     StreamingRequirement streaming_mode;
1105
1106     if (device_in_error(self)) return FALSE;
1107
1108     /* Get the device's parameters */
1109     bzero(&val, sizeof(val));
1110     if (!device_property_get(self, PROPERTY_STREAMING, &val)
1111         || !G_VALUE_HOLDS(&val, STREAMING_REQUIREMENT_TYPE)) {
1112         streaming_mode = STREAMING_REQUIREMENT_REQUIRED;
1113     } else {
1114         streaming_mode = g_value_get_enum(&val);
1115     }
1116
1117     return QUEUE_SUCCESS ==
1118         do_consumer_producer_queue_full(
1119             fd_read_producer,
1120             queue_fd,
1121             device_write_consumer,
1122             self,
1123             self->block_size,
1124             DEFAULT_MAX_BUFFER_MEMORY,
1125             streaming_mode);
1126 }
1127
1128 /* XXX WARNING XXX
1129  * All the functions below this comment are stub functions that do nothing
1130  * but implement the virtual function table. Call these functions and they
1131  * will do what you expect vis-a-vis virtual functions. But don't put code
1132  * in them beyond error checking and VFT lookup. */
1133
1134 void
1135 device_open_device (Device * self, char * device_name,
1136         char * device_type, char * device_node)
1137 {
1138     DeviceClass *klass;
1139
1140     g_assert(IS_DEVICE(self));
1141     g_assert(device_name != NULL);
1142
1143     klass = DEVICE_GET_CLASS(self);
1144     g_assert(klass->open_device);
1145     (klass->open_device)(self, device_name, device_type, device_node);
1146 }
1147
1148 DeviceStatusFlags device_read_label(Device * self) {
1149     DeviceClass * klass;
1150
1151     g_assert(self != NULL);
1152     g_assert(IS_DEVICE(self));
1153     g_assert(self->access_mode == ACCESS_NULL);
1154
1155     klass = DEVICE_GET_CLASS(self);
1156     g_assert(klass->read_label);
1157     return (klass->read_label)(self);
1158 }
1159
1160 gboolean
1161 device_finish (Device * self) {
1162     DeviceClass *klass;
1163
1164     g_assert(IS_DEVICE (self));
1165
1166     klass = DEVICE_GET_CLASS(self);
1167     g_assert(klass->finish);
1168     return (klass->finish)(self);
1169 }
1170
1171 gboolean
1172 device_configure (Device * self, gboolean use_global_config)
1173 {
1174     DeviceClass *klass;
1175
1176     g_assert(IS_DEVICE (self));
1177     g_assert(self->access_mode == ACCESS_NULL);
1178
1179     klass = DEVICE_GET_CLASS(self);
1180     if(klass->configure) {
1181         return (klass->configure)(self, use_global_config);
1182     } else {
1183         device_set_error(self,
1184             stralloc(_("Unimplemented method")),
1185             DEVICE_STATUS_DEVICE_ERROR);
1186         return FALSE;
1187     }
1188 }
1189
1190 gboolean
1191 device_start (Device * self, DeviceAccessMode mode,
1192               char * label, char * timestamp)
1193 {
1194     DeviceClass *klass;
1195     char * local_timestamp = NULL;
1196     gboolean rv;
1197
1198     g_assert(IS_DEVICE (self));
1199     g_assert(mode != ACCESS_NULL);
1200     g_assert(mode != ACCESS_WRITE || label != NULL);
1201
1202     klass = DEVICE_GET_CLASS(self);
1203     g_assert(klass->start);
1204
1205     /* For a good combination of synchronization and public simplicity,
1206        this stub function does not require a timestamp, but the actual
1207        implementation function does. We generate the timestamp here with
1208        time(). */
1209     if (mode == ACCESS_WRITE &&
1210         get_timestamp_state(timestamp) == TIME_STATE_REPLACE) {
1211         local_timestamp = timestamp =
1212             get_proper_stamp_from_time(time(NULL));
1213     }
1214
1215     rv = (klass->start)(self, mode, label, timestamp);
1216     amfree(local_timestamp);
1217     return rv;
1218 }
1219
1220 gboolean
1221 device_write_block (Device * self, guint size, gpointer block)
1222 {
1223     DeviceClass *klass;
1224
1225     g_assert(IS_DEVICE (self));
1226     g_assert(size > 0);
1227
1228     /* these are all things that the caller should take care to
1229      * guarantee, so we just assert them here */
1230     g_assert(size <= self->block_size);
1231     g_assert(self->in_file);
1232     g_assert(!selfp->wrote_short_block);
1233     g_assert(block != NULL);
1234     g_assert(IS_WRITABLE_ACCESS_MODE(self->access_mode));
1235
1236     if (size < self->block_size)
1237         selfp->wrote_short_block = TRUE;
1238
1239     klass = DEVICE_GET_CLASS(self);
1240     g_assert(klass->write_block);
1241     return (*klass->write_block)(self,size, block);
1242 }
1243
1244 gboolean
1245 device_write_from_fd (Device * self, queue_fd_t * queue_fd)
1246 {
1247     DeviceClass *klass;
1248
1249     g_assert(IS_DEVICE (self));
1250     g_assert(queue_fd->fd >= 0);
1251     g_assert(IS_WRITABLE_ACCESS_MODE(self->access_mode));
1252
1253     klass = DEVICE_GET_CLASS(self);
1254     g_assert(klass->write_from_fd);
1255     return (klass->write_from_fd)(self,queue_fd);
1256 }
1257
1258 gboolean
1259 device_start_file (Device * self, dumpfile_t * jobInfo) {
1260     DeviceClass * klass;
1261
1262     g_assert(IS_DEVICE (self));
1263     g_assert(!(self->in_file));
1264     g_assert(jobInfo != NULL);
1265
1266     selfp->wrote_short_block = FALSE;
1267
1268     klass = DEVICE_GET_CLASS(self);
1269     g_assert(klass->start_file);
1270     return (klass->start_file)(self, jobInfo );
1271 }
1272
1273 gboolean
1274 device_finish_file (Device * self)
1275 {
1276     DeviceClass *klass;
1277
1278     g_assert(IS_DEVICE (self));
1279     g_assert(IS_WRITABLE_ACCESS_MODE(self->access_mode));
1280     g_assert(self->in_file);
1281
1282     klass = DEVICE_GET_CLASS(self);
1283     g_assert(klass->finish_file);
1284     return (klass->finish_file)(self);
1285 }
1286
1287 dumpfile_t*
1288 device_seek_file (Device * self, guint file)
1289 {
1290     DeviceClass *klass;
1291
1292     g_assert(IS_DEVICE (self));
1293     g_assert(self->access_mode == ACCESS_READ);
1294
1295     klass = DEVICE_GET_CLASS(self);
1296     g_assert(klass->seek_file);
1297     return (klass->seek_file)(self,file);
1298 }
1299
1300 gboolean
1301 device_seek_block (Device * self, guint64 block)
1302 {
1303     DeviceClass *klass;
1304
1305     g_assert(IS_DEVICE (self));
1306     g_assert(self->access_mode == ACCESS_READ);
1307     g_assert(self->in_file);
1308
1309     klass = DEVICE_GET_CLASS(self);
1310     g_assert(klass->seek_block);
1311     return (klass->seek_block)(self,block);
1312 }
1313
1314 int
1315 device_read_block (Device * self, gpointer buffer, int * size)
1316 {
1317     DeviceClass *klass;
1318
1319     g_assert(IS_DEVICE (self));
1320     g_assert(size != NULL);
1321     g_assert(self->access_mode == ACCESS_READ);
1322
1323     if (*size != 0) {
1324         g_assert(buffer != NULL);
1325     }
1326
1327     klass = DEVICE_GET_CLASS(self);
1328     g_assert(klass->read_block);
1329     return (klass->read_block)(self,buffer,size);
1330 }
1331
1332 gboolean
1333 device_read_to_fd (Device * self, queue_fd_t *queue_fd)
1334 {
1335     DeviceClass *klass;
1336
1337     g_assert(IS_DEVICE (self));
1338     g_assert(queue_fd->fd >= 0);
1339     g_assert(self->access_mode == ACCESS_READ);
1340
1341     klass = DEVICE_GET_CLASS(self);
1342     g_assert(klass->read_to_fd);
1343     return (klass->read_to_fd)(self,queue_fd);
1344 }
1345
1346
1347 gboolean
1348 device_property_get_ex(
1349         Device * self,
1350         DevicePropertyId id,
1351         GValue * val,
1352         PropertySurety *surety,
1353         PropertySource *source)
1354 {
1355     DeviceClass *klass;
1356
1357     g_assert(IS_DEVICE (self));
1358     g_assert(device_property_get_by_id(id) != NULL);
1359
1360     klass = DEVICE_GET_CLASS(self);
1361
1362     g_assert(klass->property_get_ex);
1363     return (klass->property_get_ex)(self, id, val, surety, source);
1364 }
1365
1366 gboolean
1367 device_property_set_ex(
1368         Device * self,
1369         DevicePropertyId id,
1370         GValue * val,
1371         PropertySurety surety,
1372         PropertySource source)
1373 {
1374     DeviceClass *klass;
1375
1376     g_assert(IS_DEVICE (self));
1377
1378     klass = DEVICE_GET_CLASS(self);
1379
1380     g_assert(klass->property_set_ex);
1381     return (klass->property_set_ex)(self, id, val, surety, source);
1382 }
1383
1384 gboolean
1385 device_recycle_file (Device * self, guint filenum)
1386 {
1387     DeviceClass *klass;
1388
1389     g_assert(self != NULL);
1390     g_assert(IS_DEVICE (self));
1391     g_assert(self->access_mode == ACCESS_APPEND);
1392     g_assert(!self->in_file);
1393
1394     klass = DEVICE_GET_CLASS(self);
1395
1396     g_assert(klass->recycle_file);
1397     return (klass->recycle_file)(self,filenum);
1398 }
1399
1400 gboolean
1401 device_erase (Device * self)
1402 {
1403     DeviceClass *klass;
1404
1405     g_assert(IS_DEVICE (self));
1406     g_assert(self->access_mode == ACCESS_NULL);
1407     g_assert(!self->in_file);
1408
1409     klass = DEVICE_GET_CLASS(self);
1410     if(klass->erase) {
1411         return (klass->erase)(self);
1412     } else {
1413         device_set_error(self,
1414             stralloc(_("Unimplemented method")),
1415             DEVICE_STATUS_DEVICE_ERROR);
1416         return FALSE;
1417     }
1418 }
1419
1420 gboolean
1421 device_eject (Device * self)
1422 {
1423     DeviceClass *klass;
1424
1425     g_assert(IS_DEVICE (self));
1426     g_assert(self->access_mode == ACCESS_NULL);
1427     g_assert(!self->in_file);
1428
1429     klass = DEVICE_GET_CLASS(self);
1430     if (klass->eject) {
1431         return (klass->eject)(self);
1432     } else {
1433         return TRUE;
1434     }
1435 }
1436
1437 gboolean
1438 device_listen(
1439     Device *self,
1440     gboolean for_writing,
1441     DirectTCPAddr **addrs)
1442 {
1443     DeviceClass *klass;
1444
1445     klass = DEVICE_GET_CLASS(self);
1446     if(klass->listen) {
1447         return (klass->listen)(self, for_writing, addrs);
1448     } else {
1449         device_set_error(self,
1450             stralloc(_("Unimplemented method")),
1451             DEVICE_STATUS_DEVICE_ERROR);
1452         return FALSE;
1453     }
1454 }
1455
1456 gboolean
1457 device_accept(
1458     Device *self,
1459     DirectTCPConnection **conn,
1460     ProlongProc prolong,
1461     gpointer prolong_data)
1462 {
1463     DeviceClass *klass;
1464
1465     klass = DEVICE_GET_CLASS(self);
1466     if(klass->accept) {
1467         return (klass->accept)(self, conn, prolong, prolong_data);
1468     } else {
1469         device_set_error(self,
1470             stralloc(_("Unimplemented method")),
1471             DEVICE_STATUS_DEVICE_ERROR);
1472         return FALSE;
1473     }
1474 }
1475
1476 gboolean
1477 device_write_from_connection(
1478     Device *self,
1479     guint64 size,
1480     guint64 *actual_size)
1481 {
1482     DeviceClass *klass;
1483
1484     klass = DEVICE_GET_CLASS(self);
1485
1486     g_assert(self->in_file);
1487     g_assert(IS_WRITABLE_ACCESS_MODE(self->access_mode));
1488
1489     if(klass->write_from_connection) {
1490         return (klass->write_from_connection)(self, size, actual_size);
1491     } else {
1492         device_set_error(self,
1493             stralloc(_("Unimplemented method")),
1494             DEVICE_STATUS_DEVICE_ERROR);
1495         return FALSE;
1496     }
1497 }
1498
1499 gboolean
1500 device_read_to_connection(
1501     Device *self,
1502     guint64 size,
1503     guint64 *actual_size)
1504 {
1505     DeviceClass *klass;
1506
1507     g_assert(self->in_file);
1508     g_assert(self->access_mode == ACCESS_READ);
1509
1510     klass = DEVICE_GET_CLASS(self);
1511     if(klass->read_to_connection) {
1512         return (klass->read_to_connection)(self, size, actual_size);
1513     } else {
1514         device_set_error(self,
1515             stralloc(_("Unimplemented method")),
1516             DEVICE_STATUS_DEVICE_ERROR);
1517         return FALSE;
1518     }
1519 }
1520
1521 gboolean
1522 device_use_connection(
1523     Device *self,
1524     DirectTCPConnection *conn)
1525 {
1526     DeviceClass *klass;
1527
1528     g_assert(self->access_mode == ACCESS_NULL);
1529
1530     klass = DEVICE_GET_CLASS(self);
1531     if(klass->use_connection) {
1532         return (klass->use_connection)(self, conn);
1533     } else {
1534         device_set_error(self,
1535             stralloc(_("Unimplemented method")),
1536             DEVICE_STATUS_DEVICE_ERROR);
1537         return FALSE;
1538     }
1539 }
1540
1541 /* Property handling */
1542
1543 void
1544 device_class_register_property(
1545         DeviceClass *klass,
1546         DevicePropertyId id,
1547         PropertyAccessFlags access,
1548         PropertyGetFn getter,
1549         PropertySetFn setter)
1550 {
1551     DevicePropertyBase *base;
1552     DeviceProperty *prop;
1553     GSList *proplist;
1554     guint i;
1555
1556     g_assert(klass != NULL);
1557
1558     base = device_property_get_by_id(id);
1559     g_assert(base != NULL);
1560
1561     if (klass->class_properties->len <= id) {
1562         g_array_set_size(klass->class_properties, id+1);
1563     }
1564
1565     prop = &g_array_index(klass->class_properties, DeviceProperty, id);
1566     prop->base = base;
1567     prop->access = access;
1568     prop->getter = getter;
1569     prop->setter = setter;
1570
1571     /* completely rewrite the list of prop pointers, as they may have changed,
1572      * or we may have replaced an existing property*/
1573
1574     if (klass->class_properties_list) {
1575         g_slist_free(klass->class_properties_list);
1576     }
1577
1578     proplist = NULL;
1579     for (i = 0; i < klass->class_properties->len; i++) {
1580         prop = &g_array_index(klass->class_properties, DeviceProperty, i);
1581         if (!prop->base)
1582             continue;
1583         proplist = g_slist_prepend(proplist, prop);
1584     }
1585
1586     klass->class_properties_list = proplist;
1587 }
1588
1589 gboolean
1590 device_set_simple_property(
1591         Device *self,
1592         DevicePropertyId id,
1593         GValue *val,
1594         PropertySurety surety,
1595         PropertySource source)
1596 {
1597     SimpleProperty *simp;
1598     DeviceProperty *prop;
1599
1600     prop = &g_array_index(DEVICE_GET_CLASS(self)->class_properties,
1601                           DeviceProperty, id);
1602
1603     /* these assertions should already be checked, but let's be sure */
1604     g_assert(prop->base != NULL);   /* prop must be registered with device */
1605     g_assert(G_VALUE_HOLDS(val, prop->base->type));
1606
1607     simp = g_new0(SimpleProperty, 1);
1608     simp->prop = prop;
1609     g_value_unset_copy(val, &(simp->response));
1610     simp->surety = surety;
1611     simp->source = source;
1612
1613     g_hash_table_insert(selfp->simple_properties,
1614                         GINT_TO_POINTER(id),
1615                         simp);
1616
1617     return TRUE;
1618 }
1619
1620 gboolean
1621 device_simple_property_set_fn(
1622         Device *self,
1623         DevicePropertyBase *base,
1624         GValue *val,
1625         PropertySurety surety,
1626         PropertySource source)
1627 {
1628     return device_set_simple_property(self, base->ID, val, surety, source);
1629 }
1630
1631 gboolean
1632 device_get_simple_property(
1633         Device *self,
1634         DevicePropertyId id,
1635         GValue *val,
1636         PropertySurety *surety,
1637         PropertySource *source)
1638 {
1639     SimpleProperty *simp =
1640         g_hash_table_lookup(selfp->simple_properties,
1641                             GINT_TO_POINTER(id));
1642
1643     if (!simp)
1644         return FALSE;
1645
1646     if (val)
1647         g_value_unset_copy(&(simp->response), val);
1648
1649     if (surety)
1650         *surety = simp->surety;
1651
1652     if (source)
1653         *source = simp->source;
1654
1655     return TRUE;
1656 }
1657
1658 gboolean
1659 device_simple_property_get_fn(
1660         Device *self,
1661         DevicePropertyBase *base,
1662         GValue *val,
1663         PropertySurety *surety,
1664         PropertySource *source)
1665 {
1666     return device_get_simple_property(self, base->ID, val, surety, source);
1667 }