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 N Mathlida Ave, Suite 300
18 * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
23 #include "sockaddr-util.h"
24 #include "ndmpconnobj.h"
27 * NDMPConnection class implementation
30 /* level at which to snoop when VERBOSE is set; 8 = everything but hexdumps,
31 * and 5 = packets without details */
34 static GObjectClass *parent_class = NULL;
36 /* and equipment to ensure we only talk to ndmlib in one thread at a time, even
37 * using multiple connections. The ndmlib code is not necessarily reentrant,
38 * so this is better safe than sorry. */
39 static GStaticMutex ndmlib_mutex = G_STATIC_MUTEX_INIT;
41 /* macros like those in ndmlib.h, but designed for use in this class */
42 /* (copied from ndmp-src/ndmlib.h; see that file for copyright and license) */
44 #define NDMP_TRANS(SELF, TYPE) \
46 struct ndmp_xa_buf * xa = &(SELF)->conn->call_xa_buf; \
47 TYPE##_request * request; \
48 TYPE##_reply * reply; \
49 request = &xa->request.body.TYPE##_request_body; \
50 reply = &xa->reply.body.TYPE##_reply_body; \
51 NDMOS_MACRO_ZEROFILL (xa); \
52 xa->request.protocol_version = NDMP4VER; \
53 xa->request.header.message = (ndmp0_message) MT_##TYPE; \
54 g_static_mutex_lock(&ndmlib_mutex); \
57 #define NDMP_TRANS_NO_REQUEST(SELF, TYPE) \
59 struct ndmp_xa_buf * xa = &(SELF)->conn->call_xa_buf; \
60 TYPE##_reply * reply; \
61 reply = &xa->reply.body.TYPE##_reply_body; \
62 NDMOS_MACRO_ZEROFILL (xa); \
63 xa->request.protocol_version = NDMP4VER; \
64 xa->request.header.message = (ndmp0_message) MT_##TYPE; \
65 g_static_mutex_lock(&ndmlib_mutex); \
68 #define NDMP_CALL(SELF) \
70 (SELF)->last_rc = (*(SELF)->conn->call)((SELF)->conn, xa); \
71 if ((SELF)->last_rc) { \
73 g_static_mutex_unlock(&ndmlib_mutex); \
78 #define NDMP_FREE() ndmconn_free_nmb(NULL, &xa->reply)
81 g_static_mutex_unlock(&ndmlib_mutex); \
89 finalize_impl(GObject *goself)
91 NDMPConnection *self = NDMP_CONNECTION(goself);
94 G_OBJECT_CLASS(parent_class)->finalize(goself);
96 g_debug("closing conn#%d", self->connid);
98 /* close this connection if necessary */
100 ndmconn_destruct(self->conn);
104 if (self->log_state) {
105 g_free(self->log_state);
106 self->log_state = NULL;
115 ndmp_connection_err_code(
116 NDMPConnection *self)
118 if (self->startup_err) {
120 } else if (self->last_rc == NDMCONN_CALL_STATUS_REPLY_ERROR) {
121 return self->conn->last_reply_error;
128 ndmp_connection_err_msg(
129 NDMPConnection *self)
131 if (self->startup_err) {
132 return g_strdup(self->startup_err);
133 } else if (self->last_rc == NDMCONN_CALL_STATUS_REPLY_ERROR) {
134 return g_strdup_printf("Error from NDMP server: %s",
135 ndmp9_error_to_str(self->conn->last_reply_error));
136 } else if (self->last_rc) {
137 return g_strdup_printf("ndmconn error %d: %s",
138 self->last_rc, ndmconn_get_err_msg(self->conn));
140 return g_strdup_printf("No error");
145 ndmp_connection_ndmlog_deliver(
148 int lev G_GNUC_UNUSED,
151 NDMPConnection *self = NDMP_CONNECTION(log->cookie);
152 g_debug("conn#%d: %s: %s", self->connid, tag, msg);
156 ndmp_connection_set_verbose(
157 NDMPConnection *self,
160 struct ndmlog *device_ndmlog;
161 g_assert(!self->startup_err);
163 device_ndmlog = g_new0(struct ndmlog, 1);
165 self->log_state = (gpointer)device_ndmlog;
166 device_ndmlog->deliver = ndmp_connection_ndmlog_deliver;
167 device_ndmlog->cookie = self;
170 ndmconn_set_snoop(self->conn,
174 ndmconn_clear_snoop(self->conn);
183 ndmp_connection_scsi_open(
184 NDMPConnection *self,
187 g_assert(!self->startup_err);
189 NDMP_TRANS(self, ndmp4_scsi_open)
190 request->device = device;
198 ndmp_connection_scsi_close(
199 NDMPConnection *self)
201 g_assert(!self->startup_err);
203 NDMP_TRANS_NO_REQUEST(self, ndmp4_scsi_close)
211 ndmp_connection_scsi_execute_cdb(
212 NDMPConnection *self,
213 guint32 flags, /* NDMP4_SCSI_DATA_{IN,OUT}; OUT = to device */
214 guint32 timeout, /* in ms */
219 gsize *actual_dataout_len, /* output */
220 gpointer datain, /* output */
221 gsize datain_max_len, /* output buffer size */
222 gsize *actual_datain_len, /* output */
223 guint8 *status, /* output */
224 gpointer ext_sense, /* output */
225 gsize ext_sense_max_len, /* output buffer size */
226 gsize *actual_ext_sense_len /* output */
229 g_assert(!self->startup_err);
233 if (actual_dataout_len)
234 *actual_dataout_len = 0;
235 if (actual_datain_len)
236 *actual_datain_len = 0;
237 if (actual_ext_sense_len)
238 *actual_ext_sense_len = 0;
240 NDMP_TRANS(self, ndmp4_scsi_execute_cdb)
241 request->flags = flags;
242 request->timeout = timeout;
243 request->datain_len = datain_max_len;
244 request->cdb.cdb_len = cdb_len;
245 request->cdb.cdb_val = cdb;
246 request->dataout.dataout_len = dataout_len;
247 request->dataout.dataout_val = dataout;
252 *status = reply->status;
253 if (actual_dataout_len)
254 *actual_dataout_len = reply->dataout_len;
256 reply->datain.datain_len = MIN(datain_max_len, reply->datain.datain_len);
257 if (actual_datain_len)
258 *actual_datain_len = reply->datain.datain_len;
259 if (datain_max_len && datain)
260 g_memmove(datain, reply->datain.datain_val, reply->datain.datain_len);
262 reply->ext_sense.ext_sense_len = MIN(ext_sense_max_len, reply->ext_sense.ext_sense_len);
263 if (actual_ext_sense_len)
264 *actual_ext_sense_len = reply->ext_sense.ext_sense_len;
265 if (ext_sense_max_len && ext_sense)
266 g_memmove(ext_sense, reply->ext_sense.ext_sense_val, reply->ext_sense.ext_sense_len);
274 ndmp_connection_tape_open(
275 NDMPConnection *self,
277 ndmp9_tape_open_mode mode)
279 g_assert(!self->startup_err);
281 NDMP_TRANS(self, ndmp4_tape_open)
282 request->device = device;
283 request->mode = mode;
291 ndmp_connection_tape_close(
292 NDMPConnection *self)
294 g_assert(!self->startup_err);
296 NDMP_TRANS_NO_REQUEST(self, ndmp4_tape_close)
304 ndmp_connection_tape_mtio(
305 NDMPConnection *self,
306 ndmp9_tape_mtio_op tape_op,
310 g_assert(!self->startup_err);
312 NDMP_TRANS(self, ndmp4_tape_mtio)
313 request->tape_op = tape_op;
314 request->count = count;
316 *resid_count = reply->resid_count;
323 ndmp_connection_tape_write(
324 NDMPConnection *self,
329 g_assert(!self->startup_err);
333 NDMP_TRANS(self, ndmp4_tape_write)
334 request->data_out.data_out_val = buf;
335 request->data_out.data_out_len = len;
337 *count = reply->count;
344 ndmp_connection_tape_read(
345 NDMPConnection *self,
350 g_assert(!self->startup_err);
354 NDMP_TRANS(self, ndmp4_tape_read)
355 request->count = count;
357 *out_count = reply->data_in.data_in_len;
358 g_memmove(buf, reply->data_in.data_in_val, *out_count);
365 ndmp_connection_tape_get_state(
366 NDMPConnection *self,
371 g_assert(!self->startup_err);
373 NDMP_TRANS_NO_REQUEST(self, ndmp4_tape_get_state)
376 if (reply->unsupported & NDMP4_TAPE_STATE_BLOCK_SIZE_UNS)
379 *blocksize = reply->block_size;
381 if (reply->unsupported & NDMP4_TAPE_STATE_FILE_NUM_UNS)
382 *file_num = G_MAXUINT64;
384 *file_num = reply->file_num;
386 if (reply->unsupported & NDMP4_TAPE_STATE_BLOCKNO_UNS)
387 *blockno = G_MAXUINT64;
389 *blockno = reply->blockno;
397 ndmp_connection_mover_set_record_size(
398 NDMPConnection *self,
401 g_assert(!self->startup_err);
403 NDMP_TRANS(self, ndmp4_mover_set_record_size)
404 /* this field is "len" in ndmp4, but "record_size" in ndmp9 */
405 request->len = record_size;
413 ndmp_connection_mover_set_window(
414 NDMPConnection *self,
418 g_assert(!self->startup_err);
420 NDMP_TRANS(self, ndmp4_mover_set_window)
421 request->offset = offset;
422 request->length = length;
430 ndmp_connection_mover_read(
431 NDMPConnection *self,
435 g_assert(!self->startup_err);
437 NDMP_TRANS(self, ndmp4_mover_read)
438 request->offset = offset;
439 request->length = length;
447 ndmp_connection_mover_continue(
448 NDMPConnection *self)
450 g_assert(!self->startup_err);
452 NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_continue)
460 ndmp_connection_mover_listen(
461 NDMPConnection *self,
462 ndmp9_mover_mode mode,
463 ndmp9_addr_type addr_type,
464 DirectTCPAddr **addrs)
466 unsigned int naddrs, i;
469 g_assert(!self->startup_err);
471 NDMP_TRANS(self, ndmp4_mover_listen)
472 request->mode = mode;
473 request->addr_type = addr_type;
476 if (request->addr_type != reply->connect_addr.addr_type) {
477 g_warning("MOVER_LISTEN addr_type mismatch; got %d", reply->connect_addr.addr_type);
480 if (reply->connect_addr.addr_type == NDMP4_ADDR_TCP) {
481 naddrs = reply->connect_addr.ndmp4_addr_u.tcp_addr.tcp_addr_len;
482 *addrs = g_new0(DirectTCPAddr, naddrs+1);
483 for (i = 0; i < naddrs; i++) {
484 ndmp4_tcp_addr *na = &reply->connect_addr.ndmp4_addr_u.tcp_addr.tcp_addr_val[i];
485 (*addrs)[i].sin.sin_family = AF_INET;
486 (*addrs)[i].sin.sin_addr.s_addr = htonl(na->ip_addr);
487 SU_SET_PORT(addrs[i], na->port);
495 ndmp_connection_mover_connect(
496 NDMPConnection *self,
497 ndmp9_mover_mode mode,
498 DirectTCPAddr *addrs)
500 unsigned int naddrs, i;
503 g_assert(!self->startup_err);
507 for (naddrs = 0; SU_GET_FAMILY(&addrs[naddrs]) != 0; naddrs++) ;
509 /* convert addrs to an ndmp4_tcp_addr */
510 na = g_new0(ndmp4_tcp_addr, naddrs);
511 for (i = 0; i < naddrs; i++) {
512 na[i].ip_addr = ntohl(addrs[i].sin.sin_addr.s_addr);
513 na[i].port = SU_GET_PORT(&addrs[i]);
517 NDMP_TRANS(self, ndmp4_mover_connect)
518 request->mode = mode;
519 request->addr.addr_type = NDMP4_ADDR_TCP;
520 request->addr.ndmp4_addr_u.tcp_addr.tcp_addr_len = naddrs;
521 request->addr.ndmp4_addr_u.tcp_addr.tcp_addr_val = na;
529 ndmp_connection_mover_abort(
530 NDMPConnection *self)
532 g_assert(!self->startup_err);
534 NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_abort)
542 ndmp_connection_mover_stop(
543 NDMPConnection *self)
545 g_assert(!self->startup_err);
547 NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_stop)
555 ndmp_connection_mover_close(
556 NDMPConnection *self)
558 g_assert(!self->startup_err);
560 NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_close)
567 gboolean ndmp_connection_mover_get_state(
568 NDMPConnection *self,
569 ndmp9_mover_state *state,
570 guint64 *bytes_moved,
571 guint64 *window_offset,
572 guint64 *window_length)
574 g_assert(!self->startup_err);
576 NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_get_state)
578 if (state) *state = reply->state;
579 if (bytes_moved) *bytes_moved = reply->bytes_moved;
580 if (window_offset) *window_offset = reply->window_offset;
581 if (window_length) *window_length = reply->window_length;
588 ndmconn_handle_notify(
589 NDMPConnection *self,
590 struct ndmp_msg_buf *nmb)
592 g_assert(!self->startup_err);
594 if (nmb->header.message_type == NDMP0_MESSAGE_REQUEST) {
595 switch (nmb->header.message) {
596 case NDMP4_NOTIFY_DATA_HALTED: {
597 ndmp4_notify_data_halted_post *post =
598 &nmb->body.ndmp4_notify_data_halted_post_body;
599 self->data_halt_reason = post->reason;
603 case NDMP4_NOTIFY_MOVER_HALTED: {
604 ndmp4_notify_mover_halted_post *post =
605 &nmb->body.ndmp4_notify_mover_halted_post_body;
606 self->mover_halt_reason = post->reason;
610 case NDMP4_NOTIFY_MOVER_PAUSED: {
611 ndmp4_notify_mover_paused_post *post =
612 &nmb->body.ndmp4_notify_mover_paused_post_body;
613 self->mover_pause_reason = post->reason;
614 self->mover_pause_seek_position = post->seek_position;
619 case NDMP4_LOG_MESSAGE:
620 case NDMP4_LOG_NORMAL:
621 case NDMP4_LOG_DEBUG:
622 case NDMP4_LOG_ERROR:
623 case NDMP4_LOG_WARNING: {
624 ndmp4_log_message_post *post =
625 &nmb->body.ndmp4_log_message_post_body;
626 g_debug("%s", post->entry);
631 self->last_rc = NDMCONN_CALL_STATUS_REPLY_ERROR;
632 self->conn->last_reply_error = NDMP4_ILLEGAL_STATE_ERR;
636 self->last_rc = NDMCONN_CALL_STATUS_REPLY_ERROR;
637 self->conn->last_reply_error = NDMP4_ILLEGAL_STATE_ERR;
644 /* handler for "unexpected" messages. This handles notifications which happen
645 * to arrive while the connection is reading the socket looking for a reply. */
647 ndmconn_unexpected_impl (struct ndmconn *conn, struct ndmp_msg_buf *nmb)
649 NDMPConnection *self = NDMP_CONNECTION(conn->context);
651 if (!ndmconn_handle_notify(self, nmb)) {
652 g_warning("ignoring unrecognized, unexpected packet");
655 ndmconn_free_nmb(NULL, nmb);
659 ndmp_connection_wait_for_notify(
660 NDMPConnection *self,
661 ndmp9_data_halt_reason *data_halt_reason,
662 ndmp9_mover_halt_reason *mover_halt_reason,
663 ndmp9_mover_pause_reason *mover_pause_reason,
664 guint64 *mover_pause_seek_position)
666 struct ndmp_msg_buf nmb;
668 g_assert(!self->startup_err);
670 /* initialize output parameters */
671 if (data_halt_reason)
672 *data_halt_reason = NDMP4_DATA_HALT_NA;
673 if (mover_halt_reason)
674 *mover_halt_reason = NDMP4_MOVER_HALT_NA;
675 if (mover_pause_reason)
676 *mover_pause_reason = NDMP4_MOVER_PAUSE_NA;
677 if (mover_pause_seek_position)
678 *mover_pause_seek_position = 0;
681 gboolean found = FALSE;
683 SELECT_ARG_TYPE readset;
686 /* if any desired notifications have been received, then we're
688 if (data_halt_reason && self->data_halt_reason) {
690 *data_halt_reason = self->data_halt_reason;
691 self->data_halt_reason = NDMP4_DATA_HALT_NA;
694 if (mover_halt_reason && self->mover_halt_reason) {
696 *mover_halt_reason = self->mover_halt_reason;
697 self->mover_halt_reason = NDMP4_MOVER_HALT_NA;
700 if (mover_pause_reason && self->mover_pause_reason) {
702 *mover_pause_reason = self->mover_pause_reason;
703 if (mover_pause_seek_position)
704 *mover_pause_seek_position = self->mover_pause_seek_position;
705 self->mover_pause_reason = NDMP4_MOVER_PAUSE_NA;
706 self->mover_pause_seek_position = 0;
712 /* otherwise, wait for an incoming packet and handle it, then try
713 * again. There's some select trickery here to avoid hogging the
714 * ndmlib_mutex - basically, we want to block as long as possible
715 * outside of the ndmlib_mutex critical section. This will also be
716 * useful to allow the wait to be aborted. */
717 fd = self->conn->chan.fd;
719 FD_SET(fd, &readset);
720 nfound = select(fd+1, &readset, NULL, NULL, NULL);
722 /* fall on through, blind to any errors - presumably the same error
723 * condition will be caught by ndmconn_recv_nmb. */
725 g_static_mutex_lock(&ndmlib_mutex);
726 NDMOS_MACRO_ZEROFILL(&nmb);
727 nmb.protocol_version = NDMP4VER;
728 self->last_rc = ndmconn_recv_nmb(self->conn, &nmb);
729 g_static_mutex_unlock(&ndmlib_mutex);
732 /* (nothing to free) */
736 ndmconn_handle_notify(self, &nmb);
740 typedef struct notify_data {
741 NDMPConnection *self;
742 ndmp9_data_halt_reason *data_halt_reason;
743 ndmp9_mover_halt_reason *mover_halt_reason;
744 ndmp9_mover_pause_reason *mover_pause_reason;
745 guint64 *mover_pause_seek_position;
749 event_handle_t *read_event;
752 static void handle_notify(void *cookie);
755 ndmp_connection_wait_for_notify_with_cond(
756 NDMPConnection *self,
757 ndmp9_data_halt_reason *data_halt_reason,
758 ndmp9_mover_halt_reason *mover_halt_reason,
759 ndmp9_mover_pause_reason *mover_pause_reason,
760 guint64 *mover_pause_seek_position,
764 struct ndmp_msg_buf nmb;
766 gboolean found = FALSE;
769 ndata.data_halt_reason= data_halt_reason;
770 ndata.mover_halt_reason= mover_halt_reason;
771 ndata.mover_pause_reason= mover_pause_reason;
772 ndata.mover_pause_seek_position = mover_pause_seek_position;
773 ndata.abort_mutex = abort_mutex;
774 ndata.abort_cond = abort_cond;
777 g_assert(!self->startup_err);
779 /* initialize output parameters */
780 if (data_halt_reason)
781 *data_halt_reason = NDMP4_DATA_HALT_NA;
782 if (mover_halt_reason)
783 *mover_halt_reason = NDMP4_MOVER_HALT_NA;
784 if (mover_pause_reason)
785 *mover_pause_reason = NDMP4_MOVER_PAUSE_NA;
786 if (mover_pause_seek_position)
787 *mover_pause_seek_position = 0;
789 /* if any desired notifications have been received, then we're
791 if (data_halt_reason && self->data_halt_reason) {
793 *data_halt_reason = self->data_halt_reason;
794 self->data_halt_reason = NDMP4_DATA_HALT_NA;
797 if (mover_halt_reason && self->mover_halt_reason) {
799 *mover_halt_reason = self->mover_halt_reason;
800 self->mover_halt_reason = NDMP4_MOVER_HALT_NA;
803 if (mover_pause_reason && self->mover_pause_reason) {
805 *mover_pause_reason = self->mover_pause_reason;
806 if (mover_pause_seek_position)
807 *mover_pause_seek_position = self->mover_pause_seek_position;
808 self->mover_pause_reason = NDMP4_MOVER_PAUSE_NA;
809 self->mover_pause_seek_position = 0;
815 /* otherwise, wait for an incoming packet and handle it, then try
816 * again. There's some select trickery here to avoid hogging the
817 * ndmlib_mutex - basically, we want to block as long as possible
818 * outside of the ndmlib_mutex critical section. This will also be
819 * useful to allow the wait to be aborted. */
821 ndata.read_event = event_register(self->conn->chan.fd,
822 EV_READFD, handle_notify, &ndata);
824 g_cond_wait(abort_cond, abort_mutex);
826 if (ndata.read_event) {
827 event_release(ndata.read_event);
829 if (ndata.status == 2) {
830 ndmp_connection_mover_abort(self);
831 ndmp_connection_mover_stop(self);
838 handle_notify(void *cookie)
840 notify_data *ndata = cookie;
841 struct ndmp_msg_buf nmb;
842 gboolean found = FALSE;
844 g_mutex_lock(ndata->abort_mutex);
846 event_release(ndata->read_event);
847 ndata->read_event = NULL;
849 g_static_mutex_lock(&ndmlib_mutex);
850 NDMOS_MACRO_ZEROFILL(&nmb);
851 nmb.protocol_version = NDMP4VER;
852 ndata->self->last_rc = ndmconn_recv_nmb(ndata->self->conn, &nmb);
853 g_static_mutex_unlock(&ndmlib_mutex);
855 if (ndata->self->last_rc) {
856 /* (nothing to free) */
861 ndmconn_handle_notify(ndata->self, &nmb);
864 /* if any desired notifications have been received, then we're
866 if (ndata->data_halt_reason && ndata->self->data_halt_reason) {
868 *ndata->data_halt_reason = ndata->self->data_halt_reason;
869 ndata->self->data_halt_reason = NDMP4_DATA_HALT_NA;
872 if (ndata->mover_halt_reason && ndata->self->mover_halt_reason) {
874 *ndata->mover_halt_reason = ndata->self->mover_halt_reason;
875 ndata->self->mover_halt_reason = NDMP4_MOVER_HALT_NA;
878 if (ndata->mover_pause_reason && ndata->self->mover_pause_reason) {
880 *ndata->mover_pause_reason = ndata->self->mover_pause_reason;
881 if (ndata->mover_pause_seek_position)
882 *ndata->mover_pause_seek_position = ndata->self->mover_pause_seek_position;
883 ndata->self->mover_pause_reason = NDMP4_MOVER_PAUSE_NA;
884 ndata->self->mover_pause_seek_position = 0;
888 ndata->read_event = event_register(ndata->self->conn->chan.fd,
889 EV_READFD, handle_notify, ndata);
890 g_mutex_unlock(ndata->abort_mutex);
896 g_cond_broadcast(ndata->abort_cond);
897 g_mutex_unlock(ndata->abort_mutex);
904 ndmp_connection_class_init(
905 NDMPConnectionClass * c)
907 GObjectClass *goc = (GObjectClass *)c;
909 goc->finalize = finalize_impl;
911 parent_class = g_type_class_peek_parent(c);
915 ndmp_connection_get_type(void)
917 static GType type = 0;
918 if G_UNLIKELY(type == 0) {
919 static const GTypeInfo info = {
920 sizeof (NDMPConnectionClass),
921 (GBaseInitFunc) NULL,
922 (GBaseFinalizeFunc) NULL,
923 (GClassInitFunc) ndmp_connection_class_init,
924 (GClassFinalizeFunc) NULL,
925 NULL /* class_data */,
926 sizeof (NDMPConnection),
928 (GInstanceInitFunc) NULL,
932 type = g_type_register_static (G_TYPE_OBJECT, "NDMPConnection", &info,
952 NDMPConnection *self = NULL;
954 gchar *errmsg = NULL;
955 struct ndmconn *conn = NULL;
957 static int next_connid = 1;
958 static GStaticMutex next_connid_mutex = G_STATIC_MUTEX_INIT;
960 conn = ndmconn_initialize(NULL, "amanda-server");
962 errmsg = "could not initialize ndmconn";
966 /* set up a handler for unexpected messages, which should generally
967 * be notifications */
968 conn->unexpected = ndmconn_unexpected_impl;
970 if (ndmconn_connect_host_port(conn, hostname, port, 0) != 0) {
971 errmsg = ndmconn_get_err_msg(conn);
972 ndmconn_destruct(conn);
976 if (0 == g_ascii_strcasecmp(auth, "void")) {
977 rc = 0; /* don't authenticate */
978 } else if (0 == g_ascii_strcasecmp(auth, "none")) {
979 rc = ndmconn_auth_none(conn);
980 } else if (0 == g_ascii_strcasecmp(auth, "md5")) {
981 rc = ndmconn_auth_md5(conn, username, password);
982 } else if (0 == g_ascii_strcasecmp(auth, "text")) {
983 rc = ndmconn_auth_text(conn, username, password);
985 errmsg = "invalid auth type";
990 errmsg = ndmconn_get_err_msg(conn);
991 ndmconn_destruct(conn);
995 if (conn->protocol_version != NDMP4VER) {
996 errmsg = g_strdup_printf("Only NDMPv4 is supported; got NDMPv%d",
997 conn->protocol_version);
998 ndmconn_destruct(conn);
1002 self = NDMP_CONNECTION(g_object_new(TYPE_NDMP_CONNECTION, NULL));
1004 g_static_mutex_lock(&next_connid_mutex);
1005 self->connid = next_connid++;
1006 g_static_mutex_unlock(&next_connid_mutex);
1007 conn->context = (void *)self;
1008 g_debug("opening new NDMPConnection #%d: to %s:%d", self->connid, hostname, port);
1011 /* make a "fake" error connection if we have an error message. Note that
1012 * this object is not added to the instances hash */
1014 self = NDMP_CONNECTION(g_object_new(TYPE_NDMP_CONNECTION, NULL));
1015 self->startup_err = errmsg;