Imported Upstream version 3.1.0
[debian/amanda] / ndmp-src / ndmpconnobj.c
1 /*
2  * Copyright (c) 2009, 2010 Zmanda, Inc.  All Rights Reserved.
3  *
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.
7  *
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
11  * for more details.
12  *
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
16  *
17  * Contact information: Zmanda Inc., 465 N Mathlida Ave, Suite 300
18  * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
19  */
20
21 #include "amanda.h"
22 #include "ndmpconnobj.h"
23
24 /*
25  * NDMPConnection class implementation
26  */
27
28 /* level at which to snoop when VERBOSE is set; 8 = everything but hexdumps,
29  * and 5 = packets without details */
30 #define SNOOP_LEVEL 7
31
32 static GObjectClass *parent_class = NULL;
33
34 /* and equipment to ensure we only talk to ndmlib in one thread at a time, even
35  * using multiple connections.  The ndmlib code is not necessarily reentrant,
36  * so this is better safe than sorry. */
37 static GStaticMutex ndmlib_mutex = G_STATIC_MUTEX_INIT;
38
39 /* macros like those in ndmlib.h, but designed for use in this class */
40 /* (copied from ndmp-src/ndmlib.h; see that file for copyright and license) */
41
42 #define NDMP_TRANS(SELF, TYPE) \
43   { \
44         struct ndmp_xa_buf *    xa = &(SELF)->conn->call_xa_buf; \
45         TYPE##_request * request; \
46         TYPE##_reply   * reply; \
47         request = &xa->request.body.TYPE##_request_body; \
48         reply = &xa->reply.body.TYPE##_reply_body; \
49         NDMOS_MACRO_ZEROFILL (xa); \
50         xa->request.protocol_version = NDMP4VER; \
51         xa->request.header.message = (ndmp0_message) MT_##TYPE; \
52         g_static_mutex_lock(&ndmlib_mutex); \
53      {
54
55 #define NDMP_TRANS_NO_REQUEST(SELF, TYPE) \
56   { \
57         struct ndmp_xa_buf *    xa = &(SELF)->conn->call_xa_buf; \
58         TYPE##_reply   * reply; \
59         reply = &xa->reply.body.TYPE##_reply_body; \
60         NDMOS_MACRO_ZEROFILL (xa); \
61         xa->request.protocol_version = NDMP4VER; \
62         xa->request.header.message = (ndmp0_message) MT_##TYPE; \
63         g_static_mutex_lock(&ndmlib_mutex); \
64      {
65
66 #define NDMP_CALL(SELF) \
67     do { \
68         (SELF)->last_rc = (*(SELF)->conn->call)((SELF)->conn, xa); \
69         if ((SELF)->last_rc) { \
70             NDMP_FREE(); \
71             g_static_mutex_unlock(&ndmlib_mutex); \
72             return FALSE; \
73         } \
74     } while (0);
75
76 #define NDMP_FREE() ndmconn_free_nmb(NULL, &xa->reply)
77
78 #define NDMP_END \
79         g_static_mutex_unlock(&ndmlib_mutex); \
80     } }
81
82 /*
83  * Methods
84  */
85
86 static void
87 finalize_impl(GObject *goself)
88 {
89     NDMPConnection *self = NDMP_CONNECTION(goself);
90
91     /* chain up first */
92     G_OBJECT_CLASS(parent_class)->finalize(goself);
93
94     g_debug("closing conn#%d", self->connid);
95
96     /* close this connection if necessary */
97     if (self->conn) {
98         ndmconn_destruct(self->conn);
99         self->conn = NULL;
100     }
101
102     if (self->log_state) {
103         g_free(self->log_state);
104         self->log_state = NULL;
105     }
106 }
107
108 /*
109  * Error handling
110  */
111
112 ndmp4_error
113 ndmp_connection_err_code(
114     NDMPConnection *self)
115 {
116     if (self->startup_err) {
117         return NDMP4_IO_ERR;
118     } else if (self->last_rc == NDMCONN_CALL_STATUS_REPLY_ERROR) {
119         return self->conn->last_reply_error;
120     } else {
121         return NDMP4_NO_ERR;
122     }
123 }
124
125 gchar *
126 ndmp_connection_err_msg(
127     NDMPConnection *self)
128 {
129     if (self->startup_err) {
130         return g_strdup(self->startup_err);
131     } else if (self->last_rc == NDMCONN_CALL_STATUS_REPLY_ERROR) {
132         return g_strdup_printf("Error from NDMP server: %s",
133                     ndmp9_error_to_str(self->conn->last_reply_error));
134     } else if (self->last_rc) {
135         return g_strdup_printf("ndmconn error %d: %s",
136                     self->last_rc, ndmconn_get_err_msg(self->conn));
137     } else {
138         return g_strdup_printf("No error");
139     }
140 }
141
142 static void
143 ndmp_connection_ndmlog_deliver(
144     struct ndmlog *log,
145     char *tag,
146     int lev G_GNUC_UNUSED,
147     char *msg)
148 {
149     NDMPConnection *self = NDMP_CONNECTION(log->cookie);
150     g_debug("conn#%d: %s: %s", self->connid, tag, msg);
151 }
152
153 void
154 ndmp_connection_set_verbose(
155     NDMPConnection *self,
156     gboolean verbose)
157 {
158     struct ndmlog *device_ndmlog;
159     g_assert(!self->startup_err);
160
161     device_ndmlog = g_new0(struct ndmlog, 1);
162
163     self->log_state = (gpointer)device_ndmlog;
164     device_ndmlog->deliver = ndmp_connection_ndmlog_deliver;
165     device_ndmlog->cookie = self;
166
167     if (verbose) {
168         ndmconn_set_snoop(self->conn,
169             device_ndmlog,
170             SNOOP_LEVEL);
171     } else {
172         ndmconn_clear_snoop(self->conn);
173     }
174 }
175
176 /*
177  * Operations
178  */
179
180 gboolean
181 ndmp_connection_scsi_open(
182         NDMPConnection *self,
183         gchar *device)
184 {
185     g_assert(!self->startup_err);
186
187     NDMP_TRANS(self, ndmp4_scsi_open)
188         request->device = device;
189         NDMP_CALL(self);
190         NDMP_FREE();
191     NDMP_END
192     return TRUE;
193 }
194
195 gboolean
196 ndmp_connection_scsi_close(
197         NDMPConnection *self)
198 {
199     g_assert(!self->startup_err);
200
201     NDMP_TRANS_NO_REQUEST(self, ndmp4_scsi_close)
202         NDMP_CALL(self);
203         NDMP_FREE();
204     NDMP_END
205     return TRUE;
206 }
207
208 gboolean
209 ndmp_connection_scsi_execute_cdb(
210         NDMPConnection *self,
211         guint32 flags, /* NDMP4_SCSI_DATA_{IN,OUT}; OUT = to device */
212         guint32 timeout, /* in ms */
213         gpointer cdb,
214         gsize cdb_len,
215         gpointer dataout,
216         gsize dataout_len,
217         gsize *actual_dataout_len, /* output */
218         gpointer datain, /* output */
219         gsize datain_max_len, /* output buffer size */
220         gsize *actual_datain_len, /* output */
221         guint8 *status, /* output */
222         gpointer ext_sense, /* output */
223         gsize ext_sense_max_len, /* output buffer size */
224         gsize *actual_ext_sense_len /* output */
225         )
226 {
227     g_assert(!self->startup_err);
228
229     if (status)
230         *status = 0;
231     if (actual_dataout_len)
232         *actual_dataout_len = 0;
233     if (actual_datain_len)
234         *actual_datain_len = 0;
235     if (actual_ext_sense_len)
236         *actual_ext_sense_len = 0;
237
238     NDMP_TRANS(self, ndmp4_scsi_execute_cdb)
239         request->flags = flags;
240         request->timeout = timeout;
241         request->datain_len = datain_max_len;
242         request->cdb.cdb_len = cdb_len;
243         request->cdb.cdb_val = cdb;
244         request->dataout.dataout_len = dataout_len;
245         request->dataout.dataout_val = dataout;
246
247         NDMP_CALL(self);
248
249         if (status)
250             *status = reply->status;
251         if (actual_dataout_len)
252             *actual_dataout_len = reply->dataout_len;
253
254         reply->datain.datain_len = MIN(datain_max_len, reply->datain.datain_len);
255         if (actual_datain_len)
256             *actual_datain_len = reply->datain.datain_len;
257         if (datain_max_len && datain)
258             g_memmove(datain, reply->datain.datain_val, reply->datain.datain_len);
259
260         reply->ext_sense.ext_sense_len = MIN(ext_sense_max_len, reply->ext_sense.ext_sense_len);
261         if (actual_ext_sense_len)
262             *actual_ext_sense_len = reply->ext_sense.ext_sense_len;
263         if (ext_sense_max_len && ext_sense)
264             g_memmove(ext_sense, reply->ext_sense.ext_sense_val, reply->ext_sense.ext_sense_len);
265
266         NDMP_FREE();
267     NDMP_END
268     return TRUE;
269 }
270
271 gboolean
272 ndmp_connection_tape_open(
273         NDMPConnection *self,
274         gchar *device,
275         ndmp9_tape_open_mode mode)
276 {
277     g_assert(!self->startup_err);
278
279     NDMP_TRANS(self, ndmp4_tape_open)
280         request->device = device;
281         request->mode = mode;
282         NDMP_CALL(self);
283         NDMP_FREE();
284     NDMP_END
285     return TRUE;
286 }
287
288 gboolean
289 ndmp_connection_tape_close(
290         NDMPConnection *self)
291 {
292     g_assert(!self->startup_err);
293
294     NDMP_TRANS_NO_REQUEST(self, ndmp4_tape_close)
295         NDMP_CALL(self);
296         NDMP_FREE();
297     NDMP_END
298     return TRUE;
299 }
300
301 gboolean
302 ndmp_connection_tape_mtio(
303         NDMPConnection *self,
304         ndmp9_tape_mtio_op tape_op,
305         gint count,
306         guint *resid_count)
307 {
308     g_assert(!self->startup_err);
309
310     NDMP_TRANS(self, ndmp4_tape_mtio)
311         request->tape_op = tape_op;
312         request->count = count;
313         NDMP_CALL(self);
314         *resid_count = reply->resid_count;
315         NDMP_FREE();
316     NDMP_END
317     return TRUE;
318 }
319
320 gboolean
321 ndmp_connection_tape_write(
322         NDMPConnection *self,
323         gpointer buf,
324         guint64 len,
325         guint64 *count)
326 {
327     g_assert(!self->startup_err);
328
329     *count = 0;
330
331     NDMP_TRANS(self, ndmp4_tape_write)
332         request->data_out.data_out_val = buf;
333         request->data_out.data_out_len = len;
334         NDMP_CALL(self);
335         *count = reply->count;
336         NDMP_FREE();
337     NDMP_END
338     return TRUE;
339 }
340
341 gboolean
342 ndmp_connection_tape_read(
343         NDMPConnection *self,
344         gpointer buf,
345         guint64 count,
346         guint64 *out_count)
347 {
348     g_assert(!self->startup_err);
349
350     *out_count = 0;
351
352     NDMP_TRANS(self, ndmp4_tape_read)
353         request->count = count;
354         NDMP_CALL(self);
355         *out_count = reply->data_in.data_in_len;
356         g_memmove(buf, reply->data_in.data_in_val, *out_count);
357         NDMP_FREE();
358     NDMP_END
359     return TRUE;
360 }
361
362 gboolean
363 ndmp_connection_tape_get_state(
364         NDMPConnection *self,
365         guint64 *blocksize,
366         guint64 *file_num,
367         guint64 *blockno)
368 {
369     g_assert(!self->startup_err);
370
371     NDMP_TRANS_NO_REQUEST(self, ndmp4_tape_get_state)
372         NDMP_CALL(self);
373
374         if (reply->unsupported & NDMP4_TAPE_STATE_BLOCK_SIZE_UNS)
375             *blocksize = 0;
376         else
377             *blocksize = reply->block_size;
378
379         if (reply->unsupported & NDMP4_TAPE_STATE_FILE_NUM_UNS)
380             *file_num = G_MAXUINT64;
381         else
382             *file_num = reply->file_num;
383
384         if (reply->unsupported & NDMP4_TAPE_STATE_BLOCKNO_UNS)
385             *blockno = G_MAXUINT64;
386         else
387             *blockno = reply->blockno;
388
389         NDMP_FREE();
390     NDMP_END
391     return TRUE;
392 }
393
394 gboolean
395 ndmp_connection_mover_set_record_size(
396         NDMPConnection *self,
397         guint32 record_size)
398 {
399     g_assert(!self->startup_err);
400
401     NDMP_TRANS(self, ndmp4_mover_set_record_size)
402         /* this field is "len" in ndmp4, but "record_size" in ndmp9 */
403         request->len = record_size;
404         NDMP_CALL(self);
405         NDMP_FREE();
406     NDMP_END
407     return TRUE;
408 }
409
410 gboolean
411 ndmp_connection_mover_set_window(
412         NDMPConnection *self,
413         guint64 offset,
414         guint64 length)
415 {
416     g_assert(!self->startup_err);
417
418     NDMP_TRANS(self, ndmp4_mover_set_window)
419         request->offset = offset;
420         request->length = length;
421         NDMP_CALL(self);
422         NDMP_FREE();
423     NDMP_END
424     return TRUE;
425 }
426
427 gboolean
428 ndmp_connection_mover_read(
429         NDMPConnection *self,
430         guint64 offset,
431         guint64 length)
432 {
433     g_assert(!self->startup_err);
434
435     NDMP_TRANS(self, ndmp4_mover_read)
436         request->offset = offset;
437         request->length = length;
438         NDMP_CALL(self);
439         NDMP_FREE();
440     NDMP_END
441     return TRUE;
442 }
443
444 gboolean
445 ndmp_connection_mover_continue(
446         NDMPConnection *self)
447 {
448     g_assert(!self->startup_err);
449
450     NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_continue)
451         NDMP_CALL(self);
452         NDMP_FREE();
453     NDMP_END
454     return TRUE;
455 }
456
457 gboolean
458 ndmp_connection_mover_listen(
459         NDMPConnection *self,
460         ndmp9_mover_mode mode,
461         ndmp9_addr_type addr_type,
462         DirectTCPAddr **addrs)
463 {
464     unsigned int naddrs, i;
465     *addrs = NULL;
466
467     g_assert(!self->startup_err);
468
469     NDMP_TRANS(self, ndmp4_mover_listen)
470         request->mode = mode;
471         request->addr_type = addr_type;
472         NDMP_CALL(self);
473
474         if (request->addr_type != reply->connect_addr.addr_type) {
475             g_warning("MOVER_LISTEN addr_type mismatch; got %d", reply->connect_addr.addr_type);
476         }
477
478         if (reply->connect_addr.addr_type == NDMP4_ADDR_TCP) {
479             naddrs = reply->connect_addr.ndmp4_addr_u.tcp_addr.tcp_addr_len;
480             *addrs = g_new0(DirectTCPAddr, naddrs+1);
481             for (i = 0; i < naddrs; i++) {
482                 ndmp4_tcp_addr *na = &reply->connect_addr.ndmp4_addr_u.tcp_addr.tcp_addr_val[i];
483                 (*addrs)[i].ipv4 = na->ip_addr;
484                 (*addrs)[i].port = na->port;
485             }
486         }
487         NDMP_FREE();
488     NDMP_END
489     return TRUE;
490 }
491
492 gboolean
493 ndmp_connection_mover_abort(
494         NDMPConnection *self)
495 {
496     g_assert(!self->startup_err);
497
498     NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_abort)
499         NDMP_CALL(self);
500         NDMP_FREE();
501     NDMP_END
502     return TRUE;
503 }
504
505 gboolean
506 ndmp_connection_mover_stop(
507         NDMPConnection *self)
508 {
509     g_assert(!self->startup_err);
510
511     NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_stop)
512         NDMP_CALL(self);
513         NDMP_FREE();
514     NDMP_END
515     return TRUE;
516 }
517
518 gboolean
519 ndmp_connection_mover_close(
520         NDMPConnection *self)
521 {
522     g_assert(!self->startup_err);
523
524     NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_close)
525         NDMP_CALL(self);
526         NDMP_FREE();
527     NDMP_END
528     return TRUE;
529 }
530
531 gboolean ndmp_connection_mover_get_state(
532         NDMPConnection *self,
533         ndmp9_mover_state *state,
534         guint64 *bytes_moved,
535         guint64 *window_offset,
536         guint64 *window_length)
537 {
538     g_assert(!self->startup_err);
539
540     NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_get_state)
541         NDMP_CALL(self);
542         if (state) *state = reply->state;
543         if (bytes_moved) *bytes_moved = reply->bytes_moved;
544         if (window_offset) *window_offset = reply->window_offset;
545         if (window_length) *window_length = reply->window_length;
546         NDMP_FREE();
547     NDMP_END
548     return TRUE;
549 }
550
551 static gboolean
552 ndmconn_handle_notify(
553     NDMPConnection *self,
554     struct ndmp_msg_buf *nmb)
555 {
556     g_assert(!self->startup_err);
557
558     if (nmb->header.message_type == NDMP0_MESSAGE_REQUEST) {
559         switch (nmb->header.message) {
560             case NDMP4_NOTIFY_DATA_HALTED: {
561                 ndmp4_notify_data_halted_post *post =
562                     &nmb->body.ndmp4_notify_data_halted_post_body;
563                 self->data_halt_reason = post->reason;
564                 break;
565             }
566
567             case NDMP4_NOTIFY_MOVER_HALTED: {
568                 ndmp4_notify_mover_halted_post *post =
569                     &nmb->body.ndmp4_notify_mover_halted_post_body;
570                 self->mover_halt_reason = post->reason;
571                 break;
572             }
573
574             case NDMP4_NOTIFY_MOVER_PAUSED: {
575                 ndmp4_notify_mover_paused_post *post =
576                     &nmb->body.ndmp4_notify_mover_paused_post_body;
577                 self->mover_pause_reason = post->reason;
578                 self->mover_pause_seek_position = post->seek_position;
579                 break;
580             }
581
582             case NDMP4_LOG_FILE:
583             case NDMP4_LOG_MESSAGE:
584             case NDMP4_LOG_NORMAL:
585             case NDMP4_LOG_DEBUG:
586             case NDMP4_LOG_ERROR:
587             case NDMP4_LOG_WARNING: {
588                 ndmp4_log_message_post *post =
589                     &nmb->body.ndmp4_log_message_post_body;
590                 g_debug("%s", post->entry);
591                 break;
592             }
593
594             default:
595                 self->last_rc = NDMCONN_CALL_STATUS_REPLY_ERROR;
596                 self->conn->last_reply_error = NDMP4_ILLEGAL_STATE_ERR;
597                 return FALSE;
598         }
599     } else {
600         self->last_rc = NDMCONN_CALL_STATUS_REPLY_ERROR;
601         self->conn->last_reply_error = NDMP4_ILLEGAL_STATE_ERR;
602         return FALSE;
603     }
604
605     return TRUE;
606 }
607
608 /* handler for "unexpected" messages.  This handles notifications which happen
609  * to arrive while the connection is reading the socket looking for a reply. */
610 static void
611 ndmconn_unexpected_impl (struct ndmconn *conn, struct ndmp_msg_buf *nmb)
612 {
613     NDMPConnection *self = NDMP_CONNECTION(conn->context);
614
615     if (!ndmconn_handle_notify(self, nmb)) {
616         g_warning("ignoring unrecognized, unexpected packet");
617     }
618
619     ndmconn_free_nmb(NULL, nmb);
620 }
621
622 gboolean
623 ndmp_connection_wait_for_notify(
624         NDMPConnection *self,
625         ndmp9_data_halt_reason *data_halt_reason,
626         ndmp9_mover_halt_reason *mover_halt_reason,
627         ndmp9_mover_pause_reason *mover_pause_reason,
628         guint64 *mover_pause_seek_position)
629 {
630     struct ndmp_msg_buf nmb;
631
632     g_assert(!self->startup_err);
633
634     /* initialize output parameters */
635     if (data_halt_reason)
636         *data_halt_reason = NDMP4_DATA_HALT_NA;
637     if (mover_halt_reason)
638         *mover_halt_reason = NDMP4_MOVER_HALT_NA;
639     if (mover_pause_reason)
640         *mover_pause_reason = NDMP4_MOVER_PAUSE_NA;
641     if (mover_pause_seek_position)
642         *mover_pause_seek_position = 0;
643
644     while (1) {
645         gboolean found = FALSE;
646
647         /* if any desired notifications have been received, then we're
648          * done */
649         if (data_halt_reason && self->data_halt_reason) {
650             found = TRUE;
651             *data_halt_reason = self->data_halt_reason;
652             self->data_halt_reason = NDMP4_DATA_HALT_NA;
653         }
654
655         if (mover_halt_reason && self->mover_halt_reason) {
656             found = TRUE;
657             *mover_halt_reason = self->mover_halt_reason;
658             self->mover_halt_reason = NDMP4_MOVER_HALT_NA;
659         }
660
661         if (mover_pause_reason && self->mover_pause_reason) {
662             found = TRUE;
663             *mover_pause_reason = self->mover_pause_reason;
664             if (mover_pause_seek_position)
665                 *mover_pause_seek_position = self->mover_pause_seek_position;
666             self->mover_pause_reason = NDMP4_MOVER_PAUSE_NA;
667             self->mover_pause_seek_position = 0;
668         }
669
670         if (found)
671             return TRUE;
672
673         /* otherwise, wait for an incoming packet and handle it, then try
674          * again */
675         g_static_mutex_lock(&ndmlib_mutex);
676         NDMOS_MACRO_ZEROFILL(&nmb);
677         nmb.protocol_version = NDMP4VER;
678         self->last_rc = ndmconn_recv_nmb(self->conn, &nmb);
679         g_static_mutex_unlock(&ndmlib_mutex);
680
681         if (self->last_rc) {
682             /* (nothing to free) */
683             return FALSE;
684         }
685
686         ndmconn_handle_notify(self, &nmb);
687     }
688 }
689
690 /*
691  * Class Mechanics
692  */
693
694 static void
695 ndmp_connection_class_init(
696         NDMPConnectionClass * c)
697 {
698     GObjectClass *goc = (GObjectClass *)c;
699
700     goc->finalize =  finalize_impl;
701
702     parent_class = g_type_class_peek_parent(c);
703 }
704
705 GType
706 ndmp_connection_get_type(void)
707 {
708     static GType type = 0;
709     if G_UNLIKELY(type == 0) {
710         static const GTypeInfo info = {
711             sizeof (NDMPConnectionClass),
712             (GBaseInitFunc) NULL,
713             (GBaseFinalizeFunc) NULL,
714             (GClassInitFunc) ndmp_connection_class_init,
715             (GClassFinalizeFunc) NULL,
716             NULL /* class_data */,
717             sizeof (NDMPConnection),
718             0 /* n_preallocs */,
719             (GInstanceInitFunc) NULL,
720             NULL
721         };
722
723         type = g_type_register_static (G_TYPE_OBJECT, "NDMPConnection", &info,
724                                        (GTypeFlags)0);
725     }
726     return type;
727 }
728
729 /* Method stubs */
730
731 /*
732  * Constructor
733  */
734
735 NDMPConnection *
736 ndmp_connection_new(
737     gchar *hostname,
738     gint port,
739     gchar *username,
740     gchar *password,
741     gchar *auth)
742 {
743     NDMPConnection *self = NULL;
744     gchar *key = NULL;
745     gchar *errmsg = NULL;
746     struct ndmconn *conn = NULL;
747     int rc;
748     static int next_connid = 1;
749     static GStaticMutex next_connid_mutex = G_STATIC_MUTEX_INIT;
750
751     conn = ndmconn_initialize(NULL, "amanda-server");
752     if (!conn) {
753         errmsg = "could not initialize ndmconn";
754         goto out;
755     }
756
757     /* set up a handler for unexpected messages, which should generally
758      * be notifications */
759     conn->unexpected = ndmconn_unexpected_impl;
760
761     if (ndmconn_connect_host_port(conn, hostname, port, 0) != 0) {
762         errmsg = ndmconn_get_err_msg(conn);
763         ndmconn_destruct(conn);
764         goto out;
765     }
766
767     if (0 == g_ascii_strcasecmp(auth, "void")) {
768         rc = 0; /* don't authenticate */
769     } else if (0 == g_ascii_strcasecmp(auth, "none")) {
770         rc = ndmconn_auth_none(conn);
771     } else if (0 == g_ascii_strcasecmp(auth, "md5")) {
772         rc = ndmconn_auth_md5(conn, username, password);
773     } else if (0 == g_ascii_strcasecmp(auth, "text")) {
774         rc = ndmconn_auth_text(conn, username, password);
775     } else {
776         errmsg = "invalid auth type";
777         goto out;
778     }
779
780     if (rc != 0) {
781         errmsg = ndmconn_get_err_msg(conn);
782         ndmconn_destruct(conn);
783         goto out;
784     }
785
786     if (conn->protocol_version != NDMP4VER) {
787         errmsg = g_strdup_printf("Only NDMPv4 is supported; got NDMPv%d",
788             conn->protocol_version);
789         ndmconn_destruct(conn);
790         goto out;
791     }
792
793     self = NDMP_CONNECTION(g_object_new(TYPE_NDMP_CONNECTION, NULL));
794     self->conn = conn;
795     g_static_mutex_lock(&next_connid_mutex);
796     self->connid = next_connid++;
797     g_static_mutex_unlock(&next_connid_mutex);
798     conn->context = (void *)self;
799     g_debug("opening new NDMPConnection #%d: to %s:%d", self->connid, hostname, port);
800
801 out:
802     /* make a "fake" error connection if we have an error message.  Note that
803      * this object is not added to the instances hash */
804     if (errmsg) {
805         self = NDMP_CONNECTION(g_object_new(TYPE_NDMP_CONNECTION, NULL));
806         self->startup_err = errmsg;
807         errmsg = NULL;
808     }
809
810     return self;
811 }