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 N Mathlida Ave, Suite 300
18 * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
22 #include "sockaddr-util.h"
23 #include "ndmpconnobj.h"
26 * NDMPConnection class implementation
29 /* level at which to snoop when VERBOSE is set; 8 = everything but hexdumps,
30 * and 5 = packets without details */
33 static GObjectClass *parent_class = NULL;
35 /* and equipment to ensure we only talk to ndmlib in one thread at a time, even
36 * using multiple connections. The ndmlib code is not necessarily reentrant,
37 * so this is better safe than sorry. */
38 static GStaticMutex ndmlib_mutex = G_STATIC_MUTEX_INIT;
40 /* macros like those in ndmlib.h, but designed for use in this class */
41 /* (copied from ndmp-src/ndmlib.h; see that file for copyright and license) */
43 #define NDMP_TRANS(SELF, TYPE) \
45 struct ndmp_xa_buf * xa = &(SELF)->conn->call_xa_buf; \
46 TYPE##_request * request; \
47 TYPE##_reply * reply; \
48 request = &xa->request.body.TYPE##_request_body; \
49 reply = &xa->reply.body.TYPE##_reply_body; \
50 NDMOS_MACRO_ZEROFILL (xa); \
51 xa->request.protocol_version = NDMP4VER; \
52 xa->request.header.message = (ndmp0_message) MT_##TYPE; \
53 g_static_mutex_lock(&ndmlib_mutex); \
56 #define NDMP_TRANS_NO_REQUEST(SELF, TYPE) \
58 struct ndmp_xa_buf * xa = &(SELF)->conn->call_xa_buf; \
59 TYPE##_reply * reply; \
60 reply = &xa->reply.body.TYPE##_reply_body; \
61 NDMOS_MACRO_ZEROFILL (xa); \
62 xa->request.protocol_version = NDMP4VER; \
63 xa->request.header.message = (ndmp0_message) MT_##TYPE; \
64 g_static_mutex_lock(&ndmlib_mutex); \
67 #define NDMP_CALL(SELF) \
69 (SELF)->last_rc = (*(SELF)->conn->call)((SELF)->conn, xa); \
70 if ((SELF)->last_rc) { \
72 g_static_mutex_unlock(&ndmlib_mutex); \
77 #define NDMP_FREE() ndmconn_free_nmb(NULL, &xa->reply)
80 g_static_mutex_unlock(&ndmlib_mutex); \
88 finalize_impl(GObject *goself)
90 NDMPConnection *self = NDMP_CONNECTION(goself);
93 G_OBJECT_CLASS(parent_class)->finalize(goself);
95 g_debug("closing conn#%d", self->connid);
97 /* close this connection if necessary */
99 ndmconn_destruct(self->conn);
103 if (self->log_state) {
104 g_free(self->log_state);
105 self->log_state = NULL;
114 ndmp_connection_err_code(
115 NDMPConnection *self)
117 if (self->startup_err) {
119 } else if (self->last_rc == NDMCONN_CALL_STATUS_REPLY_ERROR) {
120 return self->conn->last_reply_error;
127 ndmp_connection_err_msg(
128 NDMPConnection *self)
130 if (self->startup_err) {
131 return g_strdup(self->startup_err);
132 } else if (self->last_rc == NDMCONN_CALL_STATUS_REPLY_ERROR) {
133 return g_strdup_printf("Error from NDMP server: %s",
134 ndmp9_error_to_str(self->conn->last_reply_error));
135 } else if (self->last_rc) {
136 return g_strdup_printf("ndmconn error %d: %s",
137 self->last_rc, ndmconn_get_err_msg(self->conn));
139 return g_strdup_printf("No error");
144 ndmp_connection_ndmlog_deliver(
147 int lev G_GNUC_UNUSED,
150 NDMPConnection *self = NDMP_CONNECTION(log->cookie);
151 g_debug("conn#%d: %s: %s", self->connid, tag, msg);
155 ndmp_connection_set_verbose(
156 NDMPConnection *self,
159 struct ndmlog *device_ndmlog;
160 g_assert(!self->startup_err);
162 device_ndmlog = g_new0(struct ndmlog, 1);
164 self->log_state = (gpointer)device_ndmlog;
165 device_ndmlog->deliver = ndmp_connection_ndmlog_deliver;
166 device_ndmlog->cookie = self;
169 ndmconn_set_snoop(self->conn,
173 ndmconn_clear_snoop(self->conn);
182 ndmp_connection_scsi_open(
183 NDMPConnection *self,
186 g_assert(!self->startup_err);
188 NDMP_TRANS(self, ndmp4_scsi_open)
189 request->device = device;
197 ndmp_connection_scsi_close(
198 NDMPConnection *self)
200 g_assert(!self->startup_err);
202 NDMP_TRANS_NO_REQUEST(self, ndmp4_scsi_close)
210 ndmp_connection_scsi_execute_cdb(
211 NDMPConnection *self,
212 guint32 flags, /* NDMP4_SCSI_DATA_{IN,OUT}; OUT = to device */
213 guint32 timeout, /* in ms */
218 gsize *actual_dataout_len, /* output */
219 gpointer datain, /* output */
220 gsize datain_max_len, /* output buffer size */
221 gsize *actual_datain_len, /* output */
222 guint8 *status, /* output */
223 gpointer ext_sense, /* output */
224 gsize ext_sense_max_len, /* output buffer size */
225 gsize *actual_ext_sense_len /* output */
228 g_assert(!self->startup_err);
232 if (actual_dataout_len)
233 *actual_dataout_len = 0;
234 if (actual_datain_len)
235 *actual_datain_len = 0;
236 if (actual_ext_sense_len)
237 *actual_ext_sense_len = 0;
239 NDMP_TRANS(self, ndmp4_scsi_execute_cdb)
240 request->flags = flags;
241 request->timeout = timeout;
242 request->datain_len = datain_max_len;
243 request->cdb.cdb_len = cdb_len;
244 request->cdb.cdb_val = cdb;
245 request->dataout.dataout_len = dataout_len;
246 request->dataout.dataout_val = dataout;
251 *status = reply->status;
252 if (actual_dataout_len)
253 *actual_dataout_len = reply->dataout_len;
255 reply->datain.datain_len = MIN(datain_max_len, reply->datain.datain_len);
256 if (actual_datain_len)
257 *actual_datain_len = reply->datain.datain_len;
258 if (datain_max_len && datain)
259 g_memmove(datain, reply->datain.datain_val, reply->datain.datain_len);
261 reply->ext_sense.ext_sense_len = MIN(ext_sense_max_len, reply->ext_sense.ext_sense_len);
262 if (actual_ext_sense_len)
263 *actual_ext_sense_len = reply->ext_sense.ext_sense_len;
264 if (ext_sense_max_len && ext_sense)
265 g_memmove(ext_sense, reply->ext_sense.ext_sense_val, reply->ext_sense.ext_sense_len);
273 ndmp_connection_tape_open(
274 NDMPConnection *self,
276 ndmp9_tape_open_mode mode)
278 g_assert(!self->startup_err);
280 NDMP_TRANS(self, ndmp4_tape_open)
281 request->device = device;
282 request->mode = mode;
290 ndmp_connection_tape_close(
291 NDMPConnection *self)
293 g_assert(!self->startup_err);
295 NDMP_TRANS_NO_REQUEST(self, ndmp4_tape_close)
303 ndmp_connection_tape_mtio(
304 NDMPConnection *self,
305 ndmp9_tape_mtio_op tape_op,
309 g_assert(!self->startup_err);
311 NDMP_TRANS(self, ndmp4_tape_mtio)
312 request->tape_op = tape_op;
313 request->count = count;
315 *resid_count = reply->resid_count;
322 ndmp_connection_tape_write(
323 NDMPConnection *self,
328 g_assert(!self->startup_err);
332 NDMP_TRANS(self, ndmp4_tape_write)
333 request->data_out.data_out_val = buf;
334 request->data_out.data_out_len = len;
336 *count = reply->count;
343 ndmp_connection_tape_read(
344 NDMPConnection *self,
349 g_assert(!self->startup_err);
353 NDMP_TRANS(self, ndmp4_tape_read)
354 request->count = count;
356 *out_count = reply->data_in.data_in_len;
357 g_memmove(buf, reply->data_in.data_in_val, *out_count);
364 ndmp_connection_tape_get_state(
365 NDMPConnection *self,
370 g_assert(!self->startup_err);
372 NDMP_TRANS_NO_REQUEST(self, ndmp4_tape_get_state)
375 if (reply->unsupported & NDMP4_TAPE_STATE_BLOCK_SIZE_UNS)
378 *blocksize = reply->block_size;
380 if (reply->unsupported & NDMP4_TAPE_STATE_FILE_NUM_UNS)
381 *file_num = G_MAXUINT64;
383 *file_num = reply->file_num;
385 if (reply->unsupported & NDMP4_TAPE_STATE_BLOCKNO_UNS)
386 *blockno = G_MAXUINT64;
388 *blockno = reply->blockno;
396 ndmp_connection_mover_set_record_size(
397 NDMPConnection *self,
400 g_assert(!self->startup_err);
402 NDMP_TRANS(self, ndmp4_mover_set_record_size)
403 /* this field is "len" in ndmp4, but "record_size" in ndmp9 */
404 request->len = record_size;
412 ndmp_connection_mover_set_window(
413 NDMPConnection *self,
417 g_assert(!self->startup_err);
419 NDMP_TRANS(self, ndmp4_mover_set_window)
420 request->offset = offset;
421 request->length = length;
429 ndmp_connection_mover_read(
430 NDMPConnection *self,
434 g_assert(!self->startup_err);
436 NDMP_TRANS(self, ndmp4_mover_read)
437 request->offset = offset;
438 request->length = length;
446 ndmp_connection_mover_continue(
447 NDMPConnection *self)
449 g_assert(!self->startup_err);
451 NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_continue)
459 ndmp_connection_mover_listen(
460 NDMPConnection *self,
461 ndmp9_mover_mode mode,
462 ndmp9_addr_type addr_type,
463 DirectTCPAddr **addrs)
465 unsigned int naddrs, i;
468 g_assert(!self->startup_err);
470 NDMP_TRANS(self, ndmp4_mover_listen)
471 request->mode = mode;
472 request->addr_type = addr_type;
475 if (request->addr_type != reply->connect_addr.addr_type) {
476 g_warning("MOVER_LISTEN addr_type mismatch; got %d", reply->connect_addr.addr_type);
479 if (reply->connect_addr.addr_type == NDMP4_ADDR_TCP) {
480 naddrs = reply->connect_addr.ndmp4_addr_u.tcp_addr.tcp_addr_len;
481 *addrs = g_new0(DirectTCPAddr, naddrs+1);
482 for (i = 0; i < naddrs; i++) {
483 ndmp4_tcp_addr *na = &reply->connect_addr.ndmp4_addr_u.tcp_addr.tcp_addr_val[i];
484 (*addrs)[i].sin.sin_family = AF_INET;
485 (*addrs)[i].sin.sin_addr.s_addr = htonl(na->ip_addr);
486 SU_SET_PORT(addrs[i], na->port);
494 ndmp_connection_mover_connect(
495 NDMPConnection *self,
496 ndmp9_mover_mode mode,
497 DirectTCPAddr *addrs)
499 unsigned int naddrs, i;
502 g_assert(!self->startup_err);
506 for (naddrs = 0; SU_GET_FAMILY(&addrs[naddrs]) != 0; naddrs++) ;
508 /* convert addrs to an ndmp4_tcp_addr */
509 na = g_new0(ndmp4_tcp_addr, naddrs);
510 for (i = 0; i < naddrs; i++) {
511 na[i].ip_addr = ntohl(addrs[i].sin.sin_addr.s_addr);
512 na[i].port = SU_GET_PORT(&addrs[i]);
516 NDMP_TRANS(self, ndmp4_mover_connect)
517 request->mode = mode;
518 request->addr.addr_type = NDMP4_ADDR_TCP;
519 request->addr.ndmp4_addr_u.tcp_addr.tcp_addr_len = naddrs;
520 request->addr.ndmp4_addr_u.tcp_addr.tcp_addr_val = na;
528 ndmp_connection_mover_abort(
529 NDMPConnection *self)
531 g_assert(!self->startup_err);
533 NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_abort)
541 ndmp_connection_mover_stop(
542 NDMPConnection *self)
544 g_assert(!self->startup_err);
546 NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_stop)
554 ndmp_connection_mover_close(
555 NDMPConnection *self)
557 g_assert(!self->startup_err);
559 NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_close)
566 gboolean ndmp_connection_mover_get_state(
567 NDMPConnection *self,
568 ndmp9_mover_state *state,
569 guint64 *bytes_moved,
570 guint64 *window_offset,
571 guint64 *window_length)
573 g_assert(!self->startup_err);
575 NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_get_state)
577 if (state) *state = reply->state;
578 if (bytes_moved) *bytes_moved = reply->bytes_moved;
579 if (window_offset) *window_offset = reply->window_offset;
580 if (window_length) *window_length = reply->window_length;
587 ndmconn_handle_notify(
588 NDMPConnection *self,
589 struct ndmp_msg_buf *nmb)
591 g_assert(!self->startup_err);
593 if (nmb->header.message_type == NDMP0_MESSAGE_REQUEST) {
594 switch (nmb->header.message) {
595 case NDMP4_NOTIFY_DATA_HALTED: {
596 ndmp4_notify_data_halted_post *post =
597 &nmb->body.ndmp4_notify_data_halted_post_body;
598 self->data_halt_reason = post->reason;
602 case NDMP4_NOTIFY_MOVER_HALTED: {
603 ndmp4_notify_mover_halted_post *post =
604 &nmb->body.ndmp4_notify_mover_halted_post_body;
605 self->mover_halt_reason = post->reason;
609 case NDMP4_NOTIFY_MOVER_PAUSED: {
610 ndmp4_notify_mover_paused_post *post =
611 &nmb->body.ndmp4_notify_mover_paused_post_body;
612 self->mover_pause_reason = post->reason;
613 self->mover_pause_seek_position = post->seek_position;
618 case NDMP4_LOG_MESSAGE:
619 case NDMP4_LOG_NORMAL:
620 case NDMP4_LOG_DEBUG:
621 case NDMP4_LOG_ERROR:
622 case NDMP4_LOG_WARNING: {
623 ndmp4_log_message_post *post =
624 &nmb->body.ndmp4_log_message_post_body;
625 g_debug("%s", post->entry);
630 self->last_rc = NDMCONN_CALL_STATUS_REPLY_ERROR;
631 self->conn->last_reply_error = NDMP4_ILLEGAL_STATE_ERR;
635 self->last_rc = NDMCONN_CALL_STATUS_REPLY_ERROR;
636 self->conn->last_reply_error = NDMP4_ILLEGAL_STATE_ERR;
643 /* handler for "unexpected" messages. This handles notifications which happen
644 * to arrive while the connection is reading the socket looking for a reply. */
646 ndmconn_unexpected_impl (struct ndmconn *conn, struct ndmp_msg_buf *nmb)
648 NDMPConnection *self = NDMP_CONNECTION(conn->context);
650 if (!ndmconn_handle_notify(self, nmb)) {
651 g_warning("ignoring unrecognized, unexpected packet");
654 ndmconn_free_nmb(NULL, nmb);
658 ndmp_connection_wait_for_notify(
659 NDMPConnection *self,
660 ndmp9_data_halt_reason *data_halt_reason,
661 ndmp9_mover_halt_reason *mover_halt_reason,
662 ndmp9_mover_pause_reason *mover_pause_reason,
663 guint64 *mover_pause_seek_position)
665 struct ndmp_msg_buf nmb;
667 g_assert(!self->startup_err);
669 /* initialize output parameters */
670 if (data_halt_reason)
671 *data_halt_reason = NDMP4_DATA_HALT_NA;
672 if (mover_halt_reason)
673 *mover_halt_reason = NDMP4_MOVER_HALT_NA;
674 if (mover_pause_reason)
675 *mover_pause_reason = NDMP4_MOVER_PAUSE_NA;
676 if (mover_pause_seek_position)
677 *mover_pause_seek_position = 0;
680 gboolean found = FALSE;
682 SELECT_ARG_TYPE readset;
685 /* if any desired notifications have been received, then we're
687 if (data_halt_reason && self->data_halt_reason) {
689 *data_halt_reason = self->data_halt_reason;
690 self->data_halt_reason = NDMP4_DATA_HALT_NA;
693 if (mover_halt_reason && self->mover_halt_reason) {
695 *mover_halt_reason = self->mover_halt_reason;
696 self->mover_halt_reason = NDMP4_MOVER_HALT_NA;
699 if (mover_pause_reason && self->mover_pause_reason) {
701 *mover_pause_reason = self->mover_pause_reason;
702 if (mover_pause_seek_position)
703 *mover_pause_seek_position = self->mover_pause_seek_position;
704 self->mover_pause_reason = NDMP4_MOVER_PAUSE_NA;
705 self->mover_pause_seek_position = 0;
711 /* otherwise, wait for an incoming packet and handle it, then try
712 * again. There's some select trickery here to avoid hogging the
713 * ndmlib_mutex - basically, we want to block as long as possible
714 * outside of the ndmlib_mutex critical section. This will also be
715 * useful to allow the wait to be aborted. */
716 fd = self->conn->chan.fd;
718 FD_SET(fd, &readset);
719 nfound = select(fd+1, &readset, NULL, NULL, NULL);
721 /* fall on through, blind to any errors - presumably the same error
722 * condition will be caught by ndmconn_recv_nmb. */
724 g_static_mutex_lock(&ndmlib_mutex);
725 NDMOS_MACRO_ZEROFILL(&nmb);
726 nmb.protocol_version = NDMP4VER;
727 self->last_rc = ndmconn_recv_nmb(self->conn, &nmb);
728 g_static_mutex_unlock(&ndmlib_mutex);
731 /* (nothing to free) */
735 ndmconn_handle_notify(self, &nmb);
744 ndmp_connection_class_init(
745 NDMPConnectionClass * c)
747 GObjectClass *goc = (GObjectClass *)c;
749 goc->finalize = finalize_impl;
751 parent_class = g_type_class_peek_parent(c);
755 ndmp_connection_get_type(void)
757 static GType type = 0;
758 if G_UNLIKELY(type == 0) {
759 static const GTypeInfo info = {
760 sizeof (NDMPConnectionClass),
761 (GBaseInitFunc) NULL,
762 (GBaseFinalizeFunc) NULL,
763 (GClassInitFunc) ndmp_connection_class_init,
764 (GClassFinalizeFunc) NULL,
765 NULL /* class_data */,
766 sizeof (NDMPConnection),
768 (GInstanceInitFunc) NULL,
772 type = g_type_register_static (G_TYPE_OBJECT, "NDMPConnection", &info,
792 NDMPConnection *self = NULL;
794 gchar *errmsg = NULL;
795 struct ndmconn *conn = NULL;
797 static int next_connid = 1;
798 static GStaticMutex next_connid_mutex = G_STATIC_MUTEX_INIT;
800 conn = ndmconn_initialize(NULL, "amanda-server");
802 errmsg = "could not initialize ndmconn";
806 /* set up a handler for unexpected messages, which should generally
807 * be notifications */
808 conn->unexpected = ndmconn_unexpected_impl;
810 if (ndmconn_connect_host_port(conn, hostname, port, 0) != 0) {
811 errmsg = ndmconn_get_err_msg(conn);
812 ndmconn_destruct(conn);
816 if (0 == g_ascii_strcasecmp(auth, "void")) {
817 rc = 0; /* don't authenticate */
818 } else if (0 == g_ascii_strcasecmp(auth, "none")) {
819 rc = ndmconn_auth_none(conn);
820 } else if (0 == g_ascii_strcasecmp(auth, "md5")) {
821 rc = ndmconn_auth_md5(conn, username, password);
822 } else if (0 == g_ascii_strcasecmp(auth, "text")) {
823 rc = ndmconn_auth_text(conn, username, password);
825 errmsg = "invalid auth type";
830 errmsg = ndmconn_get_err_msg(conn);
831 ndmconn_destruct(conn);
835 if (conn->protocol_version != NDMP4VER) {
836 errmsg = g_strdup_printf("Only NDMPv4 is supported; got NDMPv%d",
837 conn->protocol_version);
838 ndmconn_destruct(conn);
842 self = NDMP_CONNECTION(g_object_new(TYPE_NDMP_CONNECTION, NULL));
844 g_static_mutex_lock(&next_connid_mutex);
845 self->connid = next_connid++;
846 g_static_mutex_unlock(&next_connid_mutex);
847 conn->context = (void *)self;
848 g_debug("opening new NDMPConnection #%d: to %s:%d", self->connid, hostname, port);
851 /* make a "fake" error connection if we have an error message. Note that
852 * this object is not added to the instances hash */
854 self = NDMP_CONNECTION(g_object_new(TYPE_NDMP_CONNECTION, NULL));
855 self->startup_err = errmsg;