2 * Copyright (c) 2009-2012 Zmanda, Inc. All Rights Reserved.
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.
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
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
17 * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
18 * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
24 #include "directtcp.h"
27 #include "ndmpconnobj.h"
28 #include "sockaddr-util.h"
31 * Type checking and casting macros
33 #define TYPE_NDMP_DEVICE (ndmp_device_get_type())
34 #define NDMP_DEVICE(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), ndmp_device_get_type(), NdmpDevice)
35 #define NDMP_DEVICE_CONST(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), ndmp_device_get_type(), NdmpDevice const)
36 #define NDMP_DEVICE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST((klass), ndmp_device_get_type(), NdmpDeviceClass)
37 #define IS_NDMP_DEVICE(obj) G_TYPE_CHECK_INSTANCE_TYPE((obj), ndmp_device_get_type ())
38 #define NDMP_DEVICE_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS((obj), ndmp_device_get_type(), NdmpDeviceClass)
39 static GType ndmp_device_get_type (void);
42 * Main object structure
45 typedef struct NdmpDevice_ NdmpDevice;
51 /* true if tape service is open on the NDMP server */
54 /* addresses the object is listening on, and how the connection
56 DirectTCPAddr *listen_addrs;
59 /* support for IndirectTCP */
60 int indirecttcp_sock; /* -1 if not in use */
63 /* Current DirectTCPConnectionNDMP */
64 struct DirectTCPConnectionNDMP_ *directtcp_conn;
66 /* constructor parameters and properties */
69 gchar *ndmp_device_name;
74 gsize read_block_size;
81 typedef struct NdmpDeviceClass_ NdmpDeviceClass;
82 struct NdmpDeviceClass_ {
83 DeviceClass __parent__;
87 * A directtcp connection subclass representing a running mover on the other end of
88 * the given NDMP connection
91 #define TYPE_DIRECTTCP_CONNECTION_NDMP (directtcp_connection_ndmp_get_type())
92 #define DIRECTTCP_CONNECTION_NDMP(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), directtcp_connection_ndmp_get_type(), DirectTCPConnectionNDMP)
93 #define DIRECTTCP_CONNECTION_NDMP_CONST(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), directtcp_connection_ndmp_get_type(), DirectTCPConnectionNDMP const)
94 #define DIRECTTCP_CONNECTION_NDMP_CLASS(klass) G_TYPE_CHECK_CLASS_CAST((klass), directtcp_connection_ndmp_get_type(), DirectTCPConnectionNDMPClass)
95 #define IS_DIRECTTCP_CONNECTION_NDMP(obj) G_TYPE_CHECK_INSTANCE_TYPE((obj), directtcp_connection_ndmp_get_type ())
96 #define DIRECTTCP_CONNECTION_NDMP_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS((obj), directtcp_connection_ndmp_get_type(), DirectTCPConnectionNDMPClass)
97 GType directtcp_connection_ndmp_get_type(void);
99 typedef struct DirectTCPConnectionNDMP_ {
100 DirectTCPConnection __parent__;
102 /* NDMP connection controlling the mover */
103 NDMPConnection *ndmp;
105 /* mode for this operation */
106 ndmp9_mover_mode mode;
108 /* last reported mover position in the datastream */
110 } DirectTCPConnectionNDMP;
112 typedef struct DirectTCPConnectionNDMPClass_ {
113 DirectTCPConnectionClass __parent__;
114 } DirectTCPConnectionNDMPClass;
116 static DirectTCPConnectionNDMP *directtcp_connection_ndmp_new(
117 NDMPConnection *ndmp,
118 ndmp9_mover_mode mode);
121 * Constants and static data
124 #define NDMP_DEVICE_NAME "ndmp"
126 /* pointer to the class of our parent */
127 static DeviceClass *parent_class = NULL;
129 /* robust_write results */
132 ROBUST_WRITE_OK_LEOM,
133 ROBUST_WRITE_ERROR, /* device error already set */
134 ROBUST_WRITE_NO_SPACE
135 } robust_write_result;
138 * device-specific properties
141 /* Authentication information for NDMP agent. Both of these are strings. */
142 static DevicePropertyBase device_property_ndmp_username;
143 static DevicePropertyBase device_property_ndmp_password;
144 static DevicePropertyBase device_property_ndmp_auth;
145 static DevicePropertyBase device_property_indirect;
146 #define PROPERTY_NDMP_USERNAME (device_property_ndmp_username.ID)
147 #define PROPERTY_NDMP_PASSWORD (device_property_ndmp_password.ID)
148 #define PROPERTY_NDMP_AUTH (device_property_ndmp_auth.ID)
149 #define PROPERTY_INDIRECT (device_property_indirect.ID)
156 void ndmp_device_register(void);
157 static void set_error_from_ndmp(NdmpDevice *self);
159 #define ndmp_device_read_size(self) \
160 (((NdmpDevice *)(self))->read_block_size? \
161 ((NdmpDevice *)(self))->read_block_size : ((Device *)(self))->block_size)
172 self->ndmp = ndmp_connection_new(
179 if (ndmp_connection_err_code(self->ndmp)) {
180 char *errmsg = ndmp_connection_err_msg(self->ndmp);
181 device_set_error(DEVICE(self),
182 g_strdup_printf("could not connect to ndmp-server '%s:%d': %s",
183 self->ndmp_hostname, self->ndmp_port, errmsg),
184 DEVICE_STATUS_DEVICE_ERROR);
185 g_object_unref(self->ndmp);
191 ndmp_connection_set_verbose(self->ndmp, TRUE);
193 self->tape_open = FALSE;
203 /* note that this does not send NDMP_TAPE_CLOSE, as it's used in error
206 g_object_unref(self->ndmp);
208 self->tape_open = FALSE;
216 guint64 file_num, blockno, blocksize;
218 /* if already open, stop now */
219 if (self->tape_open) {
223 if (!open_connection(self)) {
224 /* error message set by open_connection */
228 g_debug("opening tape device '%s' on NDMP server '%s:%d'",
229 self->ndmp_device_name, self->ndmp_hostname, self->ndmp_port);
231 /* send NDMP_TAPE_OPEN, using RAW mode so that it will open even with no tape */
232 if (!ndmp_connection_tape_open(self->ndmp,
233 self->ndmp_device_name, NDMP9_TAPE_RAW_MODE)) {
234 set_error_from_ndmp(self);
238 /* check that the block sizes match */
239 if (!ndmp_connection_tape_get_state(self->ndmp,
240 &blocksize, &file_num, &blockno)) {
241 set_error_from_ndmp(self);
244 if (blocksize != 0 && blocksize != DEVICE(self)->block_size) {
245 device_set_error(DEVICE(self),
246 g_strdup_printf("NDMP device has fixed block size %ju, but Amanda "
247 "device is configured with blocksize %ju", (uintmax_t)blocksize,
248 (uintmax_t)(DEVICE(self)->block_size)),
249 DEVICE_STATUS_DEVICE_ERROR);
252 self->tape_open = TRUE;
261 if (self->tape_open) {
262 g_debug("closing tape device '%s' on NDMP server '%s:%d'",
263 self->ndmp_device_name, self->ndmp_hostname, self->ndmp_port);
264 self->tape_open = FALSE; /* count it as closed even if there is an error */
265 if (!ndmp_connection_tape_close(self->ndmp)) {
266 set_error_from_ndmp(self);
277 ndmp9_tape_mtio_op tape_op)
281 if (!ndmp_connection_tape_mtio(self->ndmp, tape_op, 1, &resid)) {
282 set_error_from_ndmp(self);
287 device_set_error(DEVICE(self),
288 g_strdup_printf("NDMP MTIO operation %d did not complete", tape_op),
289 DEVICE_STATUS_DEVICE_ERROR);
295 /* get the tape state straight from the device; we try to track these things
296 * accurately in the device, but sometimes it's good to check. */
301 Device *dself = DEVICE(self);
302 guint64 file_num, blockno, blocksize;
304 if (!ndmp_connection_tape_get_state(self->ndmp,
305 &blocksize, &file_num, &blockno)) {
306 set_error_from_ndmp(self);
310 g_assert(file_num < INT_MAX);
311 dself->file = (int)file_num;
312 dself->block = blockno;
317 static robust_write_result
324 robust_write_result subresult;
326 if (!ndmp_connection_tape_write(self->ndmp, buf, count, &actual)) {
327 switch (ndmp_connection_err_code(self->ndmp)) {
329 /* We encountered PEOM; this only happens when the caller ignores
331 return ROBUST_WRITE_NO_SPACE;
334 /* We encountered LEOM; retry the write (which should succeed) */
335 subresult = robust_write(self, buf, count);
336 if (subresult != ROBUST_WRITE_OK)
338 g_debug("ndmp device hit logical EOM");
339 return ROBUST_WRITE_OK_LEOM;
342 set_error_from_ndmp(self);
343 return ROBUST_WRITE_ERROR;
347 g_assert(count == actual);
348 return ROBUST_WRITE_OK;
355 /* translate some error codes to the corresponding Device API status */
356 switch (ndmp_connection_err_code(self->ndmp)) {
357 case NDMP9_NO_TAPE_LOADED_ERR:
358 device_set_error(DEVICE(self),
359 g_strdup(_("no tape loaded")),
360 DEVICE_STATUS_VOLUME_MISSING);
363 case NDMP9_DEVICE_BUSY_ERR:
364 device_set_error(DEVICE(self),
365 g_strdup(_("device busy")),
366 DEVICE_STATUS_DEVICE_BUSY);
370 device_set_error(DEVICE(self),
371 g_strdup(_("IO error")),
372 DEVICE_STATUS_VOLUME_UNLABELED |
373 DEVICE_STATUS_VOLUME_ERROR |
374 DEVICE_STATUS_DEVICE_ERROR);
378 device_set_error(DEVICE(self),
379 ndmp_connection_err_msg(self->ndmp),
380 DEVICE_STATUS_DEVICE_ERROR);
383 close_connection(self);
387 * Virtual function overrides
391 ndmp_device_open_device(
397 NdmpDevice *self = NDMP_DEVICE(dself);
400 /* first, extract the various parts of the device_node:
401 * HOST[:PORT]@DEVICE */
402 colon = strchr(device_node, ':');
403 at = strchr(device_node, '@');
405 colon = NULL; /* :PORT only counts if it's before the device name */
407 device_set_error(dself,
408 g_strdup_printf("invalid ndmp device name '%s'", device_name),
409 DEVICE_STATUS_DEVICE_ERROR);
415 long port = strtol(colon+1, &p, 10);
417 if (port < 0 || port >= 65536 || p != at || (!port && EINVAL == errno)) {
418 device_set_error(dself,
419 g_strdup_printf("invalid ndmp port in device name '%s'",
421 DEVICE_STATUS_DEVICE_ERROR);
424 self->ndmp_port = (gint)port;
425 self->ndmp_hostname = g_strndup(device_node, colon-device_node);
427 self->ndmp_port = 0; /* (use ndmjob's default, 10000) */
428 self->ndmp_hostname = g_strndup(device_node, at-device_node);
430 self->ndmp_device_name = g_strdup(at+1);
432 if (parent_class->open_device) {
433 parent_class->open_device(dself, device_name, device_type, device_node);
437 static void ndmp_device_finalize(GObject * obj_self)
439 NdmpDevice *self = NDMP_DEVICE (obj_self);
441 if(G_OBJECT_CLASS(parent_class)->finalize)
442 (* G_OBJECT_CLASS(parent_class)->finalize)(obj_self);
444 (void)close_tape_agent(self); /* ignore any error */
446 if (self->directtcp_conn)
447 g_object_unref(self->directtcp_conn);
449 if (self->listen_addrs)
450 g_free(self->listen_addrs);
452 close_connection(self);
454 if (self->ndmp_hostname)
455 g_free(self->ndmp_hostname);
456 if (self->ndmp_device_name)
457 g_free(self->ndmp_device_name);
458 if (self->ndmp_username)
459 g_free(self->ndmp_username);
460 if (self->ndmp_password)
461 g_free(self->ndmp_password);
463 g_free(self->ndmp_auth);
464 if (self->indirecttcp_sock != -1)
465 close(self->indirecttcp_sock);
468 static DeviceStatusFlags
469 ndmp_device_read_label(
472 NdmpDevice *self = NDMP_DEVICE(dself);
473 dumpfile_t *header = NULL;
475 guint64 buf_size = 0;
476 gsize read_block_size = 0;
478 amfree(dself->volume_label);
479 amfree(dself->volume_time);
480 dumpfile_free(dself->volume_header);
481 dself->volume_header = NULL;
483 if (device_in_error(self)) return dself->status;
485 if (!open_tape_agent(self)) {
486 /* error status was set by open_tape_agent */
487 return dself->status;
490 if (!single_ndmp_mtio(self, NDMP9_MTIO_REW)) {
491 /* error message, if any, is set by single_ndmp_mtio */
492 return dself->status;
495 /* read the tape header from the NDMP server */
497 read_block_size = ndmp_device_read_size(self);
498 buf = g_malloc(read_block_size);
499 if (!ndmp_connection_tape_read(self->ndmp,
504 /* handle known errors */
505 switch (ndmp_connection_err_code(self->ndmp)) {
506 case NDMP9_NO_TAPE_LOADED_ERR:
507 device_set_error(dself,
508 g_strdup(_("no tape loaded")),
509 DEVICE_STATUS_VOLUME_MISSING);
513 device_set_error(dself,
514 g_strdup(_("IO error reading tape label")),
515 DEVICE_STATUS_VOLUME_UNLABELED |
516 DEVICE_STATUS_VOLUME_ERROR |
517 DEVICE_STATUS_DEVICE_ERROR);
522 device_set_error(dself,
523 g_strdup(_("no tape label found")),
524 DEVICE_STATUS_VOLUME_UNLABELED);
525 header = dself->volume_header = g_new(dumpfile_t, 1);
530 set_error_from_ndmp(self);
535 header = dself->volume_header = g_new(dumpfile_t, 1);
537 parse_file_header(buf, header, buf_size);
542 if (dself->status != 0) {
543 /* error already set above */
544 return dself->status;
547 /* handle a "weird" label */
548 if (header->type != F_TAPESTART) {
549 device_set_error(dself,
550 stralloc(_("No tapestart header -- unlabeled device?")),
551 DEVICE_STATUS_VOLUME_UNLABELED);
552 return dself->status;
554 dself->volume_label = g_strdup(header->name);
555 dself->volume_time = g_strdup(header->datestamp);
556 /* dself->volume_header is already set */
558 /* note: connection is left open, as well as the tape device */
560 device_set_error(dself, NULL, DEVICE_STATUS_SUCCESS);
562 return dself->status;
569 DeviceAccessMode mode,
573 NdmpDevice *self = NDMP_DEVICE(dself);
577 self = NDMP_DEVICE(dself);
579 if (device_in_error(self)) return FALSE;
581 if (!open_tape_agent(self)) {
582 /* error status was set by open_tape_agent */
586 if (mode != ACCESS_WRITE && dself->volume_label == NULL) {
587 if (ndmp_device_read_label(dself) != DEVICE_STATUS_SUCCESS)
588 /* the error was set by ndmp_device_read_label */
592 dself->access_mode = mode;
593 g_mutex_lock(dself->device_mutex);
594 dself->in_file = FALSE;
595 g_mutex_unlock(dself->device_mutex);
597 if (!single_ndmp_mtio(self, NDMP9_MTIO_REW)) {
598 /* single_ndmp_mtio already set our error message */
602 /* Position the tape */
605 device_set_error(dself,
606 g_strdup("operation not supported"),
607 DEVICE_STATUS_DEVICE_ERROR);
616 header = make_tapestart_header(dself, label, timestamp);
617 g_assert(header != NULL);
619 header_buf = device_build_amanda_header(dself, header, NULL);
620 if (header_buf == NULL) {
621 device_set_error(dself,
622 stralloc(_("Tapestart header won't fit in a single block!")),
623 DEVICE_STATUS_DEVICE_ERROR);
624 dumpfile_free(header);
628 switch (robust_write(self, header_buf, dself->block_size)) {
629 case ROBUST_WRITE_OK_LEOM:
630 dself->is_eom = TRUE;
632 case ROBUST_WRITE_OK:
635 case ROBUST_WRITE_NO_SPACE:
636 /* this would be an odd error to see writing the tape label, but
638 device_set_error(dself,
639 stralloc(_("No space left on device")),
640 DEVICE_STATUS_VOLUME_ERROR);
641 dself->is_eom = TRUE;
644 case ROBUST_WRITE_ERROR:
645 /* error was set by robust_write or above */
646 dumpfile_free(header);
653 if (!single_ndmp_mtio(self, NDMP9_MTIO_EOF)) {
654 /* error was set by single_ndmp_mtio */
655 dumpfile_free(header);
659 dself->volume_label = newstralloc(dself->volume_label, label);
660 dself->volume_time = newstralloc(dself->volume_time, timestamp);
661 dumpfile_free(dself->volume_header);
662 dself->volume_header = header;
664 /* unset the VOLUME_UNLABELED flag, if it was set */
665 device_set_error(dself, NULL, DEVICE_STATUS_SUCCESS);
670 g_assert_not_reached();
682 NdmpDevice *self = NDMP_DEVICE(dself);
683 rval = !device_in_error(dself);
685 /* we're not in a file anymore */
686 dself->access_mode = ACCESS_NULL;
688 if (!close_tape_agent(self)) {
689 /* error is set by close_tape_agent */
694 close_connection(self);
703 NdmpDevice *self = NDMP_DEVICE(dself);
704 if (device_in_error(dself)) return FALSE;
706 if (!single_ndmp_mtio(self, NDMP9_MTIO_OFF)) {
707 /* error was set by single_ndmp_mtio */
715 /* functions for writing */
718 ndmp_device_start_file(
722 NdmpDevice *self = NDMP_DEVICE(dself);
725 if (device_in_error(self)) return FALSE;
727 dself->is_eof = FALSE;
728 dself->is_eom = FALSE;
729 g_mutex_lock(dself->device_mutex);
730 dself->bytes_written = 0;
731 g_mutex_unlock(dself->device_mutex);
733 /* set the blocksize in the header properly */
734 header->blocksize = dself->block_size;
736 header_buf = device_build_amanda_header(dself, header, NULL);
737 if (header_buf == NULL) {
738 device_set_error(dself,
739 stralloc(_("Amanda file header won't fit in a single block!")),
740 DEVICE_STATUS_DEVICE_ERROR);
744 switch (robust_write(self, header_buf, dself->block_size)) {
745 case ROBUST_WRITE_OK_LEOM:
746 dself->is_eom = TRUE;
749 case ROBUST_WRITE_OK:
752 case ROBUST_WRITE_NO_SPACE:
753 /* this would be an odd error to see writing the tape label, but
755 device_set_error(dself,
756 stralloc(_("No space left on device")),
757 DEVICE_STATUS_VOLUME_ERROR);
758 dself->is_eom = TRUE;
761 case ROBUST_WRITE_ERROR:
762 /* error was set by robust_write or above */
768 /* arrange the file numbers correctly */
769 g_mutex_lock(dself->device_mutex);
770 dself->in_file = TRUE;
771 g_mutex_unlock(dself->device_mutex);
772 if (!ndmp_get_state(self)) {
773 /* error already set by ndmp_get_state */
777 /* double-check that the tape agent gave us a non-bogus file number */
778 g_assert(dself->file > 0);
784 ndmp_device_write_block(
789 NdmpDevice *self = NDMP_DEVICE(dself);
790 gpointer replacement_buffer = NULL;
792 if (device_in_error(self)) return FALSE;
794 /* zero out to the end of a short block -- tape devices only write
796 if (size < dself->block_size) {
797 replacement_buffer = malloc(dself->block_size);
798 memcpy(replacement_buffer, data, size);
799 bzero(replacement_buffer+size, dself->block_size-size);
801 data = replacement_buffer;
802 size = dself->block_size;
805 switch (robust_write(self, data, size)) {
806 case ROBUST_WRITE_OK_LEOM:
807 dself->is_eom = TRUE;
810 case ROBUST_WRITE_OK:
813 case ROBUST_WRITE_NO_SPACE:
814 /* this would be an odd error to see writing the tape label, but
816 device_set_error(dself,
817 stralloc(_("No space left on device")),
818 DEVICE_STATUS_VOLUME_ERROR);
819 dself->is_eom = TRUE;
822 case ROBUST_WRITE_ERROR:
823 /* error was set by robust_write or above */
824 if (replacement_buffer) g_free(replacement_buffer);
829 g_mutex_lock(dself->device_mutex);
830 dself->bytes_written += size;
831 g_mutex_unlock(dself->device_mutex);
833 if (replacement_buffer) g_free(replacement_buffer);
838 ndmp_device_finish_file(
841 NdmpDevice *self = NDMP_DEVICE(dself);
843 if (device_in_error(dself)) return FALSE;
845 /* we're not in a file anymore */
846 g_mutex_lock(dself->device_mutex);
847 dself->in_file = FALSE;
848 g_mutex_unlock(dself->device_mutex);
850 if (!single_ndmp_mtio(self, NDMP9_MTIO_EOF)) {
851 /* error was set by single_ndmp_mtio */
852 dself->is_eom = TRUE;
859 /* functions for reading */
862 ndmp_device_seek_file(
866 NdmpDevice *self = NDMP_DEVICE(dself);
872 gsize read_block_size = 0;
874 if (device_in_error(dself)) return FALSE;
876 /* file 0 is the tape header, and isn't seekable as a distinct
877 * Device-API-level file */
879 device_set_error(dself,
880 g_strdup("cannot seek to file 0"),
881 DEVICE_STATUS_DEVICE_ERROR);
885 /* first, make sure the file and block numbers are correct */
886 if (!ndmp_get_state(self)) {
887 /* error already set by ndmp_get_state */
891 /* now calculate the file delta */
892 delta = file - dself->file;
895 /* Note that this algorithm will rewind to the beginning of
896 * the current part, too */
898 /* BSF *past* the filemark we want to seek to */
899 if (!ndmp_connection_tape_mtio(self->ndmp, NDMP9_MTIO_BSF, -delta + 1, &resid)) {
900 set_error_from_ndmp(self);
906 /* now we are on the BOT side of the filemark, but we want to be
907 * on the EOT side of it. An FSF will get us there.. */
908 if (!ndmp_connection_tape_mtio(self->ndmp, NDMP9_MTIO_FSF, 1, &resid)) {
909 set_error_from_ndmp(self);
915 device_set_error(dself,
916 g_strdup_printf("BSF operation failed to seek by %d files", resid),
917 DEVICE_STATUS_DEVICE_ERROR);
920 } else /* (delta > 0) */ {
921 if (!ndmp_connection_tape_mtio(self->ndmp, NDMP9_MTIO_FSF, delta, &resid)) {
922 set_error_from_ndmp(self);
926 /* if we didn't seek all the way there, then we're past the tapeend */
928 device_set_error(dself,
929 vstrallocf(_("Could not seek forward to file %d"), file),
930 DEVICE_STATUS_VOLUME_ERROR);
936 g_mutex_lock(dself->device_mutex);
937 dself->in_file = TRUE;
938 g_mutex_unlock(dself->device_mutex);
941 g_mutex_lock(dself->device_mutex);
942 dself->bytes_read = 0;
943 g_mutex_unlock(dself->device_mutex);
945 /* now read the header */
946 read_block_size = ndmp_device_read_size(self);
947 buf = g_malloc(read_block_size);
948 if (!ndmp_connection_tape_read(self->ndmp,
949 buf, read_block_size, &buf_size)) {
950 switch (ndmp_connection_err_code(self->ndmp)) {
953 return make_tapeend_header();
956 set_error_from_ndmp(self);
962 header = g_new(dumpfile_t, 1);
964 parse_file_header(buf, header, buf_size);
971 ndmp_device_seek_block(
975 if (device_in_error(dself)) return FALSE;
977 dself->block = block;
979 device_set_error(dself, g_strdup("operation not supported"), DEVICE_STATUS_DEVICE_ERROR);
984 ndmp_device_read_block (Device * dself, gpointer data, int *size_req) {
985 NdmpDevice *self = NDMP_DEVICE(dself);
986 guint64 requested, actual;
987 gsize read_block_size = ndmp_device_read_size(self);
989 /* We checked the NDMP device's blocksize when the device was opened, which should
990 * catch any misalignent of server block size and Amanda block size */
992 g_assert(read_block_size < INT_MAX); /* check data type mismatch */
993 if (!data || *size_req < (int)(read_block_size)) {
994 *size_req = (int)(read_block_size);
998 requested = *size_req;
999 if (!ndmp_connection_tape_read(self->ndmp,
1004 /* handle known errors */
1005 switch (ndmp_connection_err_code(self->ndmp)) {
1008 dself->is_eof = TRUE;
1012 set_error_from_ndmp(self);
1017 *size_req = (int)actual; /* cast is OK - requested size was < INT_MAX too */
1018 g_mutex_lock(dself->device_mutex);
1019 dself->bytes_read += actual;
1020 g_mutex_unlock(dself->device_mutex);
1028 DirectTCPAddr **addrs)
1032 self->indirecttcp_sock = stream_server(AF_INET, &port, 0, STREAM_BUFSIZE, 0);
1033 if (self->indirecttcp_sock < 0) {
1034 device_set_error(DEVICE(self),
1035 g_strdup_printf("Could not bind indirecttcp socket: %s", strerror(errno)),
1036 DEVICE_STATUS_DEVICE_ERROR);
1040 /* An IndirectTCP address is 255.255.255.255:$port */
1041 self->listen_addrs = *addrs = g_new0(DirectTCPAddr, 2);
1042 addrs[0]->sin.sin_family = AF_INET;
1043 addrs[0]->sin.sin_addr.s_addr = htonl(0xffffffff);
1044 SU_SET_PORT(addrs[0], port);
1052 gboolean for_writing,
1053 DirectTCPAddr **addrs)
1055 NdmpDevice *self = NDMP_DEVICE(dself);
1057 if (device_in_error(self)) return FALSE;
1060 g_assert(!self->listen_addrs);
1062 if (!open_tape_agent(self)) {
1063 /* error message was set by open_tape_agent */
1067 self->for_writing = for_writing;
1069 /* first, set the window to an empty span so that the mover doesn't start
1070 * reading or writing data immediately. NDMJOB tends to reset the record
1071 * size periodically (in direct contradiction to the spec), so we reset it
1073 if (!ndmp_connection_mover_set_record_size(self->ndmp,
1074 DEVICE(self)->block_size)) {
1075 set_error_from_ndmp(self);
1080 /* if we're forcing indirecttcp, just do it */
1081 if (self->indirect) {
1082 return indirecttcp_listen(self, addrs);
1084 if (!ndmp_connection_mover_set_window(self->ndmp, 0, 0)) {
1085 /* NDMP4_ILLEGAL_ARGS_ERR means the NDMP server doesn't like a zero-byte
1086 * mover window, so we'll ignore it */
1087 if (ndmp_connection_err_code(self->ndmp) != NDMP4_ILLEGAL_ARGS_ERR) {
1088 set_error_from_ndmp(self);
1092 g_debug("NDMP Device: cannot set zero-length mover window; "
1093 "falling back to IndirectTCP");
1094 /* In this case, we need to set up IndirectTCP */
1095 return indirecttcp_listen(self, addrs);
1098 /* For reading, set the window to the second mover record, so that the
1099 * mover will pause immediately when it wants to read the first mover
1101 if (!ndmp_connection_mover_set_window(self->ndmp,
1103 dself->block_size)) {
1104 set_error_from_ndmp(self);
1109 /* then tell it to start listening */
1110 if (!ndmp_connection_mover_listen(self->ndmp,
1111 for_writing? NDMP9_MOVER_MODE_READ : NDMP9_MOVER_MODE_WRITE,
1114 set_error_from_ndmp(self);
1117 self->listen_addrs = *addrs;
1125 DirectTCPConnection **dtcpconn,
1126 ProlongProc prolong,
1127 gpointer prolong_data)
1129 NdmpDevice *self = NDMP_DEVICE(dself);
1130 ndmp9_mover_state state;
1131 guint64 bytes_moved;
1132 ndmp9_mover_mode mode;
1133 ndmp9_mover_pause_reason reason;
1134 guint64 seek_position;
1136 if (device_in_error(self)) return FALSE;
1138 g_assert(self->listen_addrs);
1142 /* TODO: support aborting this operation - maybe just always poll? */
1144 prolong_data = prolong_data;
1146 if (!self->for_writing) {
1147 /* when reading, we don't get any kind of notification that the
1148 * connection has been established, but we can't call NDMP_MOVER_READ
1149 * until the mover is active. So we have to poll, waiting for ACTIVE.
1151 gulong backoff = G_USEC_PER_SEC/20; /* 5 msec */
1153 if (!ndmp_connection_mover_get_state(self->ndmp,
1154 &state, &bytes_moved, NULL, NULL)) {
1155 set_error_from_ndmp(self);
1159 if (state != NDMP9_MOVER_STATE_LISTEN)
1162 /* back off a little bit to give the other side time to breathe,
1163 * but not more than one second */
1166 if (backoff > G_USEC_PER_SEC)
1167 backoff = G_USEC_PER_SEC;
1170 /* double-check state */
1171 if (state != NDMP9_MOVER_STATE_ACTIVE) {
1172 device_set_error(DEVICE(self),
1173 g_strdup("mover did not enter the ACTIVE state as expected"),
1174 DEVICE_STATUS_DEVICE_ERROR);
1178 /* now, we need to get this into the PAUSED state, since right now we
1179 * aren't allowed to perform any tape movement commands. So we issue a
1180 * MOVER_READ request for the whole darn image stream after setting the
1181 * usual empty window. Note that this means the whole dump will be read
1182 * in one MOVER_READ operation, even if it does not begin at the
1183 * beginning of a part. */
1184 if (!ndmp_connection_mover_read(self->ndmp, 0, G_MAXUINT64)) {
1185 set_error_from_ndmp(self);
1189 /* now we should expect a notice that the mover has paused */
1191 /* when writing, the mover will pause as soon as the first byte comes
1192 * in, so there's no need to do anything to trigger the pause.
1194 * Well, sometimes it won't - specifically, when it does not allow a
1195 * zero-byte mover window, which means we've set up IndirectTCP. But in
1196 * that case, there's nothing interesting to do here.*/
1199 if (self->indirecttcp_sock == -1) {
1200 /* NDMJOB sends NDMP9_MOVER_PAUSE_SEEK to indicate that it wants to write
1201 * outside the window, while the standard specifies .._EOW, instead. When
1202 * reading to a connection, we get the appropriate .._SEEK. It's easy
1203 * enough to handle both. */
1205 if (!ndmp_connection_wait_for_notify(self->ndmp,
1208 &reason, &seek_position)) {
1209 set_error_from_ndmp(self);
1213 if (reason != NDMP9_MOVER_PAUSE_SEEK && reason != NDMP9_MOVER_PAUSE_EOW) {
1214 device_set_error(DEVICE(self),
1215 g_strdup_printf("got NOTIFY_MOVER_PAUSED, but not because of EOW or SEEK"),
1216 DEVICE_STATUS_DEVICE_ERROR);
1221 /* at this point, if we're doing directtcp, the mover is paused and ready
1222 * to go, and the listen addrs are no longer required; if we're doing
1223 * indirecttcp, then the other end may not even know of our listen_addrs
1224 * yet, so we can't free them.
1227 if (self->indirecttcp_sock == -1) {
1228 g_free(self->listen_addrs);
1229 self->listen_addrs = NULL;
1232 if (self->for_writing)
1233 mode = NDMP9_MOVER_MODE_READ;
1235 mode = NDMP9_MOVER_MODE_WRITE;
1237 /* set up the new directtcp connection */
1238 if (self->directtcp_conn)
1239 g_object_unref(self->directtcp_conn);
1240 self->directtcp_conn =
1241 directtcp_connection_ndmp_new(self->ndmp, mode);
1242 *dtcpconn = DIRECTTCP_CONNECTION(self->directtcp_conn);
1244 /* reference it for the caller */
1245 g_object_ref(*dtcpconn);
1251 accept_with_cond_impl(
1253 DirectTCPConnection **dtcpconn,
1254 GMutex *abort_mutex,
1257 NdmpDevice *self = NDMP_DEVICE(dself);
1258 ndmp9_mover_state state;
1259 guint64 bytes_moved;
1260 ndmp9_mover_mode mode;
1261 ndmp9_mover_pause_reason reason;
1262 guint64 seek_position;
1265 if (device_in_error(self)) return 1;
1267 g_assert(self->listen_addrs);
1271 if (!self->for_writing) {
1272 /* when reading, we don't get any kind of notification that the
1273 * connection has been established, but we can't call NDMP_MOVER_READ
1274 * until the mover is active. So we have to poll, waiting for ACTIVE.
1276 gulong backoff = G_USEC_PER_SEC/20; /* 5 msec */
1278 if (!ndmp_connection_mover_get_state(self->ndmp,
1279 &state, &bytes_moved, NULL, NULL)) {
1280 set_error_from_ndmp(self);
1284 if (state != NDMP9_MOVER_STATE_LISTEN)
1287 /* back off a little bit to give the other side time to breathe,
1288 * but not more than one second */
1291 if (backoff > G_USEC_PER_SEC)
1292 backoff = G_USEC_PER_SEC;
1295 /* double-check state */
1296 if (state != NDMP9_MOVER_STATE_ACTIVE) {
1297 device_set_error(DEVICE(self),
1298 g_strdup("mover did not enter the ACTIVE state as expected"),
1299 DEVICE_STATUS_DEVICE_ERROR);
1303 /* now, we need to get this into the PAUSED state, since right now we
1304 * aren't allowed to perform any tape movement commands. So we issue a
1305 * MOVER_READ request for the whole darn image stream after setting the
1306 * usual empty window. Note that this means the whole dump will be read
1307 * in one MOVER_READ operation, even if it does not begin at the
1308 * beginning of a part. */
1309 if (!ndmp_connection_mover_read(self->ndmp, 0, G_MAXUINT64)) {
1310 set_error_from_ndmp(self);
1314 /* now we should expect a notice that the mover has paused */
1316 /* when writing, the mover will pause as soon as the first byte comes
1317 * in, so there's no need to do anything to trigger the pause. */
1320 if (self->indirecttcp_sock == -1) {
1321 /* NDMJOB sends NDMP9_MOVER_PAUSE_SEEK to indicate that it wants to
1322 * write outside the window, while the standard specifies .._EOW,
1323 * instead. When reading to a connection, we get the appropriate
1324 * .._SEEK. It's easy enough to handle both. */
1325 result = ndmp_connection_wait_for_notify_with_cond(self->ndmp,
1328 &reason, &seek_position,
1329 abort_mutex, abort_cond);
1332 set_error_from_ndmp(self);
1334 } else if (result == 2) {
1338 if (reason != NDMP9_MOVER_PAUSE_SEEK &&
1339 reason != NDMP9_MOVER_PAUSE_EOW) {
1340 device_set_error(DEVICE(self),
1342 "got NOTIFY_MOVER_PAUSED, but not because of EOW or SEEK"),
1343 DEVICE_STATUS_DEVICE_ERROR);
1348 /* NDMJOB sends NDMP9_MOVER_PAUSE_SEEK to indicate that it wants to write
1349 * outside the window, while the standard specifies .._EOW, instead. When
1350 * reading to a connection, we get the appropriate .._SEEK. It's easy
1351 * enough to handle both. */
1353 if (self->indirecttcp_sock == -1) {
1354 g_free(self->listen_addrs);
1355 self->listen_addrs = NULL;
1358 if (self->for_writing)
1359 mode = NDMP9_MOVER_MODE_READ;
1361 mode = NDMP9_MOVER_MODE_WRITE;
1363 /* set up the new directtcp connection */
1364 if (self->directtcp_conn)
1365 g_object_unref(self->directtcp_conn);
1366 self->directtcp_conn =
1367 directtcp_connection_ndmp_new(self->ndmp, mode);
1368 *dtcpconn = DIRECTTCP_CONNECTION(self->directtcp_conn);
1370 /* reference it for the caller */
1371 g_object_ref(*dtcpconn);
1379 gboolean for_writing,
1380 DirectTCPAddr *addrs,
1381 DirectTCPConnection **dtcpconn,
1382 ProlongProc prolong,
1383 gpointer prolong_data)
1385 NdmpDevice *self = NDMP_DEVICE(dself);
1386 ndmp9_mover_mode mode;
1387 ndmp9_mover_pause_reason reason;
1388 guint64 seek_position;
1390 g_assert(!self->listen_addrs);
1393 self->for_writing = for_writing;
1395 /* TODO: support aborting this operation - maybe just always poll? */
1397 prolong_data = prolong_data;
1399 if (!open_tape_agent(self)) {
1400 /* error message was set by open_tape_agent */
1404 /* first, set the window to an empty span so that the mover doesn't start
1405 * reading or writing data immediately. NDMJOB tends to reset the record
1406 * size periodically (in direct contradiction to the spec), so we reset it
1408 if (!ndmp_connection_mover_set_record_size(self->ndmp,
1409 DEVICE(self)->block_size)) {
1410 set_error_from_ndmp(self);
1414 if (!ndmp_connection_mover_set_window(self->ndmp, 0, 0)) {
1415 set_error_from_ndmp(self);
1419 if (self->for_writing)
1420 mode = NDMP9_MOVER_MODE_READ;
1422 mode = NDMP9_MOVER_MODE_WRITE;
1424 if (!ndmp_connection_mover_connect(self->ndmp, mode, addrs)) {
1425 set_error_from_ndmp(self);
1429 if (!self->for_writing) {
1430 /* The agent is in the ACTIVE state, and will remain so until we tell
1431 * it to do something else. The thing we want to is for it to start
1432 * reading data from the tape, which will immediately trigger an EOW or
1434 if (!ndmp_connection_mover_read(self->ndmp, 0, G_MAXUINT64)) {
1435 set_error_from_ndmp(self);
1439 /* now we should expect a notice that the mover has paused */
1441 /* when writing, the mover will pause as soon as the first byte comes
1442 * in, so there's no need to do anything to trigger the pause. */
1445 /* NDMJOB sends NDMP9_MOVER_PAUSE_SEEK to indicate that it wants to write
1446 * outside the window, while the standard specifies .._EOW, instead. When
1447 * reading to a connection, we get the appropriate .._SEEK. It's easy
1448 * enough to handle both. */
1450 if (!ndmp_connection_wait_for_notify(self->ndmp,
1453 &reason, &seek_position)) {
1454 set_error_from_ndmp(self);
1458 if (reason != NDMP9_MOVER_PAUSE_SEEK && reason != NDMP9_MOVER_PAUSE_EOW) {
1459 device_set_error(DEVICE(self),
1460 g_strdup_printf("got NOTIFY_MOVER_PAUSED, but not because of EOW or SEEK"),
1461 DEVICE_STATUS_DEVICE_ERROR);
1465 if (self->listen_addrs) {
1466 g_free(self->listen_addrs);
1467 self->listen_addrs = NULL;
1470 /* set up the new directtcp connection */
1471 if (self->directtcp_conn)
1472 g_object_unref(self->directtcp_conn);
1473 self->directtcp_conn =
1474 directtcp_connection_ndmp_new(self->ndmp, mode);
1475 *dtcpconn = DIRECTTCP_CONNECTION(self->directtcp_conn);
1477 /* reference it for the caller */
1478 g_object_ref(*dtcpconn);
1484 connect_with_cond_impl(
1486 gboolean for_writing,
1487 DirectTCPAddr *addrs,
1488 DirectTCPConnection **dtcpconn,
1489 GMutex *abort_mutex,
1492 NdmpDevice *self = NDMP_DEVICE(dself);
1493 ndmp9_mover_mode mode;
1494 ndmp9_mover_pause_reason reason;
1495 guint64 seek_position;
1498 g_assert(!self->listen_addrs);
1501 self->for_writing = for_writing;
1503 if (!open_tape_agent(self)) {
1504 /* error message was set by open_tape_agent */
1508 /* first, set the window to an empty span so that the mover doesn't start
1509 * reading or writing data immediately. NDMJOB tends to reset the record
1510 * size periodically (in direct contradiction to the spec), so we reset it
1512 if (!ndmp_connection_mover_set_record_size(self->ndmp,
1513 DEVICE(self)->block_size)) {
1514 set_error_from_ndmp(self);
1518 if (!ndmp_connection_mover_set_window(self->ndmp, 0, 0)) {
1519 set_error_from_ndmp(self);
1523 if (self->for_writing)
1524 mode = NDMP9_MOVER_MODE_READ;
1526 mode = NDMP9_MOVER_MODE_WRITE;
1528 if (!ndmp_connection_mover_connect(self->ndmp, mode, addrs)) {
1529 set_error_from_ndmp(self);
1533 if (!self->for_writing) {
1534 /* The agent is in the ACTIVE state, and will remain so until we tell
1535 * it to do something else. The thing we want to is for it to start
1536 * reading data from the tape, which will immediately trigger an EOW or
1538 if (!ndmp_connection_mover_read(self->ndmp, 0, G_MAXUINT64)) {
1539 set_error_from_ndmp(self);
1543 /* now we should expect a notice that the mover has paused */
1545 /* when writing, the mover will pause as soon as the first byte comes
1546 * in, so there's no need to do anything to trigger the pause. */
1549 /* NDMJOB sends NDMP9_MOVER_PAUSE_SEEK to indicate that it wants to write
1550 * outside the window, while the standard specifies .._EOW, instead. When
1551 * reading to a connection, we get the appropriate .._SEEK. It's easy
1552 * enough to handle both. */
1554 result = ndmp_connection_wait_for_notify_with_cond(self->ndmp,
1557 &reason, &seek_position,
1558 abort_mutex, abort_cond);
1561 set_error_from_ndmp(self);
1563 } else if (result == 2) {
1567 if (reason != NDMP9_MOVER_PAUSE_SEEK && reason != NDMP9_MOVER_PAUSE_EOW) {
1568 device_set_error(DEVICE(self),
1569 g_strdup_printf("got NOTIFY_MOVER_PAUSED, but not because of EOW or SEEK"),
1570 DEVICE_STATUS_DEVICE_ERROR);
1574 if (self->listen_addrs) {
1575 g_free(self->listen_addrs);
1576 self->listen_addrs = NULL;
1579 /* set up the new directtcp connection */
1580 if (self->directtcp_conn)
1581 g_object_unref(self->directtcp_conn);
1582 self->directtcp_conn =
1583 directtcp_connection_ndmp_new(self->ndmp, mode);
1584 *dtcpconn = DIRECTTCP_CONNECTION(self->directtcp_conn);
1586 /* reference it for the caller */
1587 g_object_ref(*dtcpconn);
1593 indirecttcp_start_writing(
1596 DirectTCPAddr *real_addrs, *iter;
1599 /* The current state is that the other end is trying to connect to
1600 * indirecttcp_sock. The mover remains IDLE, although its window is set
1601 * correctly for the part we are about to write. */
1603 g_debug("indirecttcp_start_writing, ready to accept");
1604 conn_sock = accept(self->indirecttcp_sock, NULL, NULL);
1605 if (conn_sock < 0) {
1606 device_set_error(DEVICE(self),
1607 g_strdup_printf("Could not accept indirecttcp socket: %s", strerror(errno)),
1608 DEVICE_STATUS_DEVICE_ERROR);
1611 g_debug("indirecttcp_start_writing, accepted");
1613 close(self->indirecttcp_sock);
1614 self->indirecttcp_sock = -1;
1616 /* tell mover to start listening */
1617 g_assert(self->for_writing);
1618 if (!ndmp_connection_mover_listen(self->ndmp,
1619 NDMP4_MOVER_MODE_READ,
1622 set_error_from_ndmp(self);
1626 /* format the addresses and send them down the socket */
1627 for (iter = real_addrs; iter && SU_GET_FAMILY(iter) != 0; iter++) {
1628 char inet[INET_ADDRSTRLEN];
1632 addr = inet_ntop(AF_INET, &iter->sin.sin_addr.s_addr, inet, 40);
1634 addrspec = g_strdup_printf("%s:%d%s", addr, SU_GET_PORT(iter),
1635 SU_GET_FAMILY(iter+1) !=0? " ":"");
1636 g_debug("indirecttcp_start_writing, send %s", addrspec);
1637 if (full_write(conn_sock, addrspec, strlen(addrspec)) < strlen(addrspec)) {
1638 device_set_error(DEVICE(self),
1639 g_strdup_printf("writing to indirecttcp socket: %s", strerror(errno)),
1640 DEVICE_STATUS_DEVICE_ERROR);
1645 /* close the socket for good. This ensures that the next call to
1646 * write_from_connection_impl will not go through the mover setup process.
1648 if (close(conn_sock) < 0) {
1649 device_set_error(DEVICE(self),
1650 g_strdup_printf("closing indirecttcp socket: %s", strerror(errno)),
1651 DEVICE_STATUS_DEVICE_ERROR);
1656 /* and free the listen_addrs, since we didn't free them in accept_impl */
1657 if (self->listen_addrs) {
1658 g_free(self->listen_addrs);
1659 self->listen_addrs = NULL;
1662 /* Now it's up to the remote end to connect to the mover and start sending
1663 * data. We won't get any notification when this happens, although we could
1664 * in principle poll for such a thing. */
1669 write_from_connection_impl(
1672 guint64 *actual_size)
1674 NdmpDevice *self = NDMP_DEVICE(dself);
1675 DirectTCPConnectionNDMP *nconn = self->directtcp_conn;
1676 gboolean eom = FALSE, eof = FALSE, eow = FALSE;
1677 ndmp9_mover_state mover_state;
1678 ndmp9_mover_halt_reason halt_reason;
1679 ndmp9_mover_pause_reason pause_reason;
1680 guint64 bytes_moved_before, bytes_moved_after;
1683 if (device_in_error(self)) return FALSE;
1685 g_debug("write_from_connection_impl");
1689 /* if this is false, then the caller did not use use_connection correctly */
1690 g_assert(self->directtcp_conn != NULL);
1691 g_assert(self->ndmp == nconn->ndmp);
1692 g_assert(nconn->mode == NDMP9_MOVER_MODE_READ);
1694 if (!ndmp_connection_mover_get_state(self->ndmp,
1695 &mover_state, &bytes_moved_before, NULL, NULL)) {
1696 set_error_from_ndmp(self);
1700 if (self->indirecttcp_sock != -1) {
1701 /* If we're doing IndirectTCP, then we've deferred the whole
1702 * mover_set_window mover_listen process.. until now.
1703 * So the mover should be IDLE.
1705 g_assert(mover_state == NDMP9_MOVER_STATE_IDLE);
1707 /* the mover had best be PAUSED right now */
1708 g_assert(mover_state == NDMP9_MOVER_STATE_PAUSED);
1711 /* we want to set the window regardless of whether this is directtcp or
1714 if (!ndmp_connection_mover_set_window(self->ndmp,
1716 size? size : G_MAXUINT64 - nconn->offset)) {
1717 set_error_from_ndmp(self);
1721 /* for DirectTCP, we just tell the mover to continue; IndirectTCP is more complicated. */
1722 if (self->indirecttcp_sock != -1) {
1723 if (!indirecttcp_start_writing(self)) {
1727 if (!ndmp_connection_mover_continue(self->ndmp)) {
1728 set_error_from_ndmp(self);
1733 /* now wait for the mover to pause itself again, or halt on EOF or an error */
1734 if (!ndmp_connection_wait_for_notify(self->ndmp,
1737 &pause_reason, NULL)) {
1738 set_error_from_ndmp(self);
1744 switch (pause_reason) {
1745 case NDMP9_MOVER_PAUSE_EOM:
1749 /* ndmjob sends .._SEEK when it should send .._EOW, so deal with
1750 * both equivalently */
1751 case NDMP9_MOVER_PAUSE_EOW:
1752 case NDMP9_MOVER_PAUSE_SEEK:
1757 err = "got NOTIFY_MOVER_PAUSED, but not because of EOW or SEEK";
1760 } else if (halt_reason) {
1761 switch (halt_reason) {
1762 case NDMP9_MOVER_HALT_CONNECT_CLOSED:
1767 case NDMP9_MOVER_HALT_ABORTED:
1768 /* case NDMP9_MOVER_HALT_MEDIA_ERROR: <-- not in ndmjob */
1769 case NDMP9_MOVER_HALT_INTERNAL_ERROR:
1770 case NDMP9_MOVER_HALT_CONNECT_ERROR:
1771 err = "unexpected NDMP_NOTIFY_MOVER_HALTED";
1777 device_set_error(DEVICE(self),
1778 g_strdup_printf("waiting for accept: %s", err),
1779 DEVICE_STATUS_DEVICE_ERROR);
1783 /* no error, so the mover stopped due to one of EOM (volume out of space),
1784 * EOF (data connection is done), or EOW (maximum part size was written).
1785 * In any case, we want to know how many bytes were written. */
1787 if (!ndmp_connection_mover_get_state(self->ndmp,
1788 &mover_state, &bytes_moved_after, NULL, NULL)) {
1789 set_error_from_ndmp(self);
1792 size = bytes_moved_after - bytes_moved_before;
1793 nconn->offset += size;
1796 *actual_size = bytes_moved_after - bytes_moved_before;
1800 ; /* mover finished the whole part -- nothing to report! */
1802 DEVICE(self)->is_eof = TRUE;
1804 /* this is a *lossless* EOM, so no need to set error, but
1805 * we do need to figure out the actual size */
1806 DEVICE(self)->is_eom = TRUE;
1808 error("not reached");
1815 read_to_connection_impl(
1818 guint64 *actual_size)
1820 NdmpDevice *self = NDMP_DEVICE(dself);
1821 DirectTCPConnectionNDMP *nconn = self->directtcp_conn;
1822 gboolean eom = FALSE, eof = FALSE, eow = FALSE;
1823 ndmp9_mover_state mover_state;
1824 ndmp9_mover_halt_reason halt_reason;
1825 ndmp9_mover_pause_reason pause_reason;
1826 guint64 bytes_moved_before, bytes_moved_after;
1832 if (device_in_error(self)) return FALSE;
1834 /* read_to_connection does not support IndirectTCP */
1835 g_assert(self->indirecttcp_sock == -1);
1837 /* if this is false, then the caller did not use use_connection correctly */
1838 g_assert(nconn != NULL);
1839 g_assert(self->ndmp == nconn->ndmp);
1840 g_assert(nconn->mode == NDMP9_MOVER_MODE_WRITE);
1842 if (!ndmp_connection_mover_get_state(self->ndmp,
1843 &mover_state, &bytes_moved_before, NULL, NULL)) {
1844 set_error_from_ndmp(self);
1848 /* the mover had best be PAUSED right now */
1849 g_assert(mover_state == NDMP9_MOVER_STATE_PAUSED);
1851 if (!ndmp_connection_mover_set_window(self->ndmp,
1853 size? size : G_MAXUINT64 - nconn->offset)) {
1854 set_error_from_ndmp(self);
1858 if (!ndmp_connection_mover_continue(self->ndmp)) {
1859 set_error_from_ndmp(self);
1863 /* now wait for the mover to pause itself again, or halt on EOF or an error */
1864 if (!ndmp_connection_wait_for_notify(self->ndmp,
1867 &pause_reason, NULL)) {
1868 set_error_from_ndmp(self);
1874 switch (pause_reason) {
1875 case NDMP9_MOVER_PAUSE_EOF:
1879 /* ndmjob sends .._SEEK when it should send .._EOW, so deal with
1880 * both equivalently */
1881 case NDMP9_MOVER_PAUSE_EOW:
1882 case NDMP9_MOVER_PAUSE_SEEK:
1887 err = "got NOTIFY_MOVER_PAUSED, but not because of EOW or SEEK";
1890 } else if (halt_reason) {
1891 switch (halt_reason) {
1892 case NDMP9_MOVER_HALT_CONNECT_CLOSED:
1897 case NDMP9_MOVER_HALT_ABORTED:
1898 /* case NDMP9_MOVER_HALT_MEDIA_ERROR: <-- not in ndmjob */
1899 case NDMP9_MOVER_HALT_INTERNAL_ERROR:
1900 case NDMP9_MOVER_HALT_CONNECT_ERROR:
1901 err = "unexpected NDMP_NOTIFY_MOVER_HALTED";
1907 device_set_error(DEVICE(self),
1908 g_strdup_printf("waiting for accept: %s", err),
1909 DEVICE_STATUS_DEVICE_ERROR);
1913 /* no error, so the mover stopped due to one of EOM (volume out of space),
1914 * EOF (data connection is done), or EOW (maximum part size was written).
1915 * In any case, we want to know how many bytes were written. */
1917 if (!ndmp_connection_mover_get_state(self->ndmp,
1918 &mover_state, &bytes_moved_after, NULL, NULL)) {
1919 set_error_from_ndmp(self);
1922 size = bytes_moved_after - bytes_moved_before;
1923 nconn->offset += size;
1926 *actual_size = bytes_moved_after - bytes_moved_before;
1930 ; /* mover finished the whole part -- nothing to report! */
1932 DEVICE(self)->is_eof = TRUE;
1934 /* this is a *lossless* EOM, so no need to set error, but
1935 * we do need to figure out the actual size */
1936 DEVICE(self)->is_eom = TRUE;
1938 error("not reached");
1945 use_connection_impl(
1947 DirectTCPConnection *conn)
1949 NdmpDevice *self = NDMP_DEVICE(dself);
1950 DirectTCPConnectionNDMP *nconn;
1952 /* the device_use_connection_impl wrapper already made sure we're in
1953 * ACCESS_NULL, but we may have opened the tape service already to read
1954 * a label - so close it to be sure */
1955 if (!close_tape_agent(self)) {
1956 /* error was already set by close_tape_agent */
1960 /* we had best not be listening when this is called */
1961 g_assert(!self->listen_addrs);
1963 if (!IS_DIRECTTCP_CONNECTION_NDMP(conn)) {
1964 device_set_error(DEVICE(self),
1965 g_strdup("existing DirectTCPConnection is not compatible with this device"),
1966 DEVICE_STATUS_DEVICE_ERROR);
1970 if (self->directtcp_conn)
1971 g_object_unref(self->directtcp_conn);
1972 self->directtcp_conn = nconn = DIRECTTCP_CONNECTION_NDMP(conn);
1973 g_object_ref(self->directtcp_conn);
1975 /* if this is a different connection, use it */
1976 if (nconn->ndmp != self->ndmp) {
1978 close_connection(self);
1979 self->ndmp = nconn->ndmp;
1980 g_object_ref(self->ndmp);
1991 ndmp_device_set_username_fn(Device *dself,
1992 DevicePropertyBase *base, GValue *val,
1993 PropertySurety surety, PropertySource source)
1995 NdmpDevice *self = NDMP_DEVICE(dself);
1997 amfree(self->ndmp_username);
1998 self->ndmp_username = g_value_dup_string(val);
1999 device_clear_volume_details(dself);
2001 return device_simple_property_set_fn(dself, base, val, surety, source);
2005 ndmp_device_set_password_fn(Device *dself,
2006 DevicePropertyBase *base, GValue *val,
2007 PropertySurety surety, PropertySource source)
2009 NdmpDevice *self = NDMP_DEVICE(dself);
2011 amfree(self->ndmp_password);
2012 self->ndmp_password = g_value_dup_string(val);
2013 device_clear_volume_details(dself);
2015 return device_simple_property_set_fn(dself, base, val, surety, source);
2019 ndmp_device_set_auth_fn(Device *dself,
2020 DevicePropertyBase *base, GValue *val,
2021 PropertySurety surety, PropertySource source)
2023 NdmpDevice *self = NDMP_DEVICE(dself);
2025 amfree(self->ndmp_auth);
2026 self->ndmp_auth = g_value_dup_string(val);
2027 device_clear_volume_details(dself);
2029 return device_simple_property_set_fn(dself, base, val, surety, source);
2033 ndmp_device_set_verbose_fn(Device *p_self, DevicePropertyBase *base,
2034 GValue *val, PropertySurety surety, PropertySource source)
2036 NdmpDevice *self = NDMP_DEVICE(p_self);
2038 self->verbose = g_value_get_boolean(val);
2040 /* if the connection is active, set up verbose logging or turn it off */
2042 ndmp_connection_set_verbose(self->ndmp, self->verbose);
2046 return device_simple_property_set_fn(p_self, base, val, surety, source);
2050 ndmp_device_set_read_block_size_fn(Device *p_self, DevicePropertyBase *base G_GNUC_UNUSED,
2051 GValue *val, PropertySurety surety, PropertySource source)
2053 NdmpDevice *self = NDMP_DEVICE(p_self);
2054 gsize read_block_size = g_value_get_uint(val);
2056 if (read_block_size != 0 &&
2057 ((gsize)read_block_size < p_self->block_size ||
2058 (gsize)read_block_size > p_self->max_block_size)) {
2059 device_set_error(p_self,
2060 g_strdup_printf("Error setting READ-BLOCk-SIZE property to '%zu', it must be between %zu and %zu", read_block_size, p_self->block_size, p_self->max_block_size),
2061 DEVICE_STATUS_DEVICE_ERROR);
2065 self->read_block_size = read_block_size;
2067 /* use the READ_BLOCK_SIZE, even if we're invoked to get the old READ_BUFFER_SIZE */
2068 return device_simple_property_set_fn(p_self, base,
2069 val, surety, source);
2073 ndmp_device_set_indirect_fn(Device *dself,
2074 DevicePropertyBase *base, GValue *val,
2075 PropertySurety surety, PropertySource source)
2077 NdmpDevice *self = NDMP_DEVICE(dself);
2079 self->indirect = g_value_get_boolean(val);
2081 return device_simple_property_set_fn(dself, base, val, surety, source);
2085 ndmp_device_class_init(NdmpDeviceClass * c G_GNUC_UNUSED)
2087 GObjectClass *g_object_class = (GObjectClass*) c;
2088 DeviceClass *device_class = (DeviceClass *)c;
2090 parent_class = g_type_class_ref (TYPE_DEVICE);
2092 device_class->open_device = ndmp_device_open_device;
2093 device_class->read_label = ndmp_device_read_label;
2094 device_class->start = ndmp_device_start;
2095 device_class->finish = ndmp_device_finish;
2096 device_class->eject = ndmp_device_eject;
2098 device_class->start_file = ndmp_device_start_file;
2099 device_class->write_block = ndmp_device_write_block;
2100 device_class->finish_file = ndmp_device_finish_file;
2102 device_class->seek_file = ndmp_device_seek_file;
2103 device_class->seek_block = ndmp_device_seek_block;
2104 device_class->read_block = ndmp_device_read_block;
2106 device_class->directtcp_supported = TRUE;
2107 device_class->listen = listen_impl;
2108 device_class->accept = accept_impl;
2109 device_class->accept_with_cond = accept_with_cond_impl;
2110 device_class->connect = connect_impl;
2111 device_class->connect_with_cond = connect_with_cond_impl;
2112 device_class->write_from_connection = write_from_connection_impl;
2113 device_class->read_to_connection = read_to_connection_impl;
2114 device_class->use_connection = use_connection_impl;
2116 g_object_class->finalize = ndmp_device_finalize;
2118 device_class_register_property(device_class, PROPERTY_NDMP_USERNAME,
2119 PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
2120 device_simple_property_get_fn,
2121 ndmp_device_set_username_fn);
2123 device_class_register_property(device_class, PROPERTY_NDMP_PASSWORD,
2124 PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
2125 device_simple_property_get_fn,
2126 ndmp_device_set_password_fn);
2128 device_class_register_property(device_class, PROPERTY_NDMP_AUTH,
2129 PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
2130 device_simple_property_get_fn,
2131 ndmp_device_set_auth_fn);
2133 device_class_register_property(device_class, PROPERTY_VERBOSE,
2134 PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_MASK,
2135 device_simple_property_get_fn,
2136 ndmp_device_set_verbose_fn);
2138 device_class_register_property(device_class, PROPERTY_INDIRECT,
2139 PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_MASK,
2140 device_simple_property_get_fn,
2141 ndmp_device_set_indirect_fn);
2143 device_class_register_property(device_class, PROPERTY_READ_BLOCK_SIZE,
2144 PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
2145 device_simple_property_get_fn,
2146 ndmp_device_set_read_block_size_fn);
2150 ndmp_device_init(NdmpDevice *self)
2152 Device *dself = DEVICE(self);
2155 /* begin unconnected */
2158 /* decent defaults */
2159 dself->block_size = 32768;
2160 dself->min_block_size = 32768;
2161 dself->max_block_size = SIZE_MAX;
2163 bzero(&response, sizeof(response));
2165 g_value_init(&response, CONCURRENCY_PARADIGM_TYPE);
2166 g_value_set_enum(&response, CONCURRENCY_PARADIGM_EXCLUSIVE);
2167 device_set_simple_property(dself, PROPERTY_CONCURRENCY,
2168 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
2169 g_value_unset(&response);
2171 g_value_init(&response, STREAMING_REQUIREMENT_TYPE);
2172 g_value_set_enum(&response, STREAMING_REQUIREMENT_DESIRED);
2173 device_set_simple_property(dself, PROPERTY_STREAMING,
2174 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
2175 g_value_unset(&response);
2177 g_value_init(&response, G_TYPE_BOOLEAN);
2178 g_value_set_boolean(&response, FALSE);
2179 device_set_simple_property(dself, PROPERTY_APPENDABLE,
2180 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
2181 g_value_unset(&response);
2183 g_value_init(&response, G_TYPE_BOOLEAN);
2184 g_value_set_boolean(&response, FALSE);
2185 device_set_simple_property(dself, PROPERTY_PARTIAL_DELETION,
2186 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
2187 g_value_unset(&response);
2189 g_value_init(&response, G_TYPE_BOOLEAN);
2190 g_value_set_boolean(&response, FALSE);
2191 device_set_simple_property(dself, PROPERTY_FULL_DELETION,
2192 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
2193 g_value_unset(&response);
2195 g_value_init(&response, G_TYPE_BOOLEAN);
2196 g_value_set_boolean(&response, TRUE);
2197 device_set_simple_property(dself, PROPERTY_LEOM,
2198 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
2199 g_value_unset(&response);
2201 g_value_init(&response, MEDIA_ACCESS_MODE_TYPE);
2202 g_value_set_enum(&response, MEDIA_ACCESS_MODE_READ_WRITE);
2203 device_set_simple_property(dself, PROPERTY_MEDIUM_ACCESS_TYPE,
2204 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
2205 g_value_unset(&response);
2207 self->read_block_size = 0;
2208 g_value_init(&response, G_TYPE_UINT);
2209 g_value_set_uint(&response, self->read_block_size);
2210 device_set_simple_property(dself, PROPERTY_READ_BLOCK_SIZE,
2211 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DEFAULT);
2212 g_value_unset(&response);
2214 g_value_init(&response, G_TYPE_STRING);
2215 g_value_set_string(&response, "ndmp");
2216 device_set_simple_property(dself, PROPERTY_NDMP_USERNAME,
2217 &response, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
2218 g_value_unset(&response);
2219 self->ndmp_username = g_strdup("ndmp");
2221 g_value_init(&response, G_TYPE_STRING);
2222 g_value_set_string(&response, "ndmp");
2223 device_set_simple_property(dself, PROPERTY_NDMP_PASSWORD,
2224 &response, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
2225 g_value_unset(&response);
2226 self->ndmp_password = g_strdup("ndmp");
2228 g_value_init(&response, G_TYPE_STRING);
2229 g_value_set_string(&response, "md5");
2230 device_set_simple_property(dself, PROPERTY_NDMP_AUTH,
2231 &response, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
2232 g_value_unset(&response);
2233 self->ndmp_auth = g_strdup("md5");
2235 g_value_init(&response, G_TYPE_BOOLEAN);
2236 g_value_set_boolean(&response, FALSE);
2237 device_set_simple_property(dself, PROPERTY_INDIRECT,
2238 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DEFAULT);
2239 g_value_unset(&response);
2240 self->indirect = FALSE;
2242 self->indirecttcp_sock = -1;
2246 ndmp_device_get_type(void)
2248 static GType type = 0;
2250 if G_UNLIKELY(type == 0) {
2251 static const GTypeInfo info = {
2252 sizeof (NdmpDeviceClass),
2253 (GBaseInitFunc) NULL,
2254 (GBaseFinalizeFunc) NULL,
2255 (GClassInitFunc) ndmp_device_class_init,
2256 (GClassFinalizeFunc) NULL,
2257 NULL /* class_data */,
2258 sizeof (NdmpDevice),
2259 0 /* n_preallocs */,
2260 (GInstanceInitFunc) ndmp_device_init,
2264 type = g_type_register_static (TYPE_DEVICE, "NdmpDevice", &info,
2272 ndmp_device_factory(
2278 g_assert(0 == strcmp(device_type, NDMP_DEVICE_NAME));
2279 rval = DEVICE(g_object_new(TYPE_NDMP_DEVICE, NULL));
2281 device_open_device(rval, device_name, device_type, device_node);
2286 ndmp_device_register(void)
2288 static const char * device_prefix_list[] = { NDMP_DEVICE_NAME, NULL };
2290 /* register the device itself */
2291 register_device(ndmp_device_factory, device_prefix_list);
2293 device_property_fill_and_register(&device_property_ndmp_username,
2294 G_TYPE_STRING, "ndmp_username",
2295 "Username for access to the NDMP agent");
2296 device_property_fill_and_register(&device_property_ndmp_password,
2297 G_TYPE_STRING, "ndmp_password",
2298 "Password for access to the NDMP agent");
2299 device_property_fill_and_register(&device_property_ndmp_auth,
2300 G_TYPE_STRING, "ndmp_auth",
2301 "Authentication method for the NDMP agent - md5 (default), text, none, or void");
2302 device_property_fill_and_register(&device_property_indirect,
2303 G_TYPE_BOOLEAN, "indirect",
2304 "Use Indirect TCP mode, even if the NDMP server supports "
2309 * DirectTCPConnectionNDMP implementation
2313 directtcp_connection_ndmp_close(DirectTCPConnection *dself)
2315 DirectTCPConnectionNDMP *self = DIRECTTCP_CONNECTION_NDMP(dself);
2317 ndmp9_mover_state state;
2318 guint64 bytes_moved;
2319 ndmp9_mover_halt_reason reason;
2320 gboolean expect_notif = FALSE;
2322 /* based on the current state, we may need to abort or stop the
2323 * mover before closing it */
2324 if (!ndmp_connection_mover_get_state(self->ndmp, &state,
2325 &bytes_moved, NULL, NULL)) {
2326 rv = ndmp_connection_err_msg(self->ndmp);
2331 case NDMP9_MOVER_STATE_HALTED:
2332 break; /* nothing to do but ndmp_mover_close, below */
2334 case NDMP9_MOVER_STATE_PAUSED:
2335 if (!ndmp_connection_mover_close(self->ndmp)) {
2336 rv = ndmp_connection_err_msg(self->ndmp);
2339 expect_notif = TRUE;
2342 case NDMP9_MOVER_STATE_ACTIVE:
2344 if (!ndmp_connection_mover_abort(self->ndmp)) {
2345 rv = ndmp_connection_err_msg(self->ndmp);
2348 expect_notif = TRUE;
2352 /* the spec isn't entirely clear that mover_close and mover_abort should
2353 * generate a NOTIF_MOVER_HALTED, but ndmjob does it */
2355 if (!ndmp_connection_wait_for_notify(self->ndmp,
2357 &reason, /* value is ignored.. */
2363 if (!ndmp_connection_mover_stop(self->ndmp)) {
2364 rv = ndmp_connection_err_msg(self->ndmp);
2370 g_object_unref(self->ndmp);
2378 directtcp_connection_ndmp_class_init(DirectTCPConnectionNDMPClass * c)
2380 DirectTCPConnectionClass *connc = (DirectTCPConnectionClass *)c;
2382 connc->close = directtcp_connection_ndmp_close;
2386 directtcp_connection_ndmp_get_type (void)
2388 static GType type = 0;
2390 if G_UNLIKELY(type == 0) {
2391 static const GTypeInfo info = {
2392 sizeof (DirectTCPConnectionNDMPClass),
2393 (GBaseInitFunc) NULL,
2394 (GBaseFinalizeFunc) NULL,
2395 (GClassInitFunc) directtcp_connection_ndmp_class_init,
2396 (GClassFinalizeFunc) NULL,
2397 NULL /* class_data */,
2398 sizeof (DirectTCPConnectionNDMP),
2399 0 /* n_preallocs */,
2400 (GInstanceInitFunc) NULL,
2404 type = g_type_register_static(TYPE_DIRECTTCP_CONNECTION,
2405 "DirectTCPConnectionNDMP", &info, (GTypeFlags)0);
2411 static DirectTCPConnectionNDMP *
2412 directtcp_connection_ndmp_new(
2413 NDMPConnection *ndmp,
2414 ndmp9_mover_mode mode)
2416 DirectTCPConnectionNDMP *dcn = DIRECTTCP_CONNECTION_NDMP(
2417 g_object_new(TYPE_DIRECTTCP_CONNECTION_NDMP, NULL));
2419 /* hang onto a copy of this NDMP connection */