2 * Copyright (c) 2009, 2010 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"
30 * Type checking and casting macros
32 #define TYPE_NDMP_DEVICE (ndmp_device_get_type())
33 #define NDMP_DEVICE(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), ndmp_device_get_type(), NdmpDevice)
34 #define NDMP_DEVICE_CONST(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), ndmp_device_get_type(), NdmpDevice const)
35 #define NDMP_DEVICE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST((klass), ndmp_device_get_type(), NdmpDeviceClass)
36 #define IS_NDMP_DEVICE(obj) G_TYPE_CHECK_INSTANCE_TYPE((obj), ndmp_device_get_type ())
37 #define NDMP_DEVICE_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS((obj), ndmp_device_get_type(), NdmpDeviceClass)
38 static GType ndmp_device_get_type (void);
41 * Main object structure
44 typedef struct NdmpDevice_ NdmpDevice;
50 /* true if tape service is open on the NDMP server */
53 /* addresses the object is listening on, and how the connection
55 DirectTCPAddr *listen_addrs;
58 /* support for IndirectTCP */
59 int indirecttcp_sock; /* -1 if not in use */
60 int force_indirecttcp;
62 /* Current DirectTCPConnectionNDMP */
63 struct DirectTCPConnectionNDMP_ *directtcp_conn;
65 /* constructor parameters and properties */
68 gchar *ndmp_device_name;
79 typedef struct NdmpDeviceClass_ NdmpDeviceClass;
80 struct NdmpDeviceClass_ {
81 DeviceClass __parent__;
85 * A directtcp connection subclass representing a running mover on the other end of
86 * the given NDMP connection
89 #define TYPE_DIRECTTCP_CONNECTION_NDMP (directtcp_connection_ndmp_get_type())
90 #define DIRECTTCP_CONNECTION_NDMP(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), directtcp_connection_ndmp_get_type(), DirectTCPConnectionNDMP)
91 #define DIRECTTCP_CONNECTION_NDMP_CONST(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), directtcp_connection_ndmp_get_type(), DirectTCPConnectionNDMP const)
92 #define DIRECTTCP_CONNECTION_NDMP_CLASS(klass) G_TYPE_CHECK_CLASS_CAST((klass), directtcp_connection_ndmp_get_type(), DirectTCPConnectionNDMPClass)
93 #define IS_DIRECTTCP_CONNECTION_NDMP(obj) G_TYPE_CHECK_INSTANCE_TYPE((obj), directtcp_connection_ndmp_get_type ())
94 #define DIRECTTCP_CONNECTION_NDMP_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS((obj), directtcp_connection_ndmp_get_type(), DirectTCPConnectionNDMPClass)
95 GType directtcp_connection_ndmp_get_type(void);
97 typedef struct DirectTCPConnectionNDMP_ {
98 DirectTCPConnection __parent__;
100 /* NDMP connection controlling the mover */
101 NDMPConnection *ndmp;
103 /* mode for this operation */
104 ndmp9_mover_mode mode;
106 /* last reported mover position in the datastream */
108 } DirectTCPConnectionNDMP;
110 typedef struct DirectTCPConnectionNDMPClass_ {
111 DirectTCPConnectionClass __parent__;
112 } DirectTCPConnectionNDMPClass;
114 static DirectTCPConnectionNDMP *directtcp_connection_ndmp_new(
115 NDMPConnection *ndmp,
116 ndmp9_mover_mode mode);
119 * Constants and static data
122 #define NDMP_DEVICE_NAME "ndmp"
124 /* pointer to the class of our parent */
125 static DeviceClass *parent_class = NULL;
127 /* robust_write results */
130 ROBUST_WRITE_OK_LEOM,
131 ROBUST_WRITE_ERROR, /* device error already set */
132 ROBUST_WRITE_NO_SPACE
133 } robust_write_result;
136 * device-specific properties
139 /* Authentication information for NDMP agent. Both of these are strings. */
140 static DevicePropertyBase device_property_ndmp_username;
141 static DevicePropertyBase device_property_ndmp_password;
142 static DevicePropertyBase device_property_ndmp_auth;
143 static DevicePropertyBase device_property__force_indirecttcp;
144 #define PROPERTY_NDMP_USERNAME (device_property_ndmp_username.ID)
145 #define PROPERTY_NDMP_PASSWORD (device_property_ndmp_password.ID)
146 #define PROPERTY_NDMP_AUTH (device_property_ndmp_auth.ID)
147 #define PROPERTY__FORCE_INDIRECTTCP (device_property__force_indirecttcp.ID)
153 void ndmp_device_register(void);
154 static void set_error_from_ndmp(NdmpDevice *self);
165 self->ndmp = ndmp_connection_new(
172 if (ndmp_connection_err_code(self->ndmp)) {
173 char *errmsg = ndmp_connection_err_msg(self->ndmp);
174 device_set_error(DEVICE(self),
175 g_strdup_printf("could not connect to ndmp-server '%s:%d': %s",
176 self->ndmp_hostname, self->ndmp_port, errmsg),
177 DEVICE_STATUS_DEVICE_ERROR);
178 g_object_unref(self->ndmp);
184 ndmp_connection_set_verbose(self->ndmp, TRUE);
186 self->tape_open = FALSE;
196 /* note that this does not send NDMP_TAPE_CLOSE, as it's used in error
199 g_object_unref(self->ndmp);
201 self->tape_open = FALSE;
209 guint64 file_num, blockno, blocksize;
211 /* if already open, stop now */
212 if (self->tape_open) {
216 if (!open_connection(self)) {
217 /* error message set by open_connection */
221 g_debug("opening tape device '%s' on NDMP server '%s:%d'",
222 self->ndmp_device_name, self->ndmp_hostname, self->ndmp_port);
224 /* send NDMP_TAPE_OPEN, using RAW mode so that it will open even with no tape */
225 if (!ndmp_connection_tape_open(self->ndmp,
226 self->ndmp_device_name, NDMP9_TAPE_RAW_MODE)) {
227 set_error_from_ndmp(self);
231 /* check that the block sizes match */
232 if (!ndmp_connection_tape_get_state(self->ndmp,
233 &blocksize, &file_num, &blockno)) {
234 set_error_from_ndmp(self);
237 if (blocksize != 0 && blocksize != DEVICE(self)->block_size) {
238 device_set_error(DEVICE(self),
239 g_strdup_printf("NDMP device has fixed block size %ju, but Amanda "
240 "device is configured with blocksize %ju", (uintmax_t)blocksize,
241 (uintmax_t)(DEVICE(self)->block_size)),
242 DEVICE_STATUS_DEVICE_ERROR);
245 self->tape_open = TRUE;
254 if (self->tape_open) {
255 g_debug("closing tape device '%s' on NDMP server '%s:%d'",
256 self->ndmp_device_name, self->ndmp_hostname, self->ndmp_port);
257 self->tape_open = FALSE; /* count it as closed even if there is an error */
258 if (!ndmp_connection_tape_close(self->ndmp)) {
259 set_error_from_ndmp(self);
270 ndmp9_tape_mtio_op tape_op)
274 if (!ndmp_connection_tape_mtio(self->ndmp, tape_op, 1, &resid)) {
275 set_error_from_ndmp(self);
280 device_set_error(DEVICE(self),
281 g_strdup_printf("NDMP MTIO operation %d did not complete", tape_op),
282 DEVICE_STATUS_DEVICE_ERROR);
288 /* get the tape state straight from the device; we try to track these things
289 * accurately in the device, but sometimes it's good to check. */
294 Device *dself = DEVICE(self);
295 guint64 file_num, blockno, blocksize;
297 if (!ndmp_connection_tape_get_state(self->ndmp,
298 &blocksize, &file_num, &blockno)) {
299 set_error_from_ndmp(self);
303 g_assert(file_num < INT_MAX);
304 dself->file = (int)file_num;
305 dself->block = blockno;
310 static robust_write_result
317 robust_write_result subresult;
319 if (!ndmp_connection_tape_write(self->ndmp, buf, count, &actual)) {
320 switch (ndmp_connection_err_code(self->ndmp)) {
322 /* We encountered PEOM; this only happens when the caller ignores
324 return ROBUST_WRITE_NO_SPACE;
327 /* We encountered LEOM; retry the write (which should succeed) */
328 subresult = robust_write(self, buf, count);
329 if (subresult != ROBUST_WRITE_OK)
331 g_debug("ndmp device hit logical EOM");
332 return ROBUST_WRITE_OK_LEOM;
335 set_error_from_ndmp(self);
336 return ROBUST_WRITE_ERROR;
340 g_assert(count == actual);
341 return ROBUST_WRITE_OK;
348 /* translate some error codes to the corresponding Device API status */
349 switch (ndmp_connection_err_code(self->ndmp)) {
350 case NDMP9_NO_TAPE_LOADED_ERR:
351 device_set_error(DEVICE(self),
352 g_strdup(_("no tape loaded")),
353 DEVICE_STATUS_VOLUME_MISSING);
356 case NDMP9_DEVICE_BUSY_ERR:
357 device_set_error(DEVICE(self),
358 g_strdup(_("device busy")),
359 DEVICE_STATUS_DEVICE_BUSY);
363 device_set_error(DEVICE(self),
364 g_strdup(_("IO error")),
365 DEVICE_STATUS_VOLUME_UNLABELED |
366 DEVICE_STATUS_VOLUME_ERROR |
367 DEVICE_STATUS_DEVICE_ERROR);
371 device_set_error(DEVICE(self),
372 ndmp_connection_err_msg(self->ndmp),
373 DEVICE_STATUS_DEVICE_ERROR);
376 close_connection(self);
380 * Virtual function overrides
384 ndmp_device_open_device(
390 NdmpDevice *self = NDMP_DEVICE(dself);
393 /* first, extract the various parts of the device_node:
394 * HOST[:PORT]@DEVICE */
395 colon = strchr(device_node, ':');
396 at = strchr(device_node, '@');
398 colon = NULL; /* :PORT only counts if it's before the device name */
400 device_set_error(dself,
401 g_strdup_printf("invalid ndmp device name '%s'", device_name),
402 DEVICE_STATUS_DEVICE_ERROR);
408 long port = strtol(colon+1, &p, 10);
410 if (port < 0 || port >= 65536 || p != at || (!port && EINVAL == errno)) {
411 device_set_error(dself,
412 g_strdup_printf("invalid ndmp port in device name '%s'",
414 DEVICE_STATUS_DEVICE_ERROR);
417 self->ndmp_port = (gint)port;
418 self->ndmp_hostname = g_strndup(device_node, colon-device_node);
420 self->ndmp_port = 0; /* (use ndmjob's default, 10000) */
421 self->ndmp_hostname = g_strndup(device_node, at-device_node);
423 self->ndmp_device_name = g_strdup(at+1);
425 if (parent_class->open_device) {
426 parent_class->open_device(dself, device_name, device_type, device_node);
430 static void ndmp_device_finalize(GObject * obj_self)
432 NdmpDevice *self = NDMP_DEVICE (obj_self);
434 if(G_OBJECT_CLASS(parent_class)->finalize)
435 (* G_OBJECT_CLASS(parent_class)->finalize)(obj_self);
437 (void)close_tape_agent(self); /* ignore any error */
439 if (self->directtcp_conn)
440 g_object_unref(self->directtcp_conn);
442 if (self->listen_addrs)
443 g_free(self->listen_addrs);
445 close_connection(self);
447 if (self->ndmp_hostname)
448 g_free(self->ndmp_hostname);
449 if (self->ndmp_device_name)
450 g_free(self->ndmp_device_name);
451 if (self->ndmp_username)
452 g_free(self->ndmp_username);
453 if (self->ndmp_password)
454 g_free(self->ndmp_password);
456 g_free(self->ndmp_auth);
457 if (self->indirecttcp_sock != -1)
458 close(self->indirecttcp_sock);
461 static DeviceStatusFlags
462 ndmp_device_read_label(
465 NdmpDevice *self = NDMP_DEVICE(dself);
468 guint64 buf_size = 0;
470 amfree(dself->volume_label);
471 amfree(dself->volume_time);
472 dumpfile_free(dself->volume_header);
473 dself->volume_header = NULL;
475 if (device_in_error(self)) return dself->status;
477 header = dself->volume_header = g_new(dumpfile_t, 1);
480 if (!open_tape_agent(self)) {
481 /* error status was set by open_tape_agent */
482 return dself->status;
485 if (!single_ndmp_mtio(self, NDMP9_MTIO_REW)) {
486 /* error message, if any, is set by single_ndmp_mtio */
487 return dself->status;
490 /* read the tape header from the NDMP server */
492 buf = g_malloc(dself->block_size);
493 if (!ndmp_connection_tape_read(self->ndmp,
498 /* handle known errors */
499 switch (ndmp_connection_err_code(self->ndmp)) {
500 case NDMP9_NO_TAPE_LOADED_ERR:
501 device_set_error(dself,
502 g_strdup(_("no tape loaded")),
503 DEVICE_STATUS_VOLUME_MISSING);
507 device_set_error(dself,
508 g_strdup(_("IO error reading tape label")),
509 DEVICE_STATUS_VOLUME_UNLABELED |
510 DEVICE_STATUS_VOLUME_ERROR |
511 DEVICE_STATUS_DEVICE_ERROR);
516 device_set_error(dself,
517 g_strdup(_("no tape label found")),
518 DEVICE_STATUS_VOLUME_UNLABELED);
522 set_error_from_ndmp(self);
527 parse_file_header(buf, header, buf_size);
532 if (dself->status != 0) {
533 /* error already set above */
534 return dself->status;
537 /* handle a "weird" label */
538 if (header->type != F_TAPESTART) {
539 device_set_error(dself,
540 stralloc(_("No tapestart header -- unlabeled device?")),
541 DEVICE_STATUS_VOLUME_UNLABELED);
542 return dself->status;
544 dself->volume_label = g_strdup(header->name);
545 dself->volume_time = g_strdup(header->datestamp);
546 /* dself->volume_header is already set */
548 /* note: connection is left open, as well as the tape device */
550 device_set_error(dself, NULL, DEVICE_STATUS_SUCCESS);
552 return dself->status;
559 DeviceAccessMode mode,
563 NdmpDevice *self = NDMP_DEVICE(dself);
567 self = NDMP_DEVICE(dself);
569 if (device_in_error(self)) return FALSE;
571 if (!open_tape_agent(self)) {
572 /* error status was set by open_tape_agent */
576 if (mode != ACCESS_WRITE && dself->volume_label == NULL) {
577 if (ndmp_device_read_label(dself) != DEVICE_STATUS_SUCCESS)
578 /* the error was set by ndmp_device_read_label */
582 dself->access_mode = mode;
583 dself->in_file = FALSE;
585 if (!single_ndmp_mtio(self, NDMP9_MTIO_REW)) {
586 /* single_ndmp_mtio already set our error message */
590 /* Position the tape */
593 device_set_error(dself,
594 g_strdup("operation not supported"),
595 DEVICE_STATUS_DEVICE_ERROR);
604 header = make_tapestart_header(dself, label, timestamp);
605 g_assert(header != NULL);
607 header_buf = device_build_amanda_header(dself, header, NULL);
608 if (header_buf == NULL) {
609 device_set_error(dself,
610 stralloc(_("Tapestart header won't fit in a single block!")),
611 DEVICE_STATUS_DEVICE_ERROR);
612 dumpfile_free(header);
616 switch (robust_write(self, header_buf, dself->block_size)) {
617 case ROBUST_WRITE_OK_LEOM:
618 dself->is_eom = TRUE;
620 case ROBUST_WRITE_OK:
623 case ROBUST_WRITE_NO_SPACE:
624 /* this would be an odd error to see writing the tape label, but
626 device_set_error(dself,
627 stralloc(_("No space left on device")),
628 DEVICE_STATUS_VOLUME_ERROR);
629 dself->is_eom = TRUE;
632 case ROBUST_WRITE_ERROR:
633 /* error was set by robust_write or above */
634 dumpfile_free(header);
641 if (!single_ndmp_mtio(self, NDMP9_MTIO_EOF)) {
642 /* error was set by single_ndmp_mtio */
643 dumpfile_free(header);
647 dself->volume_label = newstralloc(dself->volume_label, label);
648 dself->volume_time = newstralloc(dself->volume_time, timestamp);
649 dumpfile_free(dself->volume_header);
650 dself->volume_header = header;
652 /* unset the VOLUME_UNLABELED flag, if it was set */
653 device_set_error(dself, NULL, DEVICE_STATUS_SUCCESS);
658 g_assert_not_reached();
668 NdmpDevice *self = NDMP_DEVICE(dself);
669 if (device_in_error(dself)) return FALSE;
671 /* we're not in a file anymore */
672 dself->access_mode = ACCESS_NULL;
674 if (!close_tape_agent(self)) {
675 /* error is set by close_tape_agent */
680 close_connection(self);
689 NdmpDevice *self = NDMP_DEVICE(dself);
690 if (device_in_error(dself)) return FALSE;
692 if (!single_ndmp_mtio(self, NDMP9_MTIO_OFF)) {
693 /* error was set by single_ndmp_mtio */
701 /* functions for writing */
704 ndmp_device_start_file(
708 NdmpDevice *self = NDMP_DEVICE(dself);
711 if (device_in_error(self)) return FALSE;
713 dself->is_eof = FALSE;
714 dself->is_eom = FALSE;
716 /* set the blocksize in the header properly */
717 header->blocksize = dself->block_size;
719 header_buf = device_build_amanda_header(dself, header, NULL);
720 if (header_buf == NULL) {
721 device_set_error(dself,
722 stralloc(_("Amanda file header won't fit in a single block!")),
723 DEVICE_STATUS_DEVICE_ERROR);
727 switch (robust_write(self, header_buf, dself->block_size)) {
728 case ROBUST_WRITE_OK_LEOM:
729 dself->is_eom = TRUE;
732 case ROBUST_WRITE_OK:
735 case ROBUST_WRITE_NO_SPACE:
736 /* this would be an odd error to see writing the tape label, but
738 device_set_error(dself,
739 stralloc(_("No space left on device")),
740 DEVICE_STATUS_VOLUME_ERROR);
741 dself->is_eom = TRUE;
744 case ROBUST_WRITE_ERROR:
745 /* error was set by robust_write or above */
751 /* arrange the file numbers correctly */
752 dself->in_file = TRUE;
753 if (!ndmp_get_state(self)) {
754 /* error already set by ndmp_get_state */
758 /* double-check that the tape agent gave us a non-bogus file number */
759 g_assert(dself->file > 0);
765 ndmp_device_write_block(
770 NdmpDevice *self = NDMP_DEVICE(dself);
771 gpointer replacement_buffer = NULL;
773 if (device_in_error(self)) return FALSE;
775 /* zero out to the end of a short block -- tape devices only write
777 if (size < dself->block_size) {
778 replacement_buffer = malloc(dself->block_size);
779 memcpy(replacement_buffer, data, size);
780 bzero(replacement_buffer+size, dself->block_size-size);
782 data = replacement_buffer;
783 size = dself->block_size;
786 switch (robust_write(self, data, size)) {
787 case ROBUST_WRITE_OK_LEOM:
788 dself->is_eom = TRUE;
791 case ROBUST_WRITE_OK:
794 case ROBUST_WRITE_NO_SPACE:
795 /* this would be an odd error to see writing the tape label, but
797 device_set_error(dself,
798 stralloc(_("No space left on device")),
799 DEVICE_STATUS_VOLUME_ERROR);
800 dself->is_eom = TRUE;
803 case ROBUST_WRITE_ERROR:
804 /* error was set by robust_write or above */
805 if (replacement_buffer) g_free(replacement_buffer);
811 if (replacement_buffer) g_free(replacement_buffer);
816 ndmp_device_finish_file(
819 NdmpDevice *self = NDMP_DEVICE(dself);
821 if (device_in_error(dself)) return FALSE;
823 /* we're not in a file anymore */
824 dself->in_file = FALSE;
826 if (!single_ndmp_mtio(self, NDMP9_MTIO_EOF)) {
827 /* error was set by single_ndmp_mtio */
828 dself->is_eom = TRUE;
835 /* functions for reading */
838 ndmp_device_seek_file(
842 NdmpDevice *self = NDMP_DEVICE(dself);
849 if (device_in_error(dself)) return FALSE;
851 /* file 0 is the tape header, and isn't seekable as a distinct
852 * Device-API-level file */
854 device_set_error(dself,
855 g_strdup("cannot seek to file 0"),
856 DEVICE_STATUS_DEVICE_ERROR);
860 /* first, make sure the file and block numbers are correct */
861 if (!ndmp_get_state(self)) {
862 /* error already set by ndmp_get_state */
866 /* now calculate the file delta */
867 delta = file - dself->file;
870 /* Note that this algorithm will rewind to the beginning of
871 * the current part, too */
873 /* BSF *past* the filemark we want to seek to */
874 if (!ndmp_connection_tape_mtio(self->ndmp, NDMP9_MTIO_BSF, -delta + 1, &resid)) {
875 set_error_from_ndmp(self);
881 /* now we are on the BOT side of the filemark, but we want to be
882 * on the EOT side of it. An FSF will get us there.. */
883 if (!ndmp_connection_tape_mtio(self->ndmp, NDMP9_MTIO_FSF, 1, &resid)) {
884 set_error_from_ndmp(self);
890 device_set_error(dself,
891 g_strdup_printf("BSF operation failed to seek by %d files", resid),
892 DEVICE_STATUS_DEVICE_ERROR);
895 } else /* (delta > 0) */ {
896 if (!ndmp_connection_tape_mtio(self->ndmp, NDMP9_MTIO_FSF, delta, &resid)) {
897 set_error_from_ndmp(self);
901 /* if we didn't seek all the way there, then we're past the tapeend */
903 device_set_error(dself,
904 vstrallocf(_("Could not seek forward to file %d"), file),
905 DEVICE_STATUS_VOLUME_ERROR);
911 dself->in_file = TRUE;
915 /* now read the header */
916 buf = g_malloc(dself->block_size);
917 if (!ndmp_connection_tape_read(self->ndmp,
918 buf, dself->block_size, &buf_size)) {
919 switch (ndmp_connection_err_code(self->ndmp)) {
922 return make_tapeend_header();
925 set_error_from_ndmp(self);
931 header = g_new(dumpfile_t, 1);
933 parse_file_header(buf, header, buf_size);
940 ndmp_device_seek_block(
944 if (device_in_error(dself)) return FALSE;
946 dself->block = block;
948 device_set_error(dself, g_strdup("operation not supported"), DEVICE_STATUS_DEVICE_ERROR);
953 ndmp_device_read_block (Device * dself, gpointer data, int *size_req) {
954 NdmpDevice *self = NDMP_DEVICE(dself);
955 guint64 requested, actual;
957 /* We checked the NDMP device's blocksize when the device was opened, which should
958 * catch any misalignent of server block size and Amanda block size */
960 g_assert(dself->block_size < INT_MAX); /* check data type mismatch */
961 if (!data || *size_req < (int)(dself->block_size)) {
962 *size_req = (int)(dself->block_size);
966 requested = *size_req;
967 if (!ndmp_connection_tape_read(self->ndmp,
972 /* handle known errors */
973 switch (ndmp_connection_err_code(self->ndmp)) {
976 dself->is_eof = TRUE;
980 set_error_from_ndmp(self);
985 *size_req = (int)actual; /* cast is OK - requested size was < INT_MAX too */
993 DirectTCPAddr **addrs)
997 self->indirecttcp_sock = stream_server(AF_INET, &port, 0, STREAM_BUFSIZE, 0);
998 if (self->indirecttcp_sock < 0) {
999 device_set_error(DEVICE(self),
1000 g_strdup_printf("Could not bind indirecttcp socket: %s", strerror(errno)),
1001 DEVICE_STATUS_DEVICE_ERROR);
1005 /* An IndirectTCP address is 255.255.255.255:$port */
1006 self->listen_addrs = *addrs = g_new0(DirectTCPAddr, 2);
1007 (*addrs)->ipv4 = 0xffffffff;
1008 (*addrs)->port = port;
1016 gboolean for_writing,
1017 DirectTCPAddr **addrs)
1019 NdmpDevice *self = NDMP_DEVICE(dself);
1021 if (device_in_error(self)) return FALSE;
1024 g_assert(!self->listen_addrs);
1026 if (!open_tape_agent(self)) {
1027 /* error message was set by open_tape_agent */
1031 self->for_writing = for_writing;
1033 /* first, set the window to an empty span so that the mover doesn't start
1034 * reading or writing data immediately. NDMJOB tends to reset the record
1035 * size periodically (in direct contradiction to the spec), so we reset it
1037 if (!ndmp_connection_mover_set_record_size(self->ndmp,
1038 DEVICE(self)->block_size)) {
1039 set_error_from_ndmp(self);
1044 /* if we're forcing indirecttcp, just do it */
1045 if (self->force_indirecttcp) {
1046 return indirecttcp_listen(self, addrs);
1048 if (!ndmp_connection_mover_set_window(self->ndmp, 0, 0)) {
1049 /* NDMP9_ILLEGAL_ARGS_ERR means the NDMP server doesn't like a zero-byte
1050 * mover window, so we'll ignore it */
1051 if (ndmp_connection_err_code(self->ndmp) != NDMP9_ILLEGAL_ARGS_ERR) {
1052 set_error_from_ndmp(self);
1056 g_debug("NDMP Device: cannot set zero-length mover window; "
1057 "falling back to IndirectTCP");
1058 /* In this case, we need to set up IndirectTCP */
1059 return indirecttcp_listen(self, addrs);
1062 /* For reading, set the window to the second mover record, so that the
1063 * mover will pause immediately when it wants to read the first mover
1065 if (!ndmp_connection_mover_set_window(self->ndmp,
1066 DEVICE(self)->block_size,
1067 DEVICE(self)->block_size)) {
1068 set_error_from_ndmp(self);
1073 /* then tell it to start listening */
1074 if (!ndmp_connection_mover_listen(self->ndmp,
1075 for_writing? NDMP4_MOVER_MODE_READ : NDMP4_MOVER_MODE_WRITE,
1078 set_error_from_ndmp(self);
1081 self->listen_addrs = *addrs;
1089 DirectTCPConnection **dtcpconn,
1090 ProlongProc prolong,
1091 gpointer prolong_data)
1093 NdmpDevice *self = NDMP_DEVICE(dself);
1094 ndmp9_mover_state state;
1095 guint64 bytes_moved;
1096 ndmp9_mover_mode mode;
1097 ndmp9_mover_pause_reason reason;
1098 guint64 seek_position;
1100 if (device_in_error(self)) return FALSE;
1102 g_assert(self->listen_addrs);
1106 /* TODO: support aborting this operation - maybe just always poll? */
1108 prolong_data = prolong_data;
1110 if (!self->for_writing) {
1111 /* when reading, we don't get any kind of notification that the
1112 * connection has been established, but we can't call NDMP_MOVER_READ
1113 * until the mover is active. So we have to poll, waiting for ACTIVE.
1115 gulong backoff = G_USEC_PER_SEC/20; /* 5 msec */
1117 if (!ndmp_connection_mover_get_state(self->ndmp,
1118 &state, &bytes_moved, NULL, NULL)) {
1119 set_error_from_ndmp(self);
1123 if (state != NDMP4_MOVER_STATE_LISTEN)
1126 /* back off a little bit to give the other side time to breathe,
1127 * but not more than one second */
1130 if (backoff > G_USEC_PER_SEC)
1131 backoff = G_USEC_PER_SEC;
1134 /* double-check state */
1135 if (state != NDMP4_MOVER_STATE_ACTIVE) {
1136 device_set_error(DEVICE(self),
1137 g_strdup("mover did not enter the ACTIVE state as expected"),
1138 DEVICE_STATUS_DEVICE_ERROR);
1142 /* now, we need to get this into the PAUSED state, since right now we
1143 * aren't allowed to perform any tape movement commands. So we issue a
1144 * MOVER_READ request for the whole darn image stream after setting the
1145 * usual empty window. Note that this means the whole dump will be read
1146 * in one MOVER_READ operation, even if it does not begin at the
1147 * beginning of a part. */
1148 if (!ndmp_connection_mover_read(self->ndmp, 0, G_MAXUINT64)) {
1149 set_error_from_ndmp(self);
1153 /* now we should expect a notice that the mover has paused */
1155 /* when writing, the mover will pause as soon as the first byte comes
1156 * in, so there's no need to do anything to trigger the pause.
1158 * Well, sometimes it won't - specifically, when it does not allow a
1159 * zero-byte mover window, which means we've set up IndirectTCP. But in
1160 * that case, there's nothing interesting to do here.*/
1163 if (self->indirecttcp_sock == -1) {
1164 /* NDMJOB sends NDMP9_MOVER_PAUSE_SEEK to indicate that it wants to write
1165 * outside the window, while the standard specifies .._EOW, instead. When
1166 * reading to a connection, we get the appropriate .._SEEK. It's easy
1167 * enough to handle both. */
1169 if (!ndmp_connection_wait_for_notify(self->ndmp,
1172 &reason, &seek_position)) {
1173 set_error_from_ndmp(self);
1177 if (reason != NDMP9_MOVER_PAUSE_SEEK && reason != NDMP9_MOVER_PAUSE_EOW) {
1178 device_set_error(DEVICE(self),
1179 g_strdup_printf("got NOTIFY_MOVER_PAUSED, but not because of EOW or SEEK"),
1180 DEVICE_STATUS_DEVICE_ERROR);
1185 /* at this point, if we're doing directtcp, the mover is paused and ready
1186 * to go, and the listen addrs are no longer required; if we're doing
1187 * indirecttcp, then the other end may not even know of our listen_addrs
1188 * yet, so we can't free them. */
1190 if (self->indirecttcp_sock == -1) {
1191 g_free(self->listen_addrs);
1192 self->listen_addrs = NULL;
1195 if (self->for_writing)
1196 mode = NDMP4_MOVER_MODE_WRITE;
1198 mode = NDMP4_MOVER_MODE_READ;
1200 /* set up the new directtcp connection */
1201 if (self->directtcp_conn)
1202 g_object_unref(self->directtcp_conn);
1203 self->directtcp_conn =
1204 directtcp_connection_ndmp_new(self->ndmp, mode);
1205 *dtcpconn = DIRECTTCP_CONNECTION(self->directtcp_conn);
1207 /* reference it for the caller */
1208 g_object_ref(*dtcpconn);
1214 indirecttcp_start_writing(
1217 DirectTCPAddr *real_addrs, *iter;
1220 /* The current state is that the other end is trying to connect to
1221 * indirecttcp_sock. The mover remains IDLE, although its window is set
1222 * correctly for the part we are about to write. */
1224 conn_sock = accept(self->indirecttcp_sock, NULL, NULL);
1225 if (conn_sock < 0) {
1226 device_set_error(DEVICE(self),
1227 g_strdup_printf("Could not accept indirecttcp socket: %s", strerror(errno)),
1228 DEVICE_STATUS_DEVICE_ERROR);
1232 close(self->indirecttcp_sock);
1233 self->indirecttcp_sock = -1;
1235 /* tell mover to start listening */
1236 g_assert(self->for_writing);
1237 if (!ndmp_connection_mover_listen(self->ndmp,
1238 NDMP4_MOVER_MODE_READ,
1241 set_error_from_ndmp(self);
1245 /* format the addresses and send them down the socket */
1246 for (iter = real_addrs; iter && iter->ipv4; iter++) {
1248 char *addr, *addrspec;
1250 in.s_addr = htonl(iter->ipv4);
1251 addr = inet_ntoa(in);
1253 addrspec = g_strdup_printf("%s:%d%s", addr, iter->port,
1254 (iter+1)->ipv4? " ":"");
1255 if (full_write(conn_sock, addrspec, strlen(addrspec)) < strlen(addrspec)) {
1256 device_set_error(DEVICE(self),
1257 g_strdup_printf("writing to indirecttcp socket: %s", strerror(errno)),
1258 DEVICE_STATUS_DEVICE_ERROR);
1263 /* close the socket for good. This ensures that the next call to
1264 * write_from_connection_impl will not go through the mover setup process.
1266 if (close(conn_sock) < 0) {
1267 device_set_error(DEVICE(self),
1268 g_strdup_printf("closing indirecttcp socket: %s", strerror(errno)),
1269 DEVICE_STATUS_DEVICE_ERROR);
1274 /* and free the listen_addrs, since we didn't free them in accept_impl */
1275 if (self->listen_addrs) {
1276 g_free(self->listen_addrs);
1277 self->listen_addrs = NULL;
1280 /* Now it's up to the remote end to connect to the mover and start sending
1281 * data. We won't get any notification when this happens, although we could
1282 * in principle poll for such a thing. */
1287 write_from_connection_impl(
1290 guint64 *actual_size)
1292 NdmpDevice *self = NDMP_DEVICE(dself);
1293 DirectTCPConnectionNDMP *nconn = self->directtcp_conn;
1294 gboolean eom = FALSE, eof = FALSE, eow = FALSE;
1295 ndmp9_mover_state mover_state;
1296 ndmp9_mover_halt_reason halt_reason;
1297 ndmp9_mover_pause_reason pause_reason;
1298 guint64 bytes_moved_before, bytes_moved_after;
1301 if (device_in_error(self)) return FALSE;
1306 /* if this is false, then the caller did not use use_connection correctly */
1307 g_assert(self->directtcp_conn != NULL);
1308 g_assert(self->ndmp == nconn->ndmp);
1310 if (!ndmp_connection_mover_get_state(self->ndmp,
1311 &mover_state, &bytes_moved_before, NULL, NULL)) {
1312 set_error_from_ndmp(self);
1316 if (self->indirecttcp_sock != -1) {
1317 /* If we're doing IndirectTCP, then we've deferred the whole mover_set_window
1318 * / mover_listen process.. until now. So the mover should be IDLE. */
1319 g_assert(mover_state == NDMP4_MOVER_STATE_IDLE);
1321 /* the mover had best be PAUSED right now */
1322 g_assert(mover_state == NDMP4_MOVER_STATE_PAUSED);
1325 /* we want to set the window regardless of whether this is directtcp or
1327 if (!ndmp_connection_mover_set_window(self->ndmp,
1329 size? size : G_MAXUINT64 - nconn->offset)) {
1330 set_error_from_ndmp(self);
1334 /* for DirectTCP, we just tell the mover to continue; IndirectTCP is more complicated. */
1335 if (self->indirecttcp_sock != -1) {
1336 if (!indirecttcp_start_writing(self)) {
1340 if (!ndmp_connection_mover_continue(self->ndmp)) {
1341 set_error_from_ndmp(self);
1346 /* now wait for the mover to pause itself again, or halt on EOF or an error */
1347 if (!ndmp_connection_wait_for_notify(self->ndmp,
1350 &pause_reason, NULL)) {
1351 set_error_from_ndmp(self);
1357 switch (pause_reason) {
1358 case NDMP9_MOVER_PAUSE_EOM:
1362 /* ndmjob sends .._SEEK when it should send .._EOW, so deal with
1363 * both equivalently */
1364 case NDMP9_MOVER_PAUSE_EOW:
1365 case NDMP9_MOVER_PAUSE_SEEK:
1370 err = "got NOTIFY_MOVER_PAUSED, but not because of EOW or SEEK";
1373 } else if (halt_reason) {
1374 switch (halt_reason) {
1375 case NDMP9_MOVER_HALT_CONNECT_CLOSED:
1380 case NDMP9_MOVER_HALT_ABORTED:
1381 /* case NDMP9_MOVER_HALT_MEDIA_ERROR: <-- not in ndmjob */
1382 case NDMP9_MOVER_HALT_INTERNAL_ERROR:
1383 case NDMP9_MOVER_HALT_CONNECT_ERROR:
1384 err = "unexpected NDMP_NOTIFY_MOVER_HALTED";
1390 device_set_error(DEVICE(self),
1391 g_strdup_printf("waiting for accept: %s", err),
1392 DEVICE_STATUS_DEVICE_ERROR);
1396 /* no error, so the mover stopped due to one of EOM (volume out of space),
1397 * EOF (data connection is done), or EOW (maximum part size was written).
1398 * In any case, we want to know how many bytes were written. */
1400 if (!ndmp_connection_mover_get_state(self->ndmp,
1401 &mover_state, &bytes_moved_after, NULL, NULL)) {
1402 set_error_from_ndmp(self);
1405 size = bytes_moved_after - bytes_moved_before;
1406 nconn->offset += size;
1409 *actual_size = bytes_moved_after - bytes_moved_before;
1413 ; /* mover finished the whole part -- nothing to report! */
1415 DEVICE(self)->is_eof = TRUE;
1417 /* this is a *lossless* EOM, so no need to set error, but
1418 * we do need to figure out the actual size */
1419 DEVICE(self)->is_eom = TRUE;
1421 error("not reached");
1428 read_to_connection_impl(
1431 guint64 *actual_size)
1433 NdmpDevice *self = NDMP_DEVICE(dself);
1434 DirectTCPConnectionNDMP *nconn = self->directtcp_conn;
1435 gboolean eom = FALSE, eof = FALSE, eow = FALSE;
1436 ndmp9_mover_state mover_state;
1437 ndmp9_mover_halt_reason halt_reason;
1438 ndmp9_mover_pause_reason pause_reason;
1439 guint64 bytes_moved_before, bytes_moved_after;
1445 if (device_in_error(self)) return FALSE;
1447 /* read_to_connection does not support IndirectTCP */
1448 g_assert(self->indirecttcp_sock == -1);
1450 /* if this is false, then the caller did not use use_connection correctly */
1451 g_assert(nconn != NULL);
1452 g_assert(self->ndmp == nconn->ndmp);
1454 if (!ndmp_connection_mover_get_state(self->ndmp,
1455 &mover_state, &bytes_moved_before, NULL, NULL)) {
1456 set_error_from_ndmp(self);
1460 /* the mover had best be PAUSED right now */
1461 g_assert(mover_state == NDMP4_MOVER_STATE_PAUSED);
1463 if (!ndmp_connection_mover_set_window(self->ndmp,
1465 size? size : G_MAXUINT64 - nconn->offset)) {
1466 set_error_from_ndmp(self);
1470 if (!ndmp_connection_mover_continue(self->ndmp)) {
1471 set_error_from_ndmp(self);
1475 /* now wait for the mover to pause itself again, or halt on EOF or an error */
1476 if (!ndmp_connection_wait_for_notify(self->ndmp,
1479 &pause_reason, NULL)) {
1480 set_error_from_ndmp(self);
1486 switch (pause_reason) {
1487 case NDMP9_MOVER_PAUSE_EOF:
1491 /* ndmjob sends .._SEEK when it should send .._EOW, so deal with
1492 * both equivalently */
1493 case NDMP9_MOVER_PAUSE_EOW:
1494 case NDMP9_MOVER_PAUSE_SEEK:
1499 err = "got NOTIFY_MOVER_PAUSED, but not because of EOW or SEEK";
1502 } else if (halt_reason) {
1503 switch (halt_reason) {
1504 case NDMP9_MOVER_HALT_CONNECT_CLOSED:
1509 case NDMP9_MOVER_HALT_ABORTED:
1510 /* case NDMP9_MOVER_HALT_MEDIA_ERROR: <-- not in ndmjob */
1511 case NDMP9_MOVER_HALT_INTERNAL_ERROR:
1512 case NDMP9_MOVER_HALT_CONNECT_ERROR:
1513 err = "unexpected NDMP_NOTIFY_MOVER_HALTED";
1519 device_set_error(DEVICE(self),
1520 g_strdup_printf("waiting for accept: %s", err),
1521 DEVICE_STATUS_DEVICE_ERROR);
1525 /* no error, so the mover stopped due to one of EOM (volume out of space),
1526 * EOF (data connection is done), or EOW (maximum part size was written).
1527 * In any case, we want to know how many bytes were written. */
1529 if (!ndmp_connection_mover_get_state(self->ndmp,
1530 &mover_state, &bytes_moved_after, NULL, NULL)) {
1531 set_error_from_ndmp(self);
1534 size = bytes_moved_after - bytes_moved_before;
1535 nconn->offset += size;
1538 *actual_size = bytes_moved_after - bytes_moved_before;
1542 ; /* mover finished the whole part -- nothing to report! */
1544 DEVICE(self)->is_eof = TRUE;
1546 /* this is a *lossless* EOM, so no need to set error, but
1547 * we do need to figure out the actual size */
1548 DEVICE(self)->is_eom = TRUE;
1550 error("not reached");
1557 use_connection_impl(
1559 DirectTCPConnection *conn)
1561 NdmpDevice *self = NDMP_DEVICE(dself);
1562 DirectTCPConnectionNDMP *nconn;
1564 /* the device_use_connection_impl wrapper already made sure we're in
1565 * ACCESS_NULL, but we may have opened the tape service already to read
1566 * a label - so close it to be sure */
1567 if (!close_tape_agent(self)) {
1568 /* error was already set by close_tape_agent */
1572 /* we had best not be listening when this is called */
1573 g_assert(!self->listen_addrs);
1575 if (!IS_DIRECTTCP_CONNECTION_NDMP(conn)) {
1576 device_set_error(DEVICE(self),
1577 g_strdup("existing DirectTCPConnection is not compatible with this device"),
1578 DEVICE_STATUS_DEVICE_ERROR);
1582 if (self->directtcp_conn)
1583 g_object_unref(self->directtcp_conn);
1584 self->directtcp_conn = nconn = DIRECTTCP_CONNECTION_NDMP(conn);
1585 g_object_ref(self->directtcp_conn);
1587 /* if this is a different connection, use it */
1588 if (nconn->ndmp != self->ndmp) {
1590 close_connection(self);
1591 self->ndmp = nconn->ndmp;
1592 g_object_ref(self->ndmp);
1603 ndmp_device_set_username_fn(Device *dself,
1604 DevicePropertyBase *base, GValue *val,
1605 PropertySurety surety, PropertySource source)
1607 NdmpDevice *self = NDMP_DEVICE(dself);
1609 amfree(self->ndmp_username);
1610 self->ndmp_username = g_value_dup_string(val);
1611 device_clear_volume_details(dself);
1613 return device_simple_property_set_fn(dself, base, val, surety, source);
1617 ndmp_device_set_password_fn(Device *dself,
1618 DevicePropertyBase *base, GValue *val,
1619 PropertySurety surety, PropertySource source)
1621 NdmpDevice *self = NDMP_DEVICE(dself);
1623 amfree(self->ndmp_password);
1624 self->ndmp_password = g_value_dup_string(val);
1625 device_clear_volume_details(dself);
1627 return device_simple_property_set_fn(dself, base, val, surety, source);
1631 ndmp_device_set_auth_fn(Device *dself,
1632 DevicePropertyBase *base, GValue *val,
1633 PropertySurety surety, PropertySource source)
1635 NdmpDevice *self = NDMP_DEVICE(dself);
1637 amfree(self->ndmp_auth);
1638 self->ndmp_auth = g_value_dup_string(val);
1639 device_clear_volume_details(dself);
1641 return device_simple_property_set_fn(dself, base, val, surety, source);
1645 ndmp_device_set_verbose_fn(Device *p_self, DevicePropertyBase *base,
1646 GValue *val, PropertySurety surety, PropertySource source)
1648 NdmpDevice *self = NDMP_DEVICE(p_self);
1650 self->verbose = g_value_get_boolean(val);
1652 /* if the connection is active, set up verbose logging or turn it off */
1654 ndmp_connection_set_verbose(self->ndmp, self->verbose);
1658 return device_simple_property_set_fn(p_self, base, val, surety, source);
1662 ndmp_device_set__force_indirecttcp_fn(Device *dself,
1663 DevicePropertyBase *base, GValue *val,
1664 PropertySurety surety, PropertySource source)
1666 NdmpDevice *self = NDMP_DEVICE(dself);
1668 self->force_indirecttcp = g_value_get_boolean(val);
1670 return device_simple_property_set_fn(dself, base, val, surety, source);
1674 ndmp_device_class_init(NdmpDeviceClass * c G_GNUC_UNUSED)
1676 GObjectClass *g_object_class = (GObjectClass*) c;
1677 DeviceClass *device_class = (DeviceClass *)c;
1679 parent_class = g_type_class_ref (TYPE_DEVICE);
1681 device_class->open_device = ndmp_device_open_device;
1682 device_class->read_label = ndmp_device_read_label;
1683 device_class->start = ndmp_device_start;
1684 device_class->finish = ndmp_device_finish;
1685 device_class->eject = ndmp_device_eject;
1687 device_class->start_file = ndmp_device_start_file;
1688 device_class->write_block = ndmp_device_write_block;
1689 device_class->finish_file = ndmp_device_finish_file;
1691 device_class->seek_file = ndmp_device_seek_file;
1692 device_class->seek_block = ndmp_device_seek_block;
1693 device_class->read_block = ndmp_device_read_block;
1695 device_class->directtcp_supported = TRUE;
1696 device_class->listen = listen_impl;
1697 device_class->accept = accept_impl;
1698 device_class->write_from_connection = write_from_connection_impl;
1699 device_class->read_to_connection = read_to_connection_impl;
1700 device_class->use_connection = use_connection_impl;
1702 g_object_class->finalize = ndmp_device_finalize;
1704 device_class_register_property(device_class, PROPERTY_NDMP_USERNAME,
1705 PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
1706 device_simple_property_get_fn,
1707 ndmp_device_set_username_fn);
1709 device_class_register_property(device_class, PROPERTY_NDMP_PASSWORD,
1710 PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
1711 device_simple_property_get_fn,
1712 ndmp_device_set_password_fn);
1714 device_class_register_property(device_class, PROPERTY_NDMP_AUTH,
1715 PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
1716 device_simple_property_get_fn,
1717 ndmp_device_set_auth_fn);
1719 device_class_register_property(device_class, PROPERTY_VERBOSE,
1720 PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_MASK,
1721 device_simple_property_get_fn,
1722 ndmp_device_set_verbose_fn);
1724 device_class_register_property(device_class, PROPERTY__FORCE_INDIRECTTCP,
1725 PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_MASK,
1726 device_simple_property_get_fn,
1727 ndmp_device_set__force_indirecttcp_fn);
1731 ndmp_device_init(NdmpDevice *self)
1733 Device *dself = DEVICE(self);
1736 /* begin unconnected */
1739 /* decent defaults */
1740 dself->block_size = 32768;
1741 dself->min_block_size = 32768;
1742 dself->max_block_size = SIZE_MAX;
1744 bzero(&response, sizeof(response));
1746 g_value_init(&response, CONCURRENCY_PARADIGM_TYPE);
1747 g_value_set_enum(&response, CONCURRENCY_PARADIGM_EXCLUSIVE);
1748 device_set_simple_property(dself, PROPERTY_CONCURRENCY,
1749 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
1750 g_value_unset(&response);
1752 g_value_init(&response, STREAMING_REQUIREMENT_TYPE);
1753 g_value_set_enum(&response, STREAMING_REQUIREMENT_DESIRED);
1754 device_set_simple_property(dself, PROPERTY_STREAMING,
1755 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
1756 g_value_unset(&response);
1758 g_value_init(&response, G_TYPE_BOOLEAN);
1759 g_value_set_boolean(&response, FALSE);
1760 device_set_simple_property(dself, PROPERTY_APPENDABLE,
1761 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
1762 g_value_unset(&response);
1764 g_value_init(&response, G_TYPE_BOOLEAN);
1765 g_value_set_boolean(&response, FALSE);
1766 device_set_simple_property(dself, PROPERTY_PARTIAL_DELETION,
1767 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
1768 g_value_unset(&response);
1770 g_value_init(&response, G_TYPE_BOOLEAN);
1771 g_value_set_boolean(&response, FALSE);
1772 device_set_simple_property(dself, PROPERTY_FULL_DELETION,
1773 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
1774 g_value_unset(&response);
1776 g_value_init(&response, MEDIA_ACCESS_MODE_TYPE);
1777 g_value_set_enum(&response, MEDIA_ACCESS_MODE_READ_WRITE);
1778 device_set_simple_property(dself, PROPERTY_MEDIUM_ACCESS_TYPE,
1779 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
1780 g_value_unset(&response);
1782 g_value_init(&response, G_TYPE_STRING);
1783 g_value_set_string(&response, "ndmp");
1784 device_set_simple_property(dself, PROPERTY_NDMP_USERNAME,
1785 &response, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
1786 g_value_unset(&response);
1787 self->ndmp_username = g_strdup("ndmp");
1789 g_value_init(&response, G_TYPE_STRING);
1790 g_value_set_string(&response, "ndmp");
1791 device_set_simple_property(dself, PROPERTY_NDMP_PASSWORD,
1792 &response, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
1793 g_value_unset(&response);
1794 self->ndmp_password = g_strdup("ndmp");
1796 g_value_init(&response, G_TYPE_STRING);
1797 g_value_set_string(&response, "md5");
1798 device_set_simple_property(dself, PROPERTY_NDMP_AUTH,
1799 &response, PROPERTY_SURETY_BAD, PROPERTY_SOURCE_DEFAULT);
1800 g_value_unset(&response);
1801 self->ndmp_auth = g_strdup("md5");
1803 g_value_init(&response, G_TYPE_BOOLEAN);
1804 g_value_set_boolean(&response, FALSE);
1805 device_set_simple_property(dself, PROPERTY__FORCE_INDIRECTTCP,
1806 &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DEFAULT);
1807 g_value_unset(&response);
1808 self->force_indirecttcp = FALSE;
1810 self->indirecttcp_sock = -1;
1814 ndmp_device_get_type(void)
1816 static GType type = 0;
1818 if G_UNLIKELY(type == 0) {
1819 static const GTypeInfo info = {
1820 sizeof (NdmpDeviceClass),
1821 (GBaseInitFunc) NULL,
1822 (GBaseFinalizeFunc) NULL,
1823 (GClassInitFunc) ndmp_device_class_init,
1824 (GClassFinalizeFunc) NULL,
1825 NULL /* class_data */,
1826 sizeof (NdmpDevice),
1827 0 /* n_preallocs */,
1828 (GInstanceInitFunc) ndmp_device_init,
1832 type = g_type_register_static (TYPE_DEVICE, "NdmpDevice", &info,
1840 ndmp_device_factory(
1846 NdmpDevice * ndmp_rval;
1847 g_assert(0 == strcmp(device_type, NDMP_DEVICE_NAME));
1848 rval = DEVICE(g_object_new(TYPE_NDMP_DEVICE, NULL));
1849 ndmp_rval = (NdmpDevice *)rval;
1851 device_open_device(rval, device_name, device_type, device_node);
1856 ndmp_device_register(void)
1858 static const char * device_prefix_list[] = { NDMP_DEVICE_NAME, NULL };
1860 /* register the device itself */
1861 register_device(ndmp_device_factory, device_prefix_list);
1863 device_property_fill_and_register(&device_property_ndmp_username,
1864 G_TYPE_STRING, "ndmp_username",
1865 "Username for access to the NDMP agent");
1866 device_property_fill_and_register(&device_property_ndmp_password,
1867 G_TYPE_STRING, "ndmp_password",
1868 "Password for access to the NDMP agent");
1869 device_property_fill_and_register(&device_property_ndmp_auth,
1870 G_TYPE_STRING, "ndmp_auth",
1871 "Authentication method for the NDMP agent - md5 (default), text, none, or void");
1872 device_property_fill_and_register(&device_property__force_indirecttcp,
1873 G_TYPE_BOOLEAN, "_force_indirecttcp",
1874 "For testing only - force IndirectTCP mode, even if the NDMP server supports "
1879 * DirectTCPConnectionNDMP implementation
1883 directtcp_connection_ndmp_close(DirectTCPConnection *dself)
1885 DirectTCPConnectionNDMP *self = DIRECTTCP_CONNECTION_NDMP(dself);
1887 ndmp9_mover_state state;
1888 guint64 bytes_moved;
1889 ndmp9_mover_halt_reason reason;
1890 gboolean expect_notif = FALSE;
1892 /* based on the current state, we may need to abort or stop the
1893 * mover before closing it */
1894 if (!ndmp_connection_mover_get_state(self->ndmp, &state,
1895 &bytes_moved, NULL, NULL)) {
1896 rv = ndmp_connection_err_msg(self->ndmp);
1901 case NDMP9_MOVER_STATE_HALTED:
1902 break; /* nothing to do but ndmp_mover_close, below */
1904 case NDMP9_MOVER_STATE_PAUSED:
1905 if (!ndmp_connection_mover_close(self->ndmp)) {
1906 rv = ndmp_connection_err_msg(self->ndmp);
1909 expect_notif = TRUE;
1912 case NDMP9_MOVER_STATE_ACTIVE:
1914 if (!ndmp_connection_mover_abort(self->ndmp)) {
1915 rv = ndmp_connection_err_msg(self->ndmp);
1918 expect_notif = TRUE;
1922 /* the spec isn't entirely clear that mover_close and mover_abort should
1923 * generate a NOTIF_MOVER_HALTED, but ndmjob does it */
1925 if (!ndmp_connection_wait_for_notify(self->ndmp,
1927 &reason, /* value is ignored.. */
1933 if (!ndmp_connection_mover_stop(self->ndmp)) {
1934 rv = ndmp_connection_err_msg(self->ndmp);
1940 g_object_unref(self->ndmp);
1948 directtcp_connection_ndmp_class_init(DirectTCPConnectionNDMPClass * c)
1950 DirectTCPConnectionClass *connc = (DirectTCPConnectionClass *)c;
1952 connc->close = directtcp_connection_ndmp_close;
1956 directtcp_connection_ndmp_get_type (void)
1958 static GType type = 0;
1960 if G_UNLIKELY(type == 0) {
1961 static const GTypeInfo info = {
1962 sizeof (DirectTCPConnectionNDMPClass),
1963 (GBaseInitFunc) NULL,
1964 (GBaseFinalizeFunc) NULL,
1965 (GClassInitFunc) directtcp_connection_ndmp_class_init,
1966 (GClassFinalizeFunc) NULL,
1967 NULL /* class_data */,
1968 sizeof (DirectTCPConnectionNDMP),
1969 0 /* n_preallocs */,
1970 (GInstanceInitFunc) NULL,
1974 type = g_type_register_static(TYPE_DIRECTTCP_CONNECTION,
1975 "DirectTCPConnectionNDMP", &info, (GTypeFlags)0);
1981 static DirectTCPConnectionNDMP *
1982 directtcp_connection_ndmp_new(
1983 NDMPConnection *ndmp,
1984 ndmp9_mover_mode mode)
1986 DirectTCPConnectionNDMP *dcn = DIRECTTCP_CONNECTION_NDMP(
1987 g_object_new(TYPE_DIRECTTCP_CONNECTION_NDMP, NULL));
1989 /* hang onto a copy of this NDMP connection */