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