921b9480c45b42f5c87382bc86af61df78755985
[debian/amanda] / device-src / device.c
1 /*
2  * Copyright (c) 2007, 2008, 2009, 2010 Zmanda, Inc.  All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License version 2 as published
6  * by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11  * for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16  *
17  * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
18  * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
19  */
20
21 /* The Device API abstracts device workings, interaction, properties, and
22  * capabilities from the rest of the Amanda code base. It supports
23  * pluggable modules for different kinds of devices. */
24
25 #include "amanda.h"
26 #include "conffile.h"
27
28 #include <regex.h>
29
30 #include "device.h"
31 #include "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     amfree(device_type);
530     amfree(device_node);
531
532     return device;
533 }
534
535 char *
536 device_error(Device * self)
537 {
538     if (self == NULL) {
539         return device_error_or_status(self);
540     } else if (selfp->errmsg) {
541         return selfp->errmsg;
542     } else {
543         return "Unknown Device error";
544     }
545 }
546
547 char *
548 device_status_error(Device * self)
549 {
550     char **status_strv;
551     char *statusmsg;
552
553     if (self == NULL) {
554         return device_error_or_status(self);
555     }
556
557     /* reuse a previous statusmsg, if it was for the same status */
558     if (selfp->statusmsg && selfp->last_status == self->status)
559         return selfp->statusmsg;
560
561     amfree(selfp->statusmsg);
562
563     status_strv = g_flags_nick_to_strv(self->status, DEVICE_STATUS_FLAGS_TYPE);
564     g_assert(g_strv_length(status_strv) > 0);
565     if (g_strv_length(status_strv) == 1) {
566         statusmsg = stralloc(*status_strv);
567     } else {
568         char * status_list = g_english_strjoinv(status_strv, "or");
569         statusmsg = g_strdup_printf("one of %s", status_list);
570         amfree(status_list);
571     }
572     g_strfreev(status_strv);
573
574     selfp->statusmsg = statusmsg;
575     selfp->last_status = self->status;
576     return statusmsg;
577 }
578
579 char *
580 device_error_or_status(Device * self)
581 {
582     if (self == NULL) {
583         return "Device is NULL";
584     } else if (selfp->errmsg) {
585         return selfp->errmsg;
586     } else {
587         return device_status_error(self);
588     }
589 }
590
591 void
592 device_set_error(Device *self, char *errmsg, DeviceStatusFlags new_flags)
593 {
594     char **flags_strv;
595     char *flags_str;
596     char *device_name;
597
598     if (!self) {
599         g_warning("device_set_error called with a NULL device: '%s'", errmsg? errmsg:"(NULL)");
600         amfree(errmsg);
601         return;
602     }
603
604     device_name = self->device_name? self->device_name : "(unknown device)";
605
606     if (errmsg && (!selfp->errmsg || strcmp(errmsg, selfp->errmsg) != 0))
607         g_debug("Device %s error = '%s'", device_name, errmsg);
608
609     amfree(selfp->errmsg);
610     selfp->errmsg = errmsg;
611
612     if (new_flags != DEVICE_STATUS_SUCCESS) {
613         flags_strv = g_flags_name_to_strv(new_flags, DEVICE_STATUS_FLAGS_TYPE);
614         g_assert(g_strv_length(flags_strv) > 0);
615         flags_str = g_english_strjoinv(flags_strv, "and");
616         g_debug("Device %s setting status flag(s): %s", device_name, flags_str);
617         amfree(flags_str);
618         g_strfreev(flags_strv);
619     }
620
621     self->status = new_flags;
622 }
623
624 char * device_build_amanda_header(Device * self, const dumpfile_t * info,
625                                   size_t *size) {
626     return build_header(info, size, self->block_size);
627 }
628
629 dumpfile_t * make_tapestart_header(Device * self, char * label,
630                                    char * timestamp) {
631     dumpfile_t * rval;
632     GValue val;
633     bzero(&val, sizeof(val));
634
635     g_assert(label != NULL);
636
637     rval = malloc(sizeof(*rval));
638     fh_init(rval);
639     rval->type = F_TAPESTART;
640     if (device_property_get(self, PROPERTY_BLOCK_SIZE, &val)) {
641         rval->blocksize = g_value_get_int(&val);
642         g_value_unset(&val);
643     }
644
645     amfree(self->volume_time);
646     if (get_timestamp_state(timestamp) == TIME_STATE_REPLACE) {
647         self->volume_time = get_proper_stamp_from_time(time(NULL));
648     } else {
649         self->volume_time = g_strdup(timestamp);
650     }
651     strncpy(rval->datestamp, self->volume_time, sizeof(rval->datestamp));
652     strncpy(rval->name, label, sizeof(rval->name));
653
654     return rval;
655 }
656
657 dumpfile_t * make_tapeend_header(void) {
658     dumpfile_t * rval;
659     char * timestamp;
660
661     rval = malloc(sizeof(*rval));
662     rval->type = F_TAPEEND;
663     timestamp = get_timestamp_from_time(time(NULL));
664     strncpy(rval->datestamp, timestamp, sizeof(rval->datestamp));
665     amfree(timestamp);
666     return rval;
667 }
668
669 /* Try setting the blocksize on a device. Check results, fallback, and
670  * set error status for problems. */
671 static gboolean
672 try_set_blocksize(Device * device, guint blocksize) {
673     GValue val;
674     gboolean success;
675     bzero(&val, sizeof(val));
676
677     g_value_init(&val, G_TYPE_INT);
678     g_value_set_int(&val, blocksize);
679     success = device_property_set(device, PROPERTY_BLOCK_SIZE, &val);
680     g_value_unset(&val);
681
682     if (!success) {
683         device_set_error(device,
684             vstrallocf(_("Setting BLOCK_SIZE to %u "
685                     "not supported for device %s.\n"),
686                     blocksize, device->device_name),
687             DEVICE_STATUS_DEVICE_ERROR);
688     }
689
690     return success;
691 }
692
693 /* A GHFunc (callback for g_hash_table_foreach) */
694 static void set_device_property(gpointer key_p, gpointer value_p,
695                                    gpointer user_data_p) {
696     char   * property_s = key_p;
697     property_t * property = value_p;
698     Device * device = user_data_p;
699     const DevicePropertyBase* property_base;
700     GValue property_value;
701     char   * value;
702
703     g_return_if_fail(IS_DEVICE(device));
704     g_return_if_fail(property_s != NULL);
705     g_return_if_fail(property != NULL);
706     g_return_if_fail(property->values != NULL);
707
708     /* don't continue beating on a device that's already erroring */
709     if (device_in_error(device)) return;
710
711     property_base = device_property_get_by_name(property_s);
712     if (property_base == NULL) {
713         /* Nonexistant property name. */
714         device_set_error(device,
715             vstrallocf(_("unknown device property name '%s'"), property_s),
716             DEVICE_STATUS_DEVICE_ERROR);
717         return;
718     }
719     if (g_slist_length(property->values) > 1) {
720         device_set_error(device,
721             vstrallocf(_("multiple values for device property '%s'"), property_s),
722             DEVICE_STATUS_DEVICE_ERROR);
723         return;
724     }
725
726     bzero(&property_value, sizeof(property_value));
727     g_value_init(&property_value, property_base->type);
728     value = property->values->data;
729     if (!g_value_set_from_string(&property_value, value)) {
730         /* Value type could not be interpreted. */
731         device_set_error(device,
732             vstrallocf(_("Could not parse property value '%s' for property '%s' (property type %s)"),
733                         value, property_base->name, g_type_name(property_base->type)),
734             DEVICE_STATUS_DEVICE_ERROR);
735         return;
736     } else {
737         g_assert (G_VALUE_HOLDS(&property_value, property_base->type));
738     }
739
740     if (!device_property_set(device, property_base->ID, &property_value)) {
741         /* Device rejects property. */
742         if (!device_in_error(device)) {
743             device_set_error(device,
744                 vstrallocf(_("Could not set property '%s' to '%s' on %s"),
745                         property_base->name, value, device->device_name),
746                 DEVICE_STATUS_DEVICE_ERROR);
747         }
748         return;
749     }
750 }
751
752 /* Set up properties based on various taper-related configuration parameters
753  * and from the tapetype.
754  */
755 static void
756 set_properties_from_global_config(Device * device) {
757     char * tapetype_name = getconf_str(CNF_TAPETYPE);
758     if (tapetype_name != NULL) {
759         tapetype_t * tapetype = lookup_tapetype(tapetype_name);
760         if (tapetype != NULL) {
761             GValue val;
762             guint64 length;
763             guint blocksize_kb;
764             gboolean success;
765
766             bzero(&val, sizeof(GValue));
767
768             if (tapetype_seen(tapetype, TAPETYPE_LENGTH)) {
769                 length = tapetype_get_length(tapetype);
770                 g_value_init(&val, G_TYPE_UINT64);
771                 g_value_set_uint64(&val, length * 1024);
772                 /* If this fails, it's not really an error. */
773                 device_property_set(device, PROPERTY_MAX_VOLUME_USAGE, &val);
774                 g_value_unset(&val);
775             }
776
777             if (tapetype_seen(tapetype, TAPETYPE_READBLOCKSIZE)) {
778                 blocksize_kb = tapetype_get_readblocksize(tapetype);
779                 g_value_init(&val, G_TYPE_UINT);
780                 g_value_set_uint(&val, blocksize_kb * 1024);
781                 success = device_property_set(device,
782                                               PROPERTY_READ_BLOCK_SIZE,
783                                               &val);
784                 g_value_unset(&val);
785                 if (!success) {
786                     /* a non-fatal error */
787                     g_warning("Setting READ_BLOCK_SIZE to %ju not supported for device %s.",
788                             1024*(uintmax_t)blocksize_kb, device->device_name);
789                 }
790             }
791
792             if (tapetype_seen(tapetype, TAPETYPE_BLOCKSIZE)) {
793                 blocksize_kb = tapetype_get_blocksize(tapetype);
794                 /* TODO: handle errors */
795                 (void)try_set_blocksize(device, blocksize_kb * 1024);
796             }
797         }
798     }
799
800     g_hash_table_foreach(getconf_proplist(CNF_DEVICE_PROPERTY),
801                          set_device_property, device);
802 }
803
804 /* Set properties specified within a device definition */
805 static void
806 set_properties_from_device_config(Device * device, device_config_t *dc) {
807     g_hash_table_foreach(device_config_get_property(dc),
808                          set_device_property, device);
809 }
810
811 static gboolean
812 default_device_configure(Device *self, gboolean use_global_config)
813 {
814     device_config_t *dc;
815
816     if (device_in_error(self))
817         return FALSE;
818
819     if (use_global_config)
820         set_properties_from_global_config(self);
821
822     if (device_in_error(self))
823         return FALSE;
824
825     if ((dc = lookup_device_config(self->device_name)))
826         set_properties_from_device_config(self, dc);
827
828     return !device_in_error(self);
829 }
830
831 void device_clear_volume_details(Device * device) {
832     if (device == NULL || device->access_mode != ACCESS_NULL) {
833         return;
834     }
835
836     amfree(device->volume_label);
837     amfree(device->volume_time);
838 }
839
840 /* Here we put default implementations of virtual functions. Since
841    this class is virtual, many of these functions offer at best
842    incomplete functionality. But they do offer the useful commonality
843    that all devices can expect to need. */
844
845 static void default_device_open_device(Device * self, char * device_name,
846                     char * device_type G_GNUC_UNUSED, char * device_node G_GNUC_UNUSED) {
847     /* Set the device_name property */
848     self->device_name = stralloc(device_name);
849 }
850
851 static gboolean
852 property_get_block_size_fn(
853         Device *self,
854         DevicePropertyBase *base G_GNUC_UNUSED,
855         GValue *val,
856         PropertySurety *surety,
857         PropertySource *source)
858 {
859     g_value_unset_init(val, G_TYPE_INT);
860     g_assert(self->block_size < G_MAXINT); /* gsize -> gint */
861     g_value_set_int(val, (gint)self->block_size);
862
863     if (surety)
864         *surety = self->block_size_surety;
865
866     if (source)
867         *source = self->block_size_source;
868
869     return TRUE;
870 }
871
872 static gboolean
873 property_set_block_size_fn(
874         Device *self,
875         DevicePropertyBase *base G_GNUC_UNUSED,
876         GValue *val,
877         PropertySurety surety,
878         PropertySource source)
879 {
880     gint block_size = g_value_get_int(val);
881
882     g_assert(block_size >= 0); /* int -> gsize (unsigned) */
883     if ((gsize)block_size < self->min_block_size
884        || (gsize)block_size > self->max_block_size)
885         return FALSE;
886
887     self->block_size = block_size;
888     self->block_size_surety = surety;
889     self->block_size_source = source;
890
891     return TRUE;
892 }
893
894 static gboolean
895 property_get_min_block_size_fn(
896         Device *self,
897         DevicePropertyBase *base G_GNUC_UNUSED,
898         GValue *val,
899         PropertySurety *surety,
900         PropertySource *source)
901 {
902     g_value_unset_init(val, G_TYPE_UINT);
903     g_assert(self->block_size < G_MAXUINT); /* gsize -> guint */
904     g_value_set_uint(val, (guint)self->min_block_size);
905
906     if (surety)
907         *surety = PROPERTY_SURETY_GOOD;
908
909     if (source)
910         *source = PROPERTY_SOURCE_DEFAULT;
911
912     return TRUE;
913 }
914
915 static gboolean
916 property_get_max_block_size_fn(
917         Device *self,
918         DevicePropertyBase *base G_GNUC_UNUSED,
919         GValue *val,
920         PropertySurety *surety,
921         PropertySource *source)
922 {
923     g_value_unset_init(val, G_TYPE_UINT);
924     g_assert(self->block_size < G_MAXUINT); /* gsize -> guint */
925     g_value_set_uint(val, (guint)self->max_block_size);
926
927     if (surety)
928         *surety = PROPERTY_SURETY_GOOD;
929
930     if (source)
931         *source = PROPERTY_SOURCE_DEFAULT;
932
933     return TRUE;
934 }
935
936 static gboolean
937 property_get_canonical_name_fn(
938         Device *self,
939         DevicePropertyBase *base G_GNUC_UNUSED,
940         GValue *val,
941         PropertySurety *surety,
942         PropertySource *source)
943 {
944     g_value_unset_init(val, G_TYPE_STRING);
945     g_value_set_string(val, self->device_name);
946
947     if (surety)
948         *surety = PROPERTY_SURETY_GOOD;
949
950     if (source)
951         *source = PROPERTY_SOURCE_DEFAULT;
952
953     return TRUE;
954 }
955
956 /* util function */
957 static PropertyPhaseFlags
958 state_to_phase(
959     Device *self)
960 {
961     if (self->access_mode == ACCESS_NULL) {
962         return PROPERTY_PHASE_BEFORE_START;
963     } else if (IS_WRITABLE_ACCESS_MODE(self->access_mode)) {
964         if (self->in_file) {
965             return PROPERTY_PHASE_INSIDE_FILE_WRITE;
966         } else {
967             return PROPERTY_PHASE_BETWEEN_FILE_WRITE;
968         }
969     } else { /* read mode */
970         if (self->in_file) {
971             return PROPERTY_PHASE_INSIDE_FILE_READ;
972         } else {
973             return PROPERTY_PHASE_BETWEEN_FILE_READ;
974         }
975     }
976 }
977
978 /* This default implementation serves up static responses, and
979    implements a few default responses based on values from the Device
980    struct. */
981 static gboolean
982 default_device_property_get_ex(
983         Device * self,
984         DevicePropertyId id,
985         GValue * val,
986         PropertySurety *surety,
987         PropertySource *source)
988 {
989     DeviceProperty *prop;
990     GArray *class_properties;
991     PropertyPhaseFlags cur_phase;
992
993     /* Most of this function's job is to sanity-check everything, then
994      * call the relevant getter. */
995
996     class_properties = DEVICE_GET_CLASS(self)->class_properties;
997     if (id >= class_properties->len)
998         return FALSE;
999
1000     prop = &g_array_index(class_properties, DeviceProperty, id);
1001     if (prop->base == NULL)
1002         return FALSE;
1003
1004     if (val || surety || source) {
1005         /* check the phase */
1006         cur_phase = state_to_phase(self);
1007         if (!(prop->access & cur_phase))
1008             return FALSE;
1009
1010         if (prop->getter == NULL)
1011             return FALSE;
1012
1013         if (!prop->getter(self, prop->base, val, surety, source))
1014             return FALSE;
1015     }
1016
1017     return TRUE;
1018 }
1019
1020 static gboolean
1021 default_device_property_set_ex(
1022     Device *self,
1023     DevicePropertyId id,
1024     GValue * val,
1025     PropertySurety surety,
1026     PropertySource source)
1027 {
1028     DeviceProperty *prop;
1029     GArray *class_properties;
1030     PropertyPhaseFlags cur_phase;
1031
1032     /* Most of this function's job is to sanity-check everything, then
1033      * call the relevant setter. */
1034
1035     if (device_in_error(self))
1036         return FALSE;
1037
1038     class_properties = DEVICE_GET_CLASS(self)->class_properties;
1039     if (id >= class_properties->len)
1040         return FALSE;
1041
1042     prop = &g_array_index(class_properties, DeviceProperty, id);
1043     if (prop->base == NULL)
1044         return FALSE;
1045
1046     /* check that the type matches */
1047     if (!G_VALUE_HOLDS(val, prop->base->type))
1048         return FALSE;
1049
1050     /* check the phase */
1051     cur_phase = state_to_phase(self) << PROPERTY_PHASE_SHIFT;
1052     if (!(prop->access & cur_phase))
1053         return FALSE;
1054
1055     if (prop->setter == NULL)
1056         return FALSE;
1057
1058     if (!prop->setter(self, prop->base, val, surety, source))
1059         return FALSE;
1060
1061     return TRUE;
1062 }
1063
1064 const GSList *
1065 device_property_get_list (Device * self)
1066 {
1067     g_assert(IS_DEVICE(self));
1068
1069     return DEVICE_GET_CLASS(self)->class_properties_list;
1070 }
1071
1072 /* XXX WARNING XXX
1073  * All the functions below this comment are stub functions that do nothing
1074  * but implement the virtual function table. Call these functions and they
1075  * will do what you expect vis-a-vis virtual functions. But don't put code
1076  * in them beyond error checking and VFT lookup. */
1077
1078 void
1079 device_open_device (Device * self, char * device_name,
1080         char * device_type, char * device_node)
1081 {
1082     DeviceClass *klass;
1083
1084     g_assert(IS_DEVICE(self));
1085     g_assert(device_name != NULL);
1086
1087     klass = DEVICE_GET_CLASS(self);
1088     g_assert(klass->open_device);
1089     (klass->open_device)(self, device_name, device_type, device_node);
1090 }
1091
1092 DeviceStatusFlags device_read_label(Device * self) {
1093     DeviceClass * klass;
1094
1095     g_assert(self != NULL);
1096     g_assert(IS_DEVICE(self));
1097     g_assert(self->access_mode == ACCESS_NULL);
1098
1099     klass = DEVICE_GET_CLASS(self);
1100     g_assert(klass->read_label);
1101     return (klass->read_label)(self);
1102 }
1103
1104 gboolean
1105 device_finish (Device * self) {
1106     DeviceClass *klass;
1107
1108     g_assert(IS_DEVICE (self));
1109
1110     klass = DEVICE_GET_CLASS(self);
1111     g_assert(klass->finish);
1112     return (klass->finish)(self);
1113 }
1114
1115 gboolean
1116 device_configure (Device * self, gboolean use_global_config)
1117 {
1118     DeviceClass *klass;
1119
1120     g_assert(IS_DEVICE (self));
1121     g_assert(self->access_mode == ACCESS_NULL);
1122
1123     klass = DEVICE_GET_CLASS(self);
1124     if(klass->configure) {
1125         return (klass->configure)(self, use_global_config);
1126     } else {
1127         device_set_error(self,
1128             stralloc(_("Unimplemented method")),
1129             DEVICE_STATUS_DEVICE_ERROR);
1130         return FALSE;
1131     }
1132 }
1133
1134 gboolean
1135 device_start (Device * self, DeviceAccessMode mode,
1136               char * label, char * timestamp)
1137 {
1138     DeviceClass *klass;
1139     char * local_timestamp = NULL;
1140     gboolean rv;
1141
1142     g_assert(IS_DEVICE (self));
1143     g_assert(mode != ACCESS_NULL);
1144     g_assert(mode != ACCESS_WRITE || label != NULL);
1145
1146     klass = DEVICE_GET_CLASS(self);
1147     g_assert(klass->start);
1148
1149     /* For a good combination of synchronization and public simplicity,
1150        this stub function does not require a timestamp, but the actual
1151        implementation function does. We generate the timestamp here with
1152        time(). */
1153     if (mode == ACCESS_WRITE &&
1154         get_timestamp_state(timestamp) == TIME_STATE_REPLACE) {
1155         local_timestamp = timestamp =
1156             get_proper_stamp_from_time(time(NULL));
1157     }
1158
1159     rv = (klass->start)(self, mode, label, timestamp);
1160     amfree(local_timestamp);
1161     return rv;
1162 }
1163
1164 gboolean
1165 device_write_block (Device * self, guint size, gpointer block)
1166 {
1167     DeviceClass *klass;
1168
1169     g_assert(IS_DEVICE (self));
1170     g_assert(size > 0);
1171
1172     /* these are all things that the caller should take care to
1173      * guarantee, so we just assert them here */
1174     g_assert(size <= self->block_size);
1175     g_assert(self->in_file);
1176     g_assert(!selfp->wrote_short_block);
1177     g_assert(block != NULL);
1178     g_assert(IS_WRITABLE_ACCESS_MODE(self->access_mode));
1179
1180     if (size < self->block_size)
1181         selfp->wrote_short_block = TRUE;
1182
1183     klass = DEVICE_GET_CLASS(self);
1184     g_assert(klass->write_block);
1185     return (*klass->write_block)(self,size, block);
1186 }
1187
1188 gboolean
1189 device_start_file (Device * self, dumpfile_t * jobInfo) {
1190     DeviceClass * klass;
1191
1192     g_assert(IS_DEVICE (self));
1193     g_assert(!(self->in_file));
1194     g_assert(jobInfo != NULL);
1195
1196     selfp->wrote_short_block = FALSE;
1197
1198     klass = DEVICE_GET_CLASS(self);
1199     g_assert(klass->start_file);
1200     return (klass->start_file)(self, jobInfo );
1201 }
1202
1203 gboolean
1204 device_finish_file (Device * self)
1205 {
1206     DeviceClass *klass;
1207
1208     g_assert(IS_DEVICE (self));
1209     g_assert(IS_WRITABLE_ACCESS_MODE(self->access_mode));
1210     g_assert(self->in_file);
1211
1212     klass = DEVICE_GET_CLASS(self);
1213     g_assert(klass->finish_file);
1214     return (klass->finish_file)(self);
1215 }
1216
1217 dumpfile_t*
1218 device_seek_file (Device * self, guint file)
1219 {
1220     DeviceClass *klass;
1221
1222     g_assert(IS_DEVICE (self));
1223     g_assert(self->access_mode == ACCESS_READ);
1224
1225     klass = DEVICE_GET_CLASS(self);
1226     g_assert(klass->seek_file);
1227     return (klass->seek_file)(self,file);
1228 }
1229
1230 gboolean
1231 device_seek_block (Device * self, guint64 block)
1232 {
1233     DeviceClass *klass;
1234
1235     g_assert(IS_DEVICE (self));
1236     g_assert(self->access_mode == ACCESS_READ);
1237     g_assert(self->in_file);
1238
1239     klass = DEVICE_GET_CLASS(self);
1240     g_assert(klass->seek_block);
1241     return (klass->seek_block)(self,block);
1242 }
1243
1244 int
1245 device_read_block (Device * self, gpointer buffer, int * size)
1246 {
1247     DeviceClass *klass;
1248
1249     g_assert(IS_DEVICE (self));
1250     g_assert(size != NULL);
1251     g_assert(self->access_mode == ACCESS_READ);
1252
1253     if (*size != 0) {
1254         g_assert(buffer != NULL);
1255     }
1256
1257     klass = DEVICE_GET_CLASS(self);
1258     g_assert(klass->read_block);
1259     return (klass->read_block)(self,buffer,size);
1260 }
1261
1262 gboolean
1263 device_property_get_ex(
1264         Device * self,
1265         DevicePropertyId id,
1266         GValue * val,
1267         PropertySurety *surety,
1268         PropertySource *source)
1269 {
1270     DeviceClass *klass;
1271
1272     g_assert(IS_DEVICE (self));
1273     g_assert(device_property_get_by_id(id) != NULL);
1274
1275     klass = DEVICE_GET_CLASS(self);
1276
1277     g_assert(klass->property_get_ex);
1278     return (klass->property_get_ex)(self, id, val, surety, source);
1279 }
1280
1281 gboolean
1282 device_property_set_ex(
1283         Device * self,
1284         DevicePropertyId id,
1285         GValue * val,
1286         PropertySurety surety,
1287         PropertySource source)
1288 {
1289     DeviceClass *klass;
1290
1291     g_assert(IS_DEVICE (self));
1292
1293     klass = DEVICE_GET_CLASS(self);
1294
1295     g_assert(klass->property_set_ex);
1296     return (klass->property_set_ex)(self, id, val, surety, source);
1297 }
1298
1299 gboolean
1300 device_recycle_file (Device * self, guint filenum)
1301 {
1302     DeviceClass *klass;
1303
1304     g_assert(self != NULL);
1305     g_assert(IS_DEVICE (self));
1306     g_assert(self->access_mode == ACCESS_APPEND);
1307     g_assert(!self->in_file);
1308
1309     klass = DEVICE_GET_CLASS(self);
1310
1311     g_assert(klass->recycle_file);
1312     return (klass->recycle_file)(self,filenum);
1313 }
1314
1315 gboolean
1316 device_erase (Device * self)
1317 {
1318     DeviceClass *klass;
1319
1320     g_assert(IS_DEVICE (self));
1321     g_assert(self->access_mode == ACCESS_NULL);
1322     g_assert(!self->in_file);
1323
1324     klass = DEVICE_GET_CLASS(self);
1325     if(klass->erase) {
1326         return (klass->erase)(self);
1327     } else {
1328         device_set_error(self,
1329             stralloc(_("Unimplemented method")),
1330             DEVICE_STATUS_DEVICE_ERROR);
1331         return FALSE;
1332     }
1333 }
1334
1335 gboolean
1336 device_eject (Device * self)
1337 {
1338     DeviceClass *klass;
1339
1340     g_assert(IS_DEVICE (self));
1341     g_assert(self->access_mode == ACCESS_NULL);
1342     g_assert(!self->in_file);
1343
1344     klass = DEVICE_GET_CLASS(self);
1345     if (klass->eject) {
1346         return (klass->eject)(self);
1347     } else {
1348         return TRUE;
1349     }
1350 }
1351
1352 gboolean
1353 device_listen(
1354     Device *self,
1355     gboolean for_writing,
1356     DirectTCPAddr **addrs)
1357 {
1358     DeviceClass *klass;
1359
1360     klass = DEVICE_GET_CLASS(self);
1361     if(klass->listen) {
1362         return (klass->listen)(self, for_writing, addrs);
1363     } else {
1364         device_set_error(self,
1365             stralloc(_("Unimplemented method")),
1366             DEVICE_STATUS_DEVICE_ERROR);
1367         return FALSE;
1368     }
1369 }
1370
1371 gboolean
1372 device_accept(
1373     Device *self,
1374     DirectTCPConnection **conn,
1375     ProlongProc prolong,
1376     gpointer prolong_data)
1377 {
1378     DeviceClass *klass;
1379
1380     klass = DEVICE_GET_CLASS(self);
1381     if(klass->accept) {
1382         return (klass->accept)(self, conn, prolong, prolong_data);
1383     } else {
1384         device_set_error(self,
1385             stralloc(_("Unimplemented method")),
1386             DEVICE_STATUS_DEVICE_ERROR);
1387         return FALSE;
1388     }
1389 }
1390
1391 gboolean
1392 device_connect(
1393     Device *self,
1394     gboolean for_writing,
1395     DirectTCPAddr *addrs,
1396     DirectTCPConnection **conn,
1397     ProlongProc prolong,
1398     gpointer prolong_data)
1399 {
1400     DeviceClass *klass;
1401
1402     klass = DEVICE_GET_CLASS(self);
1403     if(klass->connect) {
1404         return (klass->connect)(self, for_writing, addrs, conn, prolong, prolong_data);
1405     } else {
1406         device_set_error(self,
1407             stralloc(_("Unimplemented method")),
1408             DEVICE_STATUS_DEVICE_ERROR);
1409         return FALSE;
1410     }
1411 }
1412
1413 gboolean
1414 device_write_from_connection(
1415     Device *self,
1416     guint64 size,
1417     guint64 *actual_size)
1418 {
1419     DeviceClass *klass;
1420
1421     klass = DEVICE_GET_CLASS(self);
1422
1423     g_assert(self->in_file);
1424     g_assert(IS_WRITABLE_ACCESS_MODE(self->access_mode));
1425
1426     if(klass->write_from_connection) {
1427         return (klass->write_from_connection)(self, size, actual_size);
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_read_to_connection(
1438     Device *self,
1439     guint64 size,
1440     guint64 *actual_size)
1441 {
1442     DeviceClass *klass;
1443
1444     g_assert(self->in_file);
1445     g_assert(self->access_mode == ACCESS_READ);
1446
1447     klass = DEVICE_GET_CLASS(self);
1448     if(klass->read_to_connection) {
1449         return (klass->read_to_connection)(self, size, actual_size);
1450     } else {
1451         device_set_error(self,
1452             stralloc(_("Unimplemented method")),
1453             DEVICE_STATUS_DEVICE_ERROR);
1454         return FALSE;
1455     }
1456 }
1457
1458 gboolean
1459 device_use_connection(
1460     Device *self,
1461     DirectTCPConnection *conn)
1462 {
1463     DeviceClass *klass;
1464
1465     g_assert(self->access_mode == ACCESS_NULL);
1466
1467     klass = DEVICE_GET_CLASS(self);
1468     if(klass->use_connection) {
1469         return (klass->use_connection)(self, conn);
1470     } else {
1471         device_set_error(self,
1472             stralloc(_("Unimplemented method")),
1473             DEVICE_STATUS_DEVICE_ERROR);
1474         return FALSE;
1475     }
1476 }
1477
1478 /* Property handling */
1479
1480 void
1481 device_class_register_property(
1482         DeviceClass *klass,
1483         DevicePropertyId id,
1484         PropertyAccessFlags access,
1485         PropertyGetFn getter,
1486         PropertySetFn setter)
1487 {
1488     DevicePropertyBase *base;
1489     DeviceProperty *prop;
1490     GSList *proplist;
1491     guint i;
1492
1493     g_assert(klass != NULL);
1494
1495     base = device_property_get_by_id(id);
1496     g_assert(base != NULL);
1497
1498     if (klass->class_properties->len <= id) {
1499         g_array_set_size(klass->class_properties, id+1);
1500     }
1501
1502     prop = &g_array_index(klass->class_properties, DeviceProperty, id);
1503     prop->base = base;
1504     prop->access = access;
1505     prop->getter = getter;
1506     prop->setter = setter;
1507
1508     /* completely rewrite the list of prop pointers, as they may have changed,
1509      * or we may have replaced an existing property*/
1510
1511     if (klass->class_properties_list) {
1512         g_slist_free(klass->class_properties_list);
1513     }
1514
1515     proplist = NULL;
1516     for (i = 0; i < klass->class_properties->len; i++) {
1517         prop = &g_array_index(klass->class_properties, DeviceProperty, i);
1518         if (!prop->base)
1519             continue;
1520         proplist = g_slist_prepend(proplist, prop);
1521     }
1522
1523     klass->class_properties_list = proplist;
1524 }
1525
1526 gboolean
1527 device_set_simple_property(
1528         Device *self,
1529         DevicePropertyId id,
1530         GValue *val,
1531         PropertySurety surety,
1532         PropertySource source)
1533 {
1534     SimpleProperty *simp;
1535     DeviceProperty *prop;
1536
1537     prop = &g_array_index(DEVICE_GET_CLASS(self)->class_properties,
1538                           DeviceProperty, id);
1539
1540     /* these assertions should already be checked, but let's be sure */
1541     g_assert(prop->base != NULL);   /* prop must be registered with device */
1542     g_assert(G_VALUE_HOLDS(val, prop->base->type));
1543
1544     simp = g_new0(SimpleProperty, 1);
1545     simp->prop = prop;
1546     g_value_unset_copy(val, &(simp->response));
1547     simp->surety = surety;
1548     simp->source = source;
1549
1550     g_hash_table_insert(selfp->simple_properties,
1551                         GINT_TO_POINTER(id),
1552                         simp);
1553
1554     return TRUE;
1555 }
1556
1557 gboolean
1558 device_simple_property_set_fn(
1559         Device *self,
1560         DevicePropertyBase *base,
1561         GValue *val,
1562         PropertySurety surety,
1563         PropertySource source)
1564 {
1565     return device_set_simple_property(self, base->ID, val, surety, source);
1566 }
1567
1568 gboolean
1569 device_get_simple_property(
1570         Device *self,
1571         DevicePropertyId id,
1572         GValue *val,
1573         PropertySurety *surety,
1574         PropertySource *source)
1575 {
1576     SimpleProperty *simp =
1577         g_hash_table_lookup(selfp->simple_properties,
1578                             GINT_TO_POINTER(id));
1579
1580     if (!simp)
1581         return FALSE;
1582
1583     if (val)
1584         g_value_unset_copy(&(simp->response), val);
1585
1586     if (surety)
1587         *surety = simp->surety;
1588
1589     if (source)
1590         *source = simp->source;
1591
1592     return TRUE;
1593 }
1594
1595 gboolean
1596 device_simple_property_get_fn(
1597         Device *self,
1598         DevicePropertyBase *base,
1599         GValue *val,
1600         PropertySurety *surety,
1601         PropertySource *source)
1602 {
1603     return device_get_simple_property(self, base->ID, val, surety, source);
1604 }