f0258d5d25b20cba4b4a69aa999312e21016fa82
[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         device_set_error(self,
886             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),
887             DEVICE_STATUS_DEVICE_ERROR);
888         return FALSE;
889     }
890
891     self->block_size = block_size;
892     self->block_size_surety = surety;
893     self->block_size_source = source;
894
895     return TRUE;
896 }
897
898 static gboolean
899 property_get_min_block_size_fn(
900         Device *self,
901         DevicePropertyBase *base G_GNUC_UNUSED,
902         GValue *val,
903         PropertySurety *surety,
904         PropertySource *source)
905 {
906     g_value_unset_init(val, G_TYPE_UINT);
907     g_assert(self->block_size < G_MAXUINT); /* gsize -> guint */
908     g_value_set_uint(val, (guint)self->min_block_size);
909
910     if (surety)
911         *surety = PROPERTY_SURETY_GOOD;
912
913     if (source)
914         *source = PROPERTY_SOURCE_DEFAULT;
915
916     return TRUE;
917 }
918
919 static gboolean
920 property_get_max_block_size_fn(
921         Device *self,
922         DevicePropertyBase *base G_GNUC_UNUSED,
923         GValue *val,
924         PropertySurety *surety,
925         PropertySource *source)
926 {
927     g_value_unset_init(val, G_TYPE_UINT);
928     g_assert(self->block_size < G_MAXUINT); /* gsize -> guint */
929     g_value_set_uint(val, (guint)self->max_block_size);
930
931     if (surety)
932         *surety = PROPERTY_SURETY_GOOD;
933
934     if (source)
935         *source = PROPERTY_SOURCE_DEFAULT;
936
937     return TRUE;
938 }
939
940 static gboolean
941 property_get_canonical_name_fn(
942         Device *self,
943         DevicePropertyBase *base G_GNUC_UNUSED,
944         GValue *val,
945         PropertySurety *surety,
946         PropertySource *source)
947 {
948     g_value_unset_init(val, G_TYPE_STRING);
949     g_value_set_string(val, self->device_name);
950
951     if (surety)
952         *surety = PROPERTY_SURETY_GOOD;
953
954     if (source)
955         *source = PROPERTY_SOURCE_DEFAULT;
956
957     return TRUE;
958 }
959
960 /* util function */
961 static PropertyPhaseFlags
962 state_to_phase(
963     Device *self)
964 {
965     if (self->access_mode == ACCESS_NULL) {
966         return PROPERTY_PHASE_BEFORE_START;
967     } else if (IS_WRITABLE_ACCESS_MODE(self->access_mode)) {
968         if (self->in_file) {
969             return PROPERTY_PHASE_INSIDE_FILE_WRITE;
970         } else {
971             return PROPERTY_PHASE_BETWEEN_FILE_WRITE;
972         }
973     } else { /* read mode */
974         if (self->in_file) {
975             return PROPERTY_PHASE_INSIDE_FILE_READ;
976         } else {
977             return PROPERTY_PHASE_BETWEEN_FILE_READ;
978         }
979     }
980 }
981
982 /* This default implementation serves up static responses, and
983    implements a few default responses based on values from the Device
984    struct. */
985 static gboolean
986 default_device_property_get_ex(
987         Device * self,
988         DevicePropertyId id,
989         GValue * val,
990         PropertySurety *surety,
991         PropertySource *source)
992 {
993     DeviceProperty *prop;
994     GArray *class_properties;
995     PropertyPhaseFlags cur_phase;
996
997     /* Most of this function's job is to sanity-check everything, then
998      * call the relevant getter. */
999
1000     class_properties = DEVICE_GET_CLASS(self)->class_properties;
1001     if (id >= class_properties->len)
1002         return FALSE;
1003
1004     prop = &g_array_index(class_properties, DeviceProperty, id);
1005     if (prop->base == NULL)
1006         return FALSE;
1007
1008     if (val || surety || source) {
1009         /* check the phase */
1010         cur_phase = state_to_phase(self);
1011         if (!(prop->access & cur_phase))
1012             return FALSE;
1013
1014         if (prop->getter == NULL)
1015             return FALSE;
1016
1017         if (!prop->getter(self, prop->base, val, surety, source))
1018             return FALSE;
1019     }
1020
1021     return TRUE;
1022 }
1023
1024 static gboolean
1025 default_device_property_set_ex(
1026     Device *self,
1027     DevicePropertyId id,
1028     GValue * val,
1029     PropertySurety surety,
1030     PropertySource source)
1031 {
1032     DeviceProperty *prop;
1033     GArray *class_properties;
1034     PropertyPhaseFlags cur_phase;
1035
1036     /* Most of this function's job is to sanity-check everything, then
1037      * call the relevant setter. */
1038
1039     if (device_in_error(self))
1040         return FALSE;
1041
1042     class_properties = DEVICE_GET_CLASS(self)->class_properties;
1043     if (id >= class_properties->len)
1044         return FALSE;
1045
1046     prop = &g_array_index(class_properties, DeviceProperty, id);
1047     if (prop->base == NULL)
1048         return FALSE;
1049
1050     /* check that the type matches */
1051     if (!G_VALUE_HOLDS(val, prop->base->type))
1052         return FALSE;
1053
1054     /* check the phase */
1055     cur_phase = state_to_phase(self) << PROPERTY_PHASE_SHIFT;
1056     if (!(prop->access & cur_phase))
1057         return FALSE;
1058
1059     if (prop->setter == NULL)
1060         return FALSE;
1061
1062     if (!prop->setter(self, prop->base, val, surety, source))
1063         return FALSE;
1064
1065     return TRUE;
1066 }
1067
1068 const GSList *
1069 device_property_get_list (Device * self)
1070 {
1071     g_assert(IS_DEVICE(self));
1072
1073     return DEVICE_GET_CLASS(self)->class_properties_list;
1074 }
1075
1076 /* XXX WARNING XXX
1077  * All the functions below this comment are stub functions that do nothing
1078  * but implement the virtual function table. Call these functions and they
1079  * will do what you expect vis-a-vis virtual functions. But don't put code
1080  * in them beyond error checking and VFT lookup. */
1081
1082 void
1083 device_open_device (Device * self, char * device_name,
1084         char * device_type, char * device_node)
1085 {
1086     DeviceClass *klass;
1087
1088     g_assert(IS_DEVICE(self));
1089     g_assert(device_name != NULL);
1090
1091     klass = DEVICE_GET_CLASS(self);
1092     g_assert(klass->open_device);
1093     (klass->open_device)(self, device_name, device_type, device_node);
1094 }
1095
1096 DeviceStatusFlags device_read_label(Device * self) {
1097     DeviceClass * klass;
1098
1099     g_assert(self != NULL);
1100     g_assert(IS_DEVICE(self));
1101     g_assert(self->access_mode == ACCESS_NULL);
1102
1103     klass = DEVICE_GET_CLASS(self);
1104     g_assert(klass->read_label);
1105     return (klass->read_label)(self);
1106 }
1107
1108 gboolean
1109 device_finish (Device * self) {
1110     DeviceClass *klass;
1111
1112     g_assert(IS_DEVICE (self));
1113
1114     klass = DEVICE_GET_CLASS(self);
1115     g_assert(klass->finish);
1116     return (klass->finish)(self);
1117 }
1118
1119 gboolean
1120 device_configure (Device * self, gboolean use_global_config)
1121 {
1122     DeviceClass *klass;
1123
1124     g_assert(IS_DEVICE (self));
1125     g_assert(self->access_mode == ACCESS_NULL);
1126
1127     klass = DEVICE_GET_CLASS(self);
1128     if(klass->configure) {
1129         return (klass->configure)(self, use_global_config);
1130     } else {
1131         device_set_error(self,
1132             stralloc(_("Unimplemented method")),
1133             DEVICE_STATUS_DEVICE_ERROR);
1134         return FALSE;
1135     }
1136 }
1137
1138 gboolean
1139 device_start (Device * self, DeviceAccessMode mode,
1140               char * label, char * timestamp)
1141 {
1142     DeviceClass *klass;
1143     char * local_timestamp = NULL;
1144     gboolean rv;
1145
1146     g_assert(IS_DEVICE (self));
1147     g_assert(mode != ACCESS_NULL);
1148     g_assert(mode != ACCESS_WRITE || label != NULL);
1149
1150     klass = DEVICE_GET_CLASS(self);
1151     g_assert(klass->start);
1152
1153     /* For a good combination of synchronization and public simplicity,
1154        this stub function does not require a timestamp, but the actual
1155        implementation function does. We generate the timestamp here with
1156        time(). */
1157     if (mode == ACCESS_WRITE &&
1158         get_timestamp_state(timestamp) == TIME_STATE_REPLACE) {
1159         local_timestamp = timestamp =
1160             get_proper_stamp_from_time(time(NULL));
1161     }
1162
1163     rv = (klass->start)(self, mode, label, timestamp);
1164     amfree(local_timestamp);
1165     return rv;
1166 }
1167
1168 gboolean
1169 device_write_block (Device * self, guint size, gpointer block)
1170 {
1171     DeviceClass *klass;
1172
1173     g_assert(IS_DEVICE (self));
1174     g_assert(size > 0);
1175
1176     /* these are all things that the caller should take care to
1177      * guarantee, so we just assert them here */
1178     g_assert(size <= self->block_size);
1179     g_assert(self->in_file);
1180     g_assert(!selfp->wrote_short_block);
1181     g_assert(block != NULL);
1182     g_assert(IS_WRITABLE_ACCESS_MODE(self->access_mode));
1183
1184     if (size < self->block_size)
1185         selfp->wrote_short_block = TRUE;
1186
1187     klass = DEVICE_GET_CLASS(self);
1188     g_assert(klass->write_block);
1189     return (*klass->write_block)(self,size, block);
1190 }
1191
1192 gboolean
1193 device_start_file (Device * self, dumpfile_t * jobInfo) {
1194     DeviceClass * klass;
1195
1196     g_assert(IS_DEVICE (self));
1197     g_assert(!(self->in_file));
1198     g_assert(jobInfo != NULL);
1199
1200     selfp->wrote_short_block = FALSE;
1201
1202     klass = DEVICE_GET_CLASS(self);
1203     g_assert(klass->start_file);
1204     return (klass->start_file)(self, jobInfo );
1205 }
1206
1207 gboolean
1208 device_finish_file (Device * self)
1209 {
1210     DeviceClass *klass;
1211
1212     g_assert(IS_DEVICE (self));
1213     g_assert(IS_WRITABLE_ACCESS_MODE(self->access_mode));
1214     g_assert(self->in_file);
1215
1216     klass = DEVICE_GET_CLASS(self);
1217     g_assert(klass->finish_file);
1218     return (klass->finish_file)(self);
1219 }
1220
1221 dumpfile_t*
1222 device_seek_file (Device * self, guint file)
1223 {
1224     DeviceClass *klass;
1225
1226     g_assert(IS_DEVICE (self));
1227     g_assert(self->access_mode == ACCESS_READ);
1228
1229     klass = DEVICE_GET_CLASS(self);
1230     g_assert(klass->seek_file);
1231     return (klass->seek_file)(self,file);
1232 }
1233
1234 gboolean
1235 device_seek_block (Device * self, guint64 block)
1236 {
1237     DeviceClass *klass;
1238
1239     g_assert(IS_DEVICE (self));
1240     g_assert(self->access_mode == ACCESS_READ);
1241     g_assert(self->in_file);
1242
1243     klass = DEVICE_GET_CLASS(self);
1244     g_assert(klass->seek_block);
1245     return (klass->seek_block)(self,block);
1246 }
1247
1248 int
1249 device_read_block (Device * self, gpointer buffer, int * size)
1250 {
1251     DeviceClass *klass;
1252
1253     g_assert(IS_DEVICE (self));
1254     g_assert(size != NULL);
1255     g_assert(self->access_mode == ACCESS_READ);
1256
1257     if (*size != 0) {
1258         g_assert(buffer != NULL);
1259     }
1260
1261     klass = DEVICE_GET_CLASS(self);
1262     g_assert(klass->read_block);
1263     return (klass->read_block)(self,buffer,size);
1264 }
1265
1266 gboolean
1267 device_property_get_ex(
1268         Device * self,
1269         DevicePropertyId id,
1270         GValue * val,
1271         PropertySurety *surety,
1272         PropertySource *source)
1273 {
1274     DeviceClass *klass;
1275
1276     g_assert(IS_DEVICE (self));
1277     g_assert(device_property_get_by_id(id) != NULL);
1278
1279     klass = DEVICE_GET_CLASS(self);
1280
1281     g_assert(klass->property_get_ex);
1282     return (klass->property_get_ex)(self, id, val, surety, source);
1283 }
1284
1285 gboolean
1286 device_property_set_ex(
1287         Device * self,
1288         DevicePropertyId id,
1289         GValue * val,
1290         PropertySurety surety,
1291         PropertySource source)
1292 {
1293     DeviceClass *klass;
1294
1295     g_assert(IS_DEVICE (self));
1296
1297     klass = DEVICE_GET_CLASS(self);
1298
1299     g_assert(klass->property_set_ex);
1300     return (klass->property_set_ex)(self, id, val, surety, source);
1301 }
1302
1303 gboolean
1304 device_recycle_file (Device * self, guint filenum)
1305 {
1306     DeviceClass *klass;
1307
1308     g_assert(self != NULL);
1309     g_assert(IS_DEVICE (self));
1310     g_assert(self->access_mode == ACCESS_APPEND);
1311     g_assert(!self->in_file);
1312
1313     klass = DEVICE_GET_CLASS(self);
1314
1315     g_assert(klass->recycle_file);
1316     return (klass->recycle_file)(self,filenum);
1317 }
1318
1319 gboolean
1320 device_erase (Device * self)
1321 {
1322     DeviceClass *klass;
1323
1324     g_assert(IS_DEVICE (self));
1325     g_assert(self->access_mode == ACCESS_NULL);
1326     g_assert(!self->in_file);
1327
1328     klass = DEVICE_GET_CLASS(self);
1329     if(klass->erase) {
1330         return (klass->erase)(self);
1331     } else {
1332         device_set_error(self,
1333             stralloc(_("Unimplemented method")),
1334             DEVICE_STATUS_DEVICE_ERROR);
1335         return FALSE;
1336     }
1337 }
1338
1339 gboolean
1340 device_eject (Device * self)
1341 {
1342     DeviceClass *klass;
1343
1344     g_assert(IS_DEVICE (self));
1345     g_assert(self->access_mode == ACCESS_NULL);
1346     g_assert(!self->in_file);
1347
1348     klass = DEVICE_GET_CLASS(self);
1349     if (klass->eject) {
1350         return (klass->eject)(self);
1351     } else {
1352         return TRUE;
1353     }
1354 }
1355
1356 gboolean
1357 device_listen(
1358     Device *self,
1359     gboolean for_writing,
1360     DirectTCPAddr **addrs)
1361 {
1362     DeviceClass *klass;
1363
1364     klass = DEVICE_GET_CLASS(self);
1365     if(klass->listen) {
1366         return (klass->listen)(self, for_writing, addrs);
1367     } else {
1368         device_set_error(self,
1369             stralloc(_("Unimplemented method")),
1370             DEVICE_STATUS_DEVICE_ERROR);
1371         return FALSE;
1372     }
1373 }
1374
1375 gboolean
1376 device_accept(
1377     Device *self,
1378     DirectTCPConnection **conn,
1379     ProlongProc prolong,
1380     gpointer prolong_data)
1381 {
1382     DeviceClass *klass;
1383
1384     klass = DEVICE_GET_CLASS(self);
1385     if(klass->accept) {
1386         return (klass->accept)(self, conn, prolong, prolong_data);
1387     } else {
1388         device_set_error(self,
1389             stralloc(_("Unimplemented method")),
1390             DEVICE_STATUS_DEVICE_ERROR);
1391         return FALSE;
1392     }
1393 }
1394
1395 gboolean
1396 device_connect(
1397     Device *self,
1398     gboolean for_writing,
1399     DirectTCPAddr *addrs,
1400     DirectTCPConnection **conn,
1401     ProlongProc prolong,
1402     gpointer prolong_data)
1403 {
1404     DeviceClass *klass;
1405
1406     klass = DEVICE_GET_CLASS(self);
1407     if(klass->connect) {
1408         return (klass->connect)(self, for_writing, addrs, conn, prolong, prolong_data);
1409     } else {
1410         device_set_error(self,
1411             stralloc(_("Unimplemented method")),
1412             DEVICE_STATUS_DEVICE_ERROR);
1413         return FALSE;
1414     }
1415 }
1416
1417 gboolean
1418 device_write_from_connection(
1419     Device *self,
1420     guint64 size,
1421     guint64 *actual_size)
1422 {
1423     DeviceClass *klass;
1424
1425     klass = DEVICE_GET_CLASS(self);
1426
1427     g_assert(self->in_file);
1428     g_assert(IS_WRITABLE_ACCESS_MODE(self->access_mode));
1429
1430     if(klass->write_from_connection) {
1431         return (klass->write_from_connection)(self, size, actual_size);
1432     } else {
1433         device_set_error(self,
1434             stralloc(_("Unimplemented method")),
1435             DEVICE_STATUS_DEVICE_ERROR);
1436         return FALSE;
1437     }
1438 }
1439
1440 gboolean
1441 device_read_to_connection(
1442     Device *self,
1443     guint64 size,
1444     guint64 *actual_size)
1445 {
1446     DeviceClass *klass;
1447
1448     g_assert(self->in_file);
1449     g_assert(self->access_mode == ACCESS_READ);
1450
1451     klass = DEVICE_GET_CLASS(self);
1452     if(klass->read_to_connection) {
1453         return (klass->read_to_connection)(self, size, actual_size);
1454     } else {
1455         device_set_error(self,
1456             stralloc(_("Unimplemented method")),
1457             DEVICE_STATUS_DEVICE_ERROR);
1458         return FALSE;
1459     }
1460 }
1461
1462 gboolean
1463 device_use_connection(
1464     Device *self,
1465     DirectTCPConnection *conn)
1466 {
1467     DeviceClass *klass;
1468
1469     g_assert(self->access_mode == ACCESS_NULL);
1470
1471     klass = DEVICE_GET_CLASS(self);
1472     if(klass->use_connection) {
1473         return (klass->use_connection)(self, conn);
1474     } else {
1475         device_set_error(self,
1476             stralloc(_("Unimplemented method")),
1477             DEVICE_STATUS_DEVICE_ERROR);
1478         return FALSE;
1479     }
1480 }
1481
1482 /* Property handling */
1483
1484 void
1485 device_class_register_property(
1486         DeviceClass *klass,
1487         DevicePropertyId id,
1488         PropertyAccessFlags access,
1489         PropertyGetFn getter,
1490         PropertySetFn setter)
1491 {
1492     DevicePropertyBase *base;
1493     DeviceProperty *prop;
1494     GSList *proplist;
1495     guint i;
1496
1497     g_assert(klass != NULL);
1498
1499     base = device_property_get_by_id(id);
1500     g_assert(base != NULL);
1501
1502     if (klass->class_properties->len <= id) {
1503         g_array_set_size(klass->class_properties, id+1);
1504     }
1505
1506     prop = &g_array_index(klass->class_properties, DeviceProperty, id);
1507     prop->base = base;
1508     prop->access = access;
1509     prop->getter = getter;
1510     prop->setter = setter;
1511
1512     /* completely rewrite the list of prop pointers, as they may have changed,
1513      * or we may have replaced an existing property*/
1514
1515     if (klass->class_properties_list) {
1516         g_slist_free(klass->class_properties_list);
1517     }
1518
1519     proplist = NULL;
1520     for (i = 0; i < klass->class_properties->len; i++) {
1521         prop = &g_array_index(klass->class_properties, DeviceProperty, i);
1522         if (!prop->base)
1523             continue;
1524         proplist = g_slist_prepend(proplist, prop);
1525     }
1526
1527     klass->class_properties_list = proplist;
1528 }
1529
1530 gboolean
1531 device_set_simple_property(
1532         Device *self,
1533         DevicePropertyId id,
1534         GValue *val,
1535         PropertySurety surety,
1536         PropertySource source)
1537 {
1538     SimpleProperty *simp;
1539     DeviceProperty *prop;
1540
1541     prop = &g_array_index(DEVICE_GET_CLASS(self)->class_properties,
1542                           DeviceProperty, id);
1543
1544     /* these assertions should already be checked, but let's be sure */
1545     g_assert(prop->base != NULL);   /* prop must be registered with device */
1546     g_assert(G_VALUE_HOLDS(val, prop->base->type));
1547
1548     simp = g_new0(SimpleProperty, 1);
1549     simp->prop = prop;
1550     g_value_unset_copy(val, &(simp->response));
1551     simp->surety = surety;
1552     simp->source = source;
1553
1554     g_hash_table_insert(selfp->simple_properties,
1555                         GINT_TO_POINTER(id),
1556                         simp);
1557
1558     return TRUE;
1559 }
1560
1561 gboolean
1562 device_simple_property_set_fn(
1563         Device *self,
1564         DevicePropertyBase *base,
1565         GValue *val,
1566         PropertySurety surety,
1567         PropertySource source)
1568 {
1569     return device_set_simple_property(self, base->ID, val, surety, source);
1570 }
1571
1572 gboolean
1573 device_get_simple_property(
1574         Device *self,
1575         DevicePropertyId id,
1576         GValue *val,
1577         PropertySurety *surety,
1578         PropertySource *source)
1579 {
1580     SimpleProperty *simp =
1581         g_hash_table_lookup(selfp->simple_properties,
1582                             GINT_TO_POINTER(id));
1583
1584     if (!simp)
1585         return FALSE;
1586
1587     if (val)
1588         g_value_unset_copy(&(simp->response), val);
1589
1590     if (surety)
1591         *surety = simp->surety;
1592
1593     if (source)
1594         *source = simp->source;
1595
1596     return TRUE;
1597 }
1598
1599 gboolean
1600 device_simple_property_get_fn(
1601         Device *self,
1602         DevicePropertyBase *base,
1603         GValue *val,
1604         PropertySurety *surety,
1605         PropertySource *source)
1606 {
1607     return device_get_simple_property(self, base->ID, val, surety, source);
1608 }