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