Imported Upstream version 3.3.3
[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
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17  *
18  * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
19  * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
20  */
21
22 /* The Device API abstracts device workings, interaction, properties, and
23  * capabilities from the rest of the Amanda code base. It supports
24  * pluggable modules for different kinds of devices. */
25
26 #include "amanda.h"
27 #include "conffile.h"
28
29 #include <regex.h>
30
31 #include "device.h"
32 #include "property.h"
33
34 #include "timestamp.h"
35 #include "util.h"
36
37 /*
38  * Prototypes for subclass registration functions
39  */
40
41 void    null_device_register    (void);
42 void    rait_device_register    (void);
43 #ifdef WANT_S3_DEVICE
44 void    s3_device_register    (void);
45 #endif
46 #ifdef WANT_TAPE_DEVICE
47 void    tape_device_register    (void);
48 #endif
49 void    vfs_device_register     (void);
50 #ifdef WANT_DVDRW_DEVICE
51 void    dvdrw_device_register    (void);
52 #endif
53 #ifdef WANT_NDMP_DEVICE
54 void    ndmp_device_register    (void);
55 #endif
56
57 /*
58  * Registration infrastructure
59  */
60
61 static GHashTable* driverList = NULL;
62
63 void device_api_init(void) {
64     glib_init();
65     device_property_init();
66     driverList = g_hash_table_new(g_str_hash, g_str_equal);
67
68     /* register other types and devices. */
69     null_device_register();
70     vfs_device_register();
71 #ifdef WANT_TAPE_DEVICE
72     tape_device_register();
73 #endif
74     rait_device_register();
75 #ifdef WANT_S3_DEVICE
76     s3_device_register();
77 #endif
78 #ifdef WANT_DVDRW_DEVICE
79     dvdrw_device_register();
80 #endif
81 #ifdef WANT_NDMP_DEVICE
82     ndmp_device_register();
83 #endif
84 }
85
86 void
87 register_device(
88     DeviceFactory factory,
89     const char ** device_prefix_list)
90 {
91     char ** tmp;
92
93     g_assert(driverList != NULL);
94     g_assert(factory != NULL);
95     g_return_if_fail(device_prefix_list != NULL);
96     g_return_if_fail(*device_prefix_list != NULL);
97
98     tmp = (char**)device_prefix_list;
99     while (*tmp != NULL) {
100         g_hash_table_insert(driverList, *tmp, (gpointer)factory);
101         tmp ++;
102     }
103 }
104
105 static DeviceFactory lookup_device_factory(const char *device_type) {
106     gpointer key, value;
107     g_assert(driverList != NULL);
108
109     if (g_hash_table_lookup_extended(driverList, device_type, &key, &value)) {
110         return (DeviceFactory)value;
111     } else {
112         return NULL;
113     }
114 }
115
116 static const GFlagsValue device_status_flags_values[] = {
117     { DEVICE_STATUS_SUCCESS,
118       "DEVICE_STATUS_SUCCESS",
119       "Success" },
120     { DEVICE_STATUS_DEVICE_ERROR,
121       "DEVICE_STATUS_DEVICE_ERROR",
122       "Device error" },
123     { DEVICE_STATUS_DEVICE_BUSY,
124       "DEVICE_STATUS_DEVICE_BUSY",
125       "Device busy" },
126     { DEVICE_STATUS_VOLUME_MISSING,
127       "DEVICE_STATUS_VOLUME_MISSING",
128       "Volume not found" },
129     { DEVICE_STATUS_VOLUME_UNLABELED,
130       "DEVICE_STATUS_VOLUME_UNLABELED",
131       "Volume not labeled" },
132     { DEVICE_STATUS_VOLUME_ERROR,
133       "DEVICE_STATUS_VOLUME_ERROR",
134       "Volume error" },
135     { 0, NULL, NULL }
136 };
137
138 GType device_status_flags_get_type(void) {
139     static GType type = 0;
140     if (G_UNLIKELY(type == 0)) {
141         type = g_flags_register_static("DeviceStatusFlags",
142                                        device_status_flags_values);
143     }
144     return type;
145 }
146
147 /* Device class definition starts here. */
148
149 struct DevicePrivate_s {
150     /* hash table mapping ID to SimpleProperty object */
151     GHashTable * simple_properties;
152
153     /* In writing mode, after a short block is written, no additional blocks
154      * are allowed the file is finished and a new file started. This is only
155      * used for assertions. */
156     gboolean wrote_short_block;
157
158     /* Holds an error message if the function returned an error. */
159     char * errmsg;
160
161     /* temporary holding place for device_status_error() */
162     char * statusmsg;
163     DeviceStatusFlags last_status;
164 };
165
166 /* This holds the default response to a particular property. */
167 typedef struct {
168     DeviceProperty *prop;
169     GValue response;
170     PropertySurety surety;
171     PropertySource source;
172 } SimpleProperty;
173
174 #define selfp (self->private)
175
176 /* here are local prototypes, so we can make function pointers. */
177 static void device_init (Device * o);
178 static void device_class_init (DeviceClass * c);
179 static void device_base_init (DeviceClass * c);
180
181 static void simple_property_free(SimpleProperty *o);
182
183 static void default_device_open_device(Device * self, char * device_name,
184                                     char * device_type, char * device_node);
185 static gboolean default_device_configure(Device *self, gboolean use_global_config);
186 static gboolean default_device_property_get_ex(Device * self, DevicePropertyId id,
187                                                GValue * val,
188                                                PropertySurety *surety,
189                                                PropertySource *source);
190 static gboolean default_device_property_set_ex(Device *self,
191                                                DevicePropertyId id,
192                                                GValue * val,
193                                                PropertySurety surety,
194                                                PropertySource source);
195 static void set_properties_from_global_config(Device * device);
196 static void set_properties_from_device_config(Device * device, device_config_t *dc);
197
198 static gboolean property_get_block_size_fn(Device *self,
199     DevicePropertyBase *base, GValue *val,
200     PropertySurety *surety, PropertySource *source);
201
202 static gboolean property_set_block_size_fn(Device *self,
203     DevicePropertyBase *base, GValue *val,
204     PropertySurety surety, PropertySource source);
205
206 static gboolean property_get_min_block_size_fn(Device *self,
207     DevicePropertyBase *base, GValue *val,
208     PropertySurety *surety, PropertySource *source);
209
210 static gboolean property_get_max_block_size_fn(Device *self,
211     DevicePropertyBase *base, GValue *val,
212     PropertySurety *surety, PropertySource *source);
213
214 static gboolean property_get_canonical_name_fn(Device *self,
215     DevicePropertyBase *base, GValue *val,
216     PropertySurety *surety, PropertySource *source);
217
218 /* pointer to the class of our parent */
219 static GObjectClass *parent_class = NULL;
220
221 GType
222 device_get_type (void)
223 {
224     static GType type = 0;
225
226     if G_UNLIKELY(type == 0) {
227         static const GTypeInfo info = {
228             sizeof (DeviceClass),
229             (GBaseInitFunc) device_base_init,
230             (GBaseFinalizeFunc) NULL,
231             (GClassInitFunc) device_class_init,
232             (GClassFinalizeFunc) NULL,
233             NULL /* class_data */,
234             sizeof (Device),
235             0 /* n_preallocs */,
236             (GInstanceInitFunc) device_init,
237             NULL
238         };
239
240         type = g_type_register_static (G_TYPE_OBJECT, "Device", &info,
241                                        (GTypeFlags)G_TYPE_FLAG_ABSTRACT);
242     }
243
244     return type;
245 }
246
247 static void device_finalize(GObject *obj_self) {
248     Device *self G_GNUC_UNUSED = DEVICE (obj_self);
249     if(G_OBJECT_CLASS(parent_class)->finalize)
250         (* G_OBJECT_CLASS(parent_class)->finalize)(obj_self);
251
252     /* Here we call device_finish() if it hasn't been done
253        yet. Subclasses may need to do this same check earlier. */
254     if (self->access_mode != ACCESS_NULL) {
255         device_finish(self);
256     }
257
258     amfree(self->device_name);
259     amfree(self->volume_label);
260     amfree(self->volume_time);
261     amfree(self->volume_header);
262     amfree(selfp->errmsg);
263     amfree(selfp->statusmsg);
264     g_hash_table_destroy(selfp->simple_properties);
265     amfree(self->private);
266 }
267
268 static void
269 device_init (Device * self)
270 {
271     self->private = malloc(sizeof(DevicePrivate));
272     self->device_name = NULL;
273     self->access_mode = ACCESS_NULL;
274     self->is_eof = FALSE;
275     self->is_eom = FALSE;
276     self->file = -1;
277     self->block = 0;
278     self->in_file = FALSE;
279     self->volume_label = NULL;
280     self->volume_time = NULL;
281     self->status = DEVICE_STATUS_SUCCESS;
282     self->min_block_size = 1;
283     self->max_block_size = SIZE_MAX; /* subclasses *really* should choose something smaller */
284     self->block_size = DISK_BLOCK_BYTES;
285     selfp->errmsg = NULL;
286     selfp->statusmsg = NULL;
287     selfp->last_status = 0;
288     selfp->simple_properties =
289         g_hash_table_new_full(g_direct_hash,
290                               g_direct_equal,
291                               NULL,
292                               (GDestroyNotify) simple_property_free);
293 }
294
295 static void
296 device_class_init (DeviceClass * device_class)
297 {
298     GObjectClass *g_object_class = (GObjectClass*) device_class;
299
300     parent_class = g_type_class_ref (G_TYPE_OBJECT);
301
302     device_class->directtcp_supported = FALSE;
303
304     device_class->open_device = default_device_open_device;
305     device_class->configure = default_device_configure;
306     device_class->property_get_ex = default_device_property_get_ex;
307     device_class->property_set_ex = default_device_property_set_ex;
308     g_object_class->finalize = device_finalize;
309 }
310
311 static void
312 device_base_init (DeviceClass * device_class)
313 {
314     /* The base_init function is called once each time a child class is
315      * created, before the class_init functions (even our own) are called.  */
316
317     device_class->class_properties = g_array_new(FALSE, TRUE, sizeof(DeviceProperty));
318     device_class->class_properties_list = NULL;
319
320     device_class_register_property(device_class, PROPERTY_BLOCK_SIZE,
321             PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
322             property_get_block_size_fn,
323             property_set_block_size_fn);
324
325     device_class_register_property(device_class, PROPERTY_MIN_BLOCK_SIZE,
326             PROPERTY_ACCESS_GET_MASK,
327             property_get_min_block_size_fn,
328             NULL);
329
330     device_class_register_property(device_class, PROPERTY_MAX_BLOCK_SIZE,
331             PROPERTY_ACCESS_GET_MASK,
332             property_get_max_block_size_fn,
333             NULL);
334
335     device_class_register_property(device_class, PROPERTY_CANONICAL_NAME,
336             PROPERTY_ACCESS_GET_MASK,
337             property_get_canonical_name_fn,
338             NULL);
339
340     device_class_register_property(device_class, PROPERTY_CONCURRENCY,
341             PROPERTY_ACCESS_GET_MASK,
342             device_simple_property_get_fn,
343             device_simple_property_set_fn);
344
345     device_class_register_property(device_class, PROPERTY_STREAMING,
346             PROPERTY_ACCESS_GET_MASK,
347             device_simple_property_get_fn,
348             device_simple_property_set_fn);
349
350     device_class_register_property(device_class, PROPERTY_APPENDABLE,
351             PROPERTY_ACCESS_GET_MASK,
352             device_simple_property_get_fn,
353             device_simple_property_set_fn);
354
355     device_class_register_property(device_class, PROPERTY_PARTIAL_DELETION,
356             PROPERTY_ACCESS_GET_MASK,
357             device_simple_property_get_fn,
358             device_simple_property_set_fn);
359
360     device_class_register_property(device_class, PROPERTY_FULL_DELETION,
361             PROPERTY_ACCESS_GET_MASK,
362             device_simple_property_get_fn,
363             device_simple_property_set_fn);
364
365     device_class_register_property(device_class, PROPERTY_MEDIUM_ACCESS_TYPE,
366             PROPERTY_ACCESS_GET_MASK,
367             device_simple_property_get_fn,
368             device_simple_property_set_fn);
369
370     device_class_register_property(device_class, PROPERTY_COMMENT,
371             PROPERTY_ACCESS_GET_MASK|PROPERTY_ACCESS_SET_MASK,
372             device_simple_property_get_fn,
373             device_simple_property_set_fn);
374
375     device_class_register_property(device_class, PROPERTY_LEOM,
376             PROPERTY_ACCESS_GET_MASK,
377             device_simple_property_get_fn,
378             device_simple_property_set_fn);
379 }
380
381 static void simple_property_free(SimpleProperty * resp) {
382     g_value_unset(&(resp->response));
383     amfree(resp);
384 }
385
386 static char *
387 regex_message(int result, regex_t *regex) {
388     char * rval;
389     size_t size;
390
391     size = regerror(result, regex, NULL, 0);
392     rval = malloc(size);
393     regerror(result, regex, rval, size);
394
395     return rval;
396 }
397
398 static gboolean
399 handle_device_regex(const char * user_name, char ** driver_name,
400                     char ** device, char **errmsg) {
401     regex_t regex;
402     int reg_result;
403     regmatch_t pmatch[3];
404     static const char * regex_string = "^([a-z0-9]+):(.*)$";
405
406     bzero(&regex, sizeof(regex));
407
408     reg_result = regcomp(&regex, regex_string, REG_EXTENDED | REG_ICASE);
409     if (reg_result != 0) {
410         char * message = regex_message(reg_result, &regex);
411         *errmsg = newvstrallocf(*errmsg, "Error compiling regular expression \"%s\": %s\n",
412                               regex_string, message);
413         amfree(message);
414         return FALSE;
415     }
416
417     reg_result = regexec(&regex, user_name, 3, pmatch, 0);
418     if (reg_result != 0 && reg_result != REG_NOMATCH) {
419         char * message = regex_message(reg_result, &regex);
420         *errmsg = newvstrallocf(*errmsg,
421                         "Error applying regular expression \"%s\" to string \"%s\": %s\n",
422                         user_name, regex_string, message);
423         amfree(message);
424         regfree(&regex);
425         return FALSE;
426     } else if (reg_result == REG_NOMATCH) {
427 #ifdef WANT_TAPE_DEVICE
428         g_warning(
429                 "\"%s\" uses deprecated device naming convention; \n"
430                 "using \"tape:%s\" instead.\n",
431                 user_name, user_name);
432         *driver_name = stralloc("tape");
433         *device = stralloc(user_name);
434 #else /* !WANT_TAPE_DEVICE */
435         *errmsg = newvstrallocf(*errmsg, "\"%s\" is not a valid device name.\n", user_name);
436         regfree(&regex);
437         return FALSE;
438 #endif /* WANT_TAPE_DEVICE */
439     } else {
440         *driver_name = find_regex_substring(user_name, pmatch[1]);
441         *device = find_regex_substring(user_name, pmatch[2]);
442     }
443     regfree(&regex);
444     return TRUE;
445 }
446
447 /* helper function for device_open */
448 static Device *
449 make_null_error(char *errmsg, DeviceStatusFlags status)
450 {
451     DeviceFactory factory;
452     Device *device;
453
454     factory = lookup_device_factory("null");
455     g_assert(factory != NULL);
456
457     device = factory("null:", "null", "");
458     device_set_error(device, errmsg, status);
459
460     return device;
461 }
462
463 char *
464 device_unaliased_name(
465     char *device_name)
466 {
467     device_config_t *dc;
468     char *unaliased_name;
469
470     /* look up the unaliased device name in the configuration */
471     if ((dc = lookup_device_config(device_name))) {
472         if (!(unaliased_name = device_config_get_tapedev(dc))
473             || unaliased_name[0] == '\0') {
474             return NULL;
475         }
476     } else {
477         unaliased_name = device_name;
478     }
479
480     return unaliased_name;
481 }
482
483 Device*
484 device_open (char * device_name)
485 {
486     char *device_type = NULL;
487     char *device_node = NULL;
488     char *errmsg = NULL;
489     char *unaliased_name = NULL;
490     DeviceFactory factory;
491     Device *device;
492
493     g_assert(device_name != NULL);
494
495     if (driverList == NULL) {
496         g_critical("device_open() called without device_api_init()!");
497         g_assert_not_reached();
498     }
499
500     if (device_name == NULL)
501         return make_null_error(stralloc(_("No device name specified")), DEVICE_STATUS_DEVICE_ERROR);
502
503     unaliased_name = device_unaliased_name(device_name);
504     if (!unaliased_name) {
505         return make_null_error(
506                 vstrallocf(_("Device '%s' has no tapedev"), device_name),
507                 DEVICE_STATUS_DEVICE_ERROR);
508     }
509
510     if (!handle_device_regex(unaliased_name, &device_type, &device_node,
511                              &errmsg)) {
512         amfree(device_type);
513         amfree(device_node);
514         return make_null_error(errmsg, DEVICE_STATUS_DEVICE_ERROR);
515     }
516
517     factory = lookup_device_factory(device_type);
518
519     if (factory == NULL) {
520         Device *nulldev = make_null_error(vstrallocf(_("Device type %s is not known."),
521             device_type), DEVICE_STATUS_DEVICE_ERROR);
522         amfree(device_type);
523         amfree(device_node);
524         return nulldev;
525     }
526
527     device = factory(device_name, device_type, device_node);
528     g_assert(device != NULL); /* factories must always return a device */
529
530     device->device_mutex = g_mutex_new();
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     g_strlcpy(rval->datestamp, self->volume_time, sizeof(rval->datestamp));
654     g_strlcpy(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     g_strlcpy(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         device_set_error(self,
888             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),
889             DEVICE_STATUS_DEVICE_ERROR);
890         return FALSE;
891     }
892
893     self->block_size = block_size;
894     self->block_size_surety = surety;
895     self->block_size_source = source;
896
897     return TRUE;
898 }
899
900 static gboolean
901 property_get_min_block_size_fn(
902         Device *self,
903         DevicePropertyBase *base G_GNUC_UNUSED,
904         GValue *val,
905         PropertySurety *surety,
906         PropertySource *source)
907 {
908     g_value_unset_init(val, G_TYPE_UINT);
909     g_assert(self->block_size < G_MAXUINT); /* gsize -> guint */
910     g_value_set_uint(val, (guint)self->min_block_size);
911
912     if (surety)
913         *surety = PROPERTY_SURETY_GOOD;
914
915     if (source)
916         *source = PROPERTY_SOURCE_DEFAULT;
917
918     return TRUE;
919 }
920
921 static gboolean
922 property_get_max_block_size_fn(
923         Device *self,
924         DevicePropertyBase *base G_GNUC_UNUSED,
925         GValue *val,
926         PropertySurety *surety,
927         PropertySource *source)
928 {
929     g_value_unset_init(val, G_TYPE_UINT);
930     g_assert(self->block_size < G_MAXUINT); /* gsize -> guint */
931     g_value_set_uint(val, (guint)self->max_block_size);
932
933     if (surety)
934         *surety = PROPERTY_SURETY_GOOD;
935
936     if (source)
937         *source = PROPERTY_SOURCE_DEFAULT;
938
939     return TRUE;
940 }
941
942 static gboolean
943 property_get_canonical_name_fn(
944         Device *self,
945         DevicePropertyBase *base G_GNUC_UNUSED,
946         GValue *val,
947         PropertySurety *surety,
948         PropertySource *source)
949 {
950     g_value_unset_init(val, G_TYPE_STRING);
951     g_value_set_string(val, self->device_name);
952
953     if (surety)
954         *surety = PROPERTY_SURETY_GOOD;
955
956     if (source)
957         *source = PROPERTY_SOURCE_DEFAULT;
958
959     return TRUE;
960 }
961
962 /* util function */
963 static PropertyPhaseFlags
964 state_to_phase(
965     Device *self)
966 {
967     if (self->access_mode == ACCESS_NULL) {
968         return PROPERTY_PHASE_BEFORE_START;
969     } else if (IS_WRITABLE_ACCESS_MODE(self->access_mode)) {
970         if (self->in_file) {
971             return PROPERTY_PHASE_INSIDE_FILE_WRITE;
972         } else {
973             return PROPERTY_PHASE_BETWEEN_FILE_WRITE;
974         }
975     } else { /* read mode */
976         if (self->in_file) {
977             return PROPERTY_PHASE_INSIDE_FILE_READ;
978         } else {
979             return PROPERTY_PHASE_BETWEEN_FILE_READ;
980         }
981     }
982 }
983
984 /* This default implementation serves up static responses, and
985    implements a few default responses based on values from the Device
986    struct. */
987 static gboolean
988 default_device_property_get_ex(
989         Device * self,
990         DevicePropertyId id,
991         GValue * val,
992         PropertySurety *surety,
993         PropertySource *source)
994 {
995     DeviceProperty *prop;
996     GArray *class_properties;
997     PropertyPhaseFlags cur_phase;
998
999     /* Most of this function's job is to sanity-check everything, then
1000      * call the relevant getter. */
1001
1002     class_properties = DEVICE_GET_CLASS(self)->class_properties;
1003     if (id >= class_properties->len)
1004         return FALSE;
1005
1006     prop = &g_array_index(class_properties, DeviceProperty, id);
1007     if (prop->base == NULL)
1008         return FALSE;
1009
1010     if (val || surety || source) {
1011         /* check the phase */
1012         cur_phase = state_to_phase(self);
1013         if (!(prop->access & cur_phase))
1014             return FALSE;
1015
1016         if (prop->getter == NULL)
1017             return FALSE;
1018
1019         if (!prop->getter(self, prop->base, val, surety, source))
1020             return FALSE;
1021     }
1022
1023     return TRUE;
1024 }
1025
1026 static gboolean
1027 default_device_property_set_ex(
1028     Device *self,
1029     DevicePropertyId id,
1030     GValue * val,
1031     PropertySurety surety,
1032     PropertySource source)
1033 {
1034     DeviceProperty *prop;
1035     GArray *class_properties;
1036     PropertyPhaseFlags cur_phase;
1037
1038     /* Most of this function's job is to sanity-check everything, then
1039      * call the relevant setter. */
1040
1041     if (device_in_error(self))
1042         return FALSE;
1043
1044     class_properties = DEVICE_GET_CLASS(self)->class_properties;
1045     if (id >= class_properties->len)
1046         return FALSE;
1047
1048     prop = &g_array_index(class_properties, DeviceProperty, id);
1049     if (prop->base == NULL)
1050         return FALSE;
1051
1052     /* check that the type matches */
1053     if (!G_VALUE_HOLDS(val, prop->base->type))
1054         return FALSE;
1055
1056     /* check the phase */
1057     cur_phase = state_to_phase(self) << PROPERTY_PHASE_SHIFT;
1058     if (!(prop->access & cur_phase))
1059         return FALSE;
1060
1061     if (prop->setter == NULL)
1062         return FALSE;
1063
1064     if (!prop->setter(self, prop->base, val, surety, source))
1065         return FALSE;
1066
1067     return TRUE;
1068 }
1069
1070 const GSList *
1071 device_property_get_list (Device * self)
1072 {
1073     g_assert(IS_DEVICE(self));
1074
1075     return DEVICE_GET_CLASS(self)->class_properties_list;
1076 }
1077
1078 /* XXX WARNING XXX
1079  * All the functions below this comment are stub functions that do nothing
1080  * but implement the virtual function table. Call these functions and they
1081  * will do what you expect vis-a-vis virtual functions. But don't put code
1082  * in them beyond error checking and VFT lookup. */
1083
1084 void
1085 device_open_device (Device * self, char * device_name,
1086         char * device_type, char * device_node)
1087 {
1088     DeviceClass *klass;
1089
1090     g_assert(IS_DEVICE(self));
1091     g_assert(device_name != NULL);
1092
1093     klass = DEVICE_GET_CLASS(self);
1094     g_assert(klass->open_device);
1095     (klass->open_device)(self, device_name, device_type, device_node);
1096 }
1097
1098 DeviceStatusFlags device_read_label(Device * self) {
1099     DeviceClass * klass;
1100
1101     g_assert(self != NULL);
1102     g_assert(IS_DEVICE(self));
1103     g_assert(self->access_mode == ACCESS_NULL);
1104
1105     klass = DEVICE_GET_CLASS(self);
1106     g_assert(klass->read_label);
1107     return (klass->read_label)(self);
1108 }
1109
1110 gboolean
1111 device_finish (Device * self) {
1112     DeviceClass *klass;
1113
1114     g_assert(IS_DEVICE (self));
1115
1116     klass = DEVICE_GET_CLASS(self);
1117     g_assert(klass->finish);
1118     return (klass->finish)(self);
1119 }
1120
1121 guint64
1122 device_get_bytes_read (Device * self) {
1123     DeviceClass *klass;
1124     guint64 bytes = 0;
1125
1126     g_assert(IS_DEVICE (self));
1127
1128     g_mutex_lock(self->device_mutex);
1129     if (self->in_file) {
1130         klass = DEVICE_GET_CLASS(self);
1131         if (klass->get_bytes_read) {
1132             bytes = (klass->get_bytes_read)(self);
1133         } else {
1134             bytes = self->bytes_read;
1135         }
1136     }
1137     g_mutex_unlock(self->device_mutex);
1138     return bytes;
1139 }
1140
1141 guint64
1142 device_get_bytes_written (Device * self) {
1143     DeviceClass *klass;
1144     guint64 bytes = 0;
1145
1146     g_assert(IS_DEVICE (self));
1147
1148     g_mutex_lock(self->device_mutex);
1149     if (self->in_file) {
1150         klass = DEVICE_GET_CLASS(self);
1151         if (klass->get_bytes_written) {
1152             bytes = (klass->get_bytes_written)(self);
1153         } else {
1154             bytes = self->bytes_written;
1155         }
1156     }
1157     g_mutex_unlock(self->device_mutex);
1158     return bytes;
1159 }
1160
1161 gboolean
1162 device_configure (Device * self, gboolean use_global_config)
1163 {
1164     DeviceClass *klass;
1165
1166     g_assert(IS_DEVICE (self));
1167     g_assert(self->access_mode == ACCESS_NULL);
1168
1169     klass = DEVICE_GET_CLASS(self);
1170     if(klass->configure) {
1171         return (klass->configure)(self, use_global_config);
1172     } else {
1173         device_set_error(self,
1174             stralloc(_("Unimplemented method")),
1175             DEVICE_STATUS_DEVICE_ERROR);
1176         return FALSE;
1177     }
1178 }
1179
1180 gboolean
1181 device_start (Device * self, DeviceAccessMode mode,
1182               char * label, char * timestamp)
1183 {
1184     DeviceClass *klass;
1185     char * local_timestamp = NULL;
1186     gboolean rv;
1187
1188     g_assert(IS_DEVICE (self));
1189     g_assert(mode != ACCESS_NULL);
1190     g_assert(mode != ACCESS_WRITE || label != NULL);
1191
1192     klass = DEVICE_GET_CLASS(self);
1193     g_assert(klass->start);
1194
1195     /* For a good combination of synchronization and public simplicity,
1196        this stub function does not require a timestamp, but the actual
1197        implementation function does. We generate the timestamp here with
1198        time(). */
1199     if (mode == ACCESS_WRITE &&
1200         get_timestamp_state(timestamp) == TIME_STATE_REPLACE) {
1201         local_timestamp = timestamp =
1202             get_proper_stamp_from_time(time(NULL));
1203     }
1204
1205     rv = (klass->start)(self, mode, label, timestamp);
1206     amfree(local_timestamp);
1207     return rv;
1208 }
1209
1210 gboolean
1211 device_write_block (Device * self, guint size, gpointer block)
1212 {
1213     DeviceClass *klass;
1214
1215     g_assert(IS_DEVICE (self));
1216     g_assert(size > 0);
1217
1218     /* these are all things that the caller should take care to
1219      * guarantee, so we just assert them here */
1220     g_assert(size <= self->block_size);
1221     g_assert(self->in_file);
1222     g_assert(!selfp->wrote_short_block);
1223     g_assert(block != NULL);
1224     g_assert(IS_WRITABLE_ACCESS_MODE(self->access_mode));
1225
1226     if (size < self->block_size)
1227         selfp->wrote_short_block = TRUE;
1228
1229     klass = DEVICE_GET_CLASS(self);
1230     g_assert(klass->write_block);
1231     return (*klass->write_block)(self,size, block);
1232 }
1233
1234 gboolean
1235 device_start_file (Device * self, dumpfile_t * jobInfo) {
1236     DeviceClass * klass;
1237
1238     g_assert(IS_DEVICE (self));
1239     g_assert(!(self->in_file));
1240     g_assert(jobInfo != NULL);
1241
1242     selfp->wrote_short_block = FALSE;
1243
1244     klass = DEVICE_GET_CLASS(self);
1245     g_assert(klass->start_file);
1246     return (klass->start_file)(self, jobInfo );
1247 }
1248
1249 gboolean
1250 device_finish_file (Device * self)
1251 {
1252     DeviceClass *klass;
1253
1254     g_assert(IS_DEVICE (self));
1255     g_assert(IS_WRITABLE_ACCESS_MODE(self->access_mode));
1256     g_assert(self->in_file);
1257
1258     klass = DEVICE_GET_CLASS(self);
1259     g_assert(klass->finish_file);
1260     return (klass->finish_file)(self);
1261 }
1262
1263 dumpfile_t*
1264 device_seek_file (Device * self, guint file)
1265 {
1266     DeviceClass *klass;
1267
1268     g_assert(IS_DEVICE (self));
1269     g_assert(self->access_mode == ACCESS_READ);
1270
1271     klass = DEVICE_GET_CLASS(self);
1272     g_assert(klass->seek_file);
1273     return (klass->seek_file)(self,file);
1274 }
1275
1276 gboolean
1277 device_seek_block (Device * self, guint64 block)
1278 {
1279     DeviceClass *klass;
1280
1281     g_assert(IS_DEVICE (self));
1282     g_assert(self->access_mode == ACCESS_READ);
1283     g_assert(self->in_file);
1284
1285     klass = DEVICE_GET_CLASS(self);
1286     g_assert(klass->seek_block);
1287     return (klass->seek_block)(self,block);
1288 }
1289
1290 int
1291 device_read_block (Device * self, gpointer buffer, int * size)
1292 {
1293     DeviceClass *klass;
1294
1295     g_assert(IS_DEVICE (self));
1296     g_assert(size != NULL);
1297     g_assert(self->access_mode == ACCESS_READ);
1298
1299     if (*size != 0) {
1300         g_assert(buffer != NULL);
1301     }
1302
1303     klass = DEVICE_GET_CLASS(self);
1304     g_assert(klass->read_block);
1305     return (klass->read_block)(self,buffer,size);
1306 }
1307
1308 gboolean
1309 device_property_get_ex(
1310         Device * self,
1311         DevicePropertyId id,
1312         GValue * val,
1313         PropertySurety *surety,
1314         PropertySource *source)
1315 {
1316     DeviceClass *klass;
1317
1318     g_assert(IS_DEVICE (self));
1319     g_assert(device_property_get_by_id(id) != NULL);
1320
1321     klass = DEVICE_GET_CLASS(self);
1322
1323     g_assert(klass->property_get_ex);
1324     return (klass->property_get_ex)(self, id, val, surety, source);
1325 }
1326
1327 gboolean
1328 device_property_set_ex(
1329         Device * self,
1330         DevicePropertyId id,
1331         GValue * val,
1332         PropertySurety surety,
1333         PropertySource source)
1334 {
1335     DeviceClass *klass;
1336
1337     g_assert(IS_DEVICE (self));
1338
1339     klass = DEVICE_GET_CLASS(self);
1340
1341     g_assert(klass->property_set_ex);
1342     return (klass->property_set_ex)(self, id, val, surety, source);
1343 }
1344
1345 gboolean
1346 device_recycle_file (Device * self, guint filenum)
1347 {
1348     DeviceClass *klass;
1349
1350     g_assert(self != NULL);
1351     g_assert(IS_DEVICE (self));
1352     g_assert(self->access_mode == ACCESS_APPEND);
1353     g_assert(!self->in_file);
1354
1355     klass = DEVICE_GET_CLASS(self);
1356
1357     g_assert(klass->recycle_file);
1358     return (klass->recycle_file)(self,filenum);
1359 }
1360
1361 gboolean
1362 device_erase (Device * self)
1363 {
1364     DeviceClass *klass;
1365
1366     g_assert(IS_DEVICE (self));
1367     g_assert(self->access_mode == ACCESS_NULL);
1368     g_assert(!self->in_file);
1369
1370     klass = DEVICE_GET_CLASS(self);
1371     if(klass->erase) {
1372         return (klass->erase)(self);
1373     } else {
1374         device_set_error(self,
1375             stralloc(_("Unimplemented method")),
1376             DEVICE_STATUS_DEVICE_ERROR);
1377         return FALSE;
1378     }
1379 }
1380
1381 gboolean
1382 device_eject (Device * self)
1383 {
1384     DeviceClass *klass;
1385
1386     g_assert(IS_DEVICE (self));
1387     g_assert(self->access_mode == ACCESS_NULL);
1388     g_assert(!self->in_file);
1389
1390     klass = DEVICE_GET_CLASS(self);
1391     if (klass->eject) {
1392         return (klass->eject)(self);
1393     } else {
1394         return TRUE;
1395     }
1396 }
1397
1398 gboolean
1399 device_listen(
1400     Device *self,
1401     gboolean for_writing,
1402     DirectTCPAddr **addrs)
1403 {
1404     DeviceClass *klass;
1405
1406     klass = DEVICE_GET_CLASS(self);
1407     if(klass->listen) {
1408         return (klass->listen)(self, for_writing, addrs);
1409     } else {
1410         device_set_error(self,
1411             stralloc(_("Unimplemented method")),
1412             DEVICE_STATUS_DEVICE_ERROR);
1413         return FALSE;
1414     }
1415 }
1416
1417 int
1418 device_accept(
1419     Device *self,
1420     DirectTCPConnection **conn,
1421     int    *cancelled,
1422     GMutex *abort_mutex,
1423     GCond  *abort_cond)
1424 {
1425     DeviceClass *klass;
1426
1427     klass = DEVICE_GET_CLASS(self);
1428     if(klass->accept) {
1429         return (klass->accept)(self, conn, cancelled, abort_mutex, abort_cond);
1430     } else {
1431         device_set_error(self,
1432             g_strdup(_("Unimplemented method")),
1433             DEVICE_STATUS_DEVICE_ERROR);
1434         return 1;
1435     }
1436 }
1437
1438
1439 int
1440 device_connect(
1441     Device *self,
1442     gboolean for_writing,
1443     DirectTCPAddr *addrs,
1444     DirectTCPConnection **conn,
1445     int    *cancelled,
1446     GMutex *abort_mutex,
1447     GCond  *abort_cond)
1448 {
1449     DeviceClass *klass;
1450
1451     klass = DEVICE_GET_CLASS(self);
1452     if(klass->connect) {
1453         return (klass->connect)(self, for_writing, addrs, conn, cancelled,
1454                                 abort_mutex, abort_cond);
1455     } else {
1456         device_set_error(self,
1457             g_strdup(_("Unimplemented method")),
1458             DEVICE_STATUS_DEVICE_ERROR);
1459         return 1;
1460     }
1461 }
1462
1463 int
1464 device_write_from_connection(
1465     Device *self,
1466     guint64 size,
1467     guint64 *actual_size,
1468     int    *cancelled,
1469     GMutex *abort_mutex,
1470     GCond  *abort_cond)
1471 {
1472     DeviceClass *klass;
1473
1474     klass = DEVICE_GET_CLASS(self);
1475
1476     g_assert(self->in_file);
1477     g_assert(IS_WRITABLE_ACCESS_MODE(self->access_mode));
1478
1479     if(klass->write_from_connection) {
1480         return (klass->write_from_connection)(self, size, actual_size,
1481                                               cancelled,
1482                                               abort_mutex, abort_cond);
1483     } else {
1484         device_set_error(self,
1485             stralloc(_("Unimplemented method")),
1486             DEVICE_STATUS_DEVICE_ERROR);
1487         return 1;
1488     }
1489 }
1490
1491 int
1492 device_read_to_connection(
1493     Device *self,
1494     guint64 size,
1495     guint64 *actual_size,
1496     int    *cancelled,
1497     GMutex *abort_mutex,
1498     GCond  *abort_cond)
1499 {
1500     DeviceClass *klass;
1501
1502     g_assert(self->in_file);
1503     g_assert(self->access_mode == ACCESS_READ);
1504
1505     klass = DEVICE_GET_CLASS(self);
1506     if(klass->read_to_connection) {
1507         return (klass->read_to_connection)(self, size, actual_size,
1508                                            cancelled, abort_mutex, abort_cond);
1509     } else {
1510         device_set_error(self,
1511             stralloc(_("Unimplemented method")),
1512             DEVICE_STATUS_DEVICE_ERROR);
1513         return 1;
1514     }
1515 }
1516
1517 gboolean
1518 device_use_connection(
1519     Device *self,
1520     DirectTCPConnection *conn)
1521 {
1522     DeviceClass *klass;
1523
1524     g_assert(self->access_mode == ACCESS_NULL);
1525
1526     klass = DEVICE_GET_CLASS(self);
1527     if(klass->use_connection) {
1528         return (klass->use_connection)(self, conn);
1529     } else {
1530         device_set_error(self,
1531             stralloc(_("Unimplemented method")),
1532             DEVICE_STATUS_DEVICE_ERROR);
1533         return FALSE;
1534     }
1535 }
1536
1537 /* Property handling */
1538
1539 void
1540 device_class_register_property(
1541         DeviceClass *klass,
1542         DevicePropertyId id,
1543         PropertyAccessFlags access,
1544         PropertyGetFn getter,
1545         PropertySetFn setter)
1546 {
1547     DevicePropertyBase *base;
1548     DeviceProperty *prop;
1549     GSList *proplist;
1550     guint i;
1551
1552     g_assert(klass != NULL);
1553
1554     base = device_property_get_by_id(id);
1555     g_assert(base != NULL);
1556
1557     if (klass->class_properties->len <= id) {
1558         g_array_set_size(klass->class_properties, id+1);
1559     }
1560
1561     prop = &g_array_index(klass->class_properties, DeviceProperty, id);
1562     prop->base = base;
1563     prop->access = access;
1564     prop->getter = getter;
1565     prop->setter = setter;
1566
1567     /* completely rewrite the list of prop pointers, as they may have changed,
1568      * or we may have replaced an existing property*/
1569
1570     if (klass->class_properties_list) {
1571         g_slist_free(klass->class_properties_list);
1572     }
1573
1574     proplist = NULL;
1575     for (i = 0; i < klass->class_properties->len; i++) {
1576         prop = &g_array_index(klass->class_properties, DeviceProperty, i);
1577         if (!prop->base)
1578             continue;
1579         proplist = g_slist_prepend(proplist, prop);
1580     }
1581
1582     klass->class_properties_list = proplist;
1583 }
1584
1585 gboolean
1586 device_set_simple_property(
1587         Device *self,
1588         DevicePropertyId id,
1589         GValue *val,
1590         PropertySurety surety,
1591         PropertySource source)
1592 {
1593     SimpleProperty *simp;
1594     DeviceProperty *prop;
1595
1596     prop = &g_array_index(DEVICE_GET_CLASS(self)->class_properties,
1597                           DeviceProperty, id);
1598
1599     /* these assertions should already be checked, but let's be sure */
1600     g_assert(prop->base != NULL);   /* prop must be registered with device */
1601     g_assert(G_VALUE_HOLDS(val, prop->base->type));
1602
1603     simp = g_new0(SimpleProperty, 1);
1604     simp->prop = prop;
1605     g_value_unset_copy(val, &(simp->response));
1606     simp->surety = surety;
1607     simp->source = source;
1608
1609     g_hash_table_insert(selfp->simple_properties,
1610                         GINT_TO_POINTER(id),
1611                         simp);
1612
1613     return TRUE;
1614 }
1615
1616 gboolean
1617 device_simple_property_set_fn(
1618         Device *self,
1619         DevicePropertyBase *base,
1620         GValue *val,
1621         PropertySurety surety,
1622         PropertySource source)
1623 {
1624     return device_set_simple_property(self, base->ID, val, surety, source);
1625 }
1626
1627 gboolean
1628 device_get_simple_property(
1629         Device *self,
1630         DevicePropertyId id,
1631         GValue *val,
1632         PropertySurety *surety,
1633         PropertySource *source)
1634 {
1635     SimpleProperty *simp =
1636         g_hash_table_lookup(selfp->simple_properties,
1637                             GINT_TO_POINTER(id));
1638
1639     if (!simp)
1640         return FALSE;
1641
1642     if (val)
1643         g_value_unset_copy(&(simp->response), val);
1644
1645     if (surety)
1646         *surety = simp->surety;
1647
1648     if (source)
1649         *source = simp->source;
1650
1651     return TRUE;
1652 }
1653
1654 gboolean
1655 device_simple_property_get_fn(
1656         Device *self,
1657         DevicePropertyBase *base,
1658         GValue *val,
1659         PropertySurety *surety,
1660         PropertySource *source)
1661 {
1662     return device_get_simple_property(self, base->ID, val, surety, source);
1663 }