Imported Upstream version 3.2.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 ndmp_connection_mover_connect(
493         NDMPConnection *self,
494         ndmp9_mover_mode mode,
495         DirectTCPAddr *addrs)
496 {
497     unsigned int naddrs, i;
498     ndmp4_tcp_addr *na;
499
500     g_assert(!self->startup_err);
501
502     /* count addrs */
503     g_assert(addrs);
504     for (naddrs = 0; addrs[naddrs].ipv4; naddrs++) ;
505
506     /* convert addrs to an ndmp4_tcp_addr */
507     na = g_new0(ndmp4_tcp_addr, naddrs);
508     for (i = 0; i < naddrs; i++) {
509         na[i].ip_addr = addrs[i].ipv4;
510         na[i].port = addrs[i].port;
511     }
512
513
514     NDMP_TRANS(self, ndmp4_mover_connect)
515         request->mode = mode;
516         request->addr.addr_type = NDMP4_ADDR_TCP;
517         request->addr.ndmp4_addr_u.tcp_addr.tcp_addr_len = naddrs;
518         request->addr.ndmp4_addr_u.tcp_addr.tcp_addr_val = na;
519         NDMP_CALL(self);
520         NDMP_FREE();
521     NDMP_END
522     return TRUE;
523 }
524
525 gboolean
526 ndmp_connection_mover_abort(
527         NDMPConnection *self)
528 {
529     g_assert(!self->startup_err);
530
531     NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_abort)
532         NDMP_CALL(self);
533         NDMP_FREE();
534     NDMP_END
535     return TRUE;
536 }
537
538 gboolean
539 ndmp_connection_mover_stop(
540         NDMPConnection *self)
541 {
542     g_assert(!self->startup_err);
543
544     NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_stop)
545         NDMP_CALL(self);
546         NDMP_FREE();
547     NDMP_END
548     return TRUE;
549 }
550
551 gboolean
552 ndmp_connection_mover_close(
553         NDMPConnection *self)
554 {
555     g_assert(!self->startup_err);
556
557     NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_close)
558         NDMP_CALL(self);
559         NDMP_FREE();
560     NDMP_END
561     return TRUE;
562 }
563
564 gboolean ndmp_connection_mover_get_state(
565         NDMPConnection *self,
566         ndmp9_mover_state *state,
567         guint64 *bytes_moved,
568         guint64 *window_offset,
569         guint64 *window_length)
570 {
571     g_assert(!self->startup_err);
572
573     NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_get_state)
574         NDMP_CALL(self);
575         if (state) *state = reply->state;
576         if (bytes_moved) *bytes_moved = reply->bytes_moved;
577         if (window_offset) *window_offset = reply->window_offset;
578         if (window_length) *window_length = reply->window_length;
579         NDMP_FREE();
580     NDMP_END
581     return TRUE;
582 }
583
584 static gboolean
585 ndmconn_handle_notify(
586     NDMPConnection *self,
587     struct ndmp_msg_buf *nmb)
588 {
589     g_assert(!self->startup_err);
590
591     if (nmb->header.message_type == NDMP0_MESSAGE_REQUEST) {
592         switch (nmb->header.message) {
593             case NDMP4_NOTIFY_DATA_HALTED: {
594                 ndmp4_notify_data_halted_post *post =
595                     &nmb->body.ndmp4_notify_data_halted_post_body;
596                 self->data_halt_reason = post->reason;
597                 break;
598             }
599
600             case NDMP4_NOTIFY_MOVER_HALTED: {
601                 ndmp4_notify_mover_halted_post *post =
602                     &nmb->body.ndmp4_notify_mover_halted_post_body;
603                 self->mover_halt_reason = post->reason;
604                 break;
605             }
606
607             case NDMP4_NOTIFY_MOVER_PAUSED: {
608                 ndmp4_notify_mover_paused_post *post =
609                     &nmb->body.ndmp4_notify_mover_paused_post_body;
610                 self->mover_pause_reason = post->reason;
611                 self->mover_pause_seek_position = post->seek_position;
612                 break;
613             }
614
615             case NDMP4_LOG_FILE:
616             case NDMP4_LOG_MESSAGE:
617             case NDMP4_LOG_NORMAL:
618             case NDMP4_LOG_DEBUG:
619             case NDMP4_LOG_ERROR:
620             case NDMP4_LOG_WARNING: {
621                 ndmp4_log_message_post *post =
622                     &nmb->body.ndmp4_log_message_post_body;
623                 g_debug("%s", post->entry);
624                 break;
625             }
626
627             default:
628                 self->last_rc = NDMCONN_CALL_STATUS_REPLY_ERROR;
629                 self->conn->last_reply_error = NDMP4_ILLEGAL_STATE_ERR;
630                 return FALSE;
631         }
632     } else {
633         self->last_rc = NDMCONN_CALL_STATUS_REPLY_ERROR;
634         self->conn->last_reply_error = NDMP4_ILLEGAL_STATE_ERR;
635         return FALSE;
636     }
637
638     return TRUE;
639 }
640
641 /* handler for "unexpected" messages.  This handles notifications which happen
642  * to arrive while the connection is reading the socket looking for a reply. */
643 static void
644 ndmconn_unexpected_impl (struct ndmconn *conn, struct ndmp_msg_buf *nmb)
645 {
646     NDMPConnection *self = NDMP_CONNECTION(conn->context);
647
648     if (!ndmconn_handle_notify(self, nmb)) {
649         g_warning("ignoring unrecognized, unexpected packet");
650     }
651
652     ndmconn_free_nmb(NULL, nmb);
653 }
654
655 gboolean
656 ndmp_connection_wait_for_notify(
657         NDMPConnection *self,
658         ndmp9_data_halt_reason *data_halt_reason,
659         ndmp9_mover_halt_reason *mover_halt_reason,
660         ndmp9_mover_pause_reason *mover_pause_reason,
661         guint64 *mover_pause_seek_position)
662 {
663     struct ndmp_msg_buf nmb;
664
665     g_assert(!self->startup_err);
666
667     /* initialize output parameters */
668     if (data_halt_reason)
669         *data_halt_reason = NDMP4_DATA_HALT_NA;
670     if (mover_halt_reason)
671         *mover_halt_reason = NDMP4_MOVER_HALT_NA;
672     if (mover_pause_reason)
673         *mover_pause_reason = NDMP4_MOVER_PAUSE_NA;
674     if (mover_pause_seek_position)
675         *mover_pause_seek_position = 0;
676
677     while (1) {
678         gboolean found = FALSE;
679         int fd;
680         SELECT_ARG_TYPE readset;
681         int nfound;
682
683         /* if any desired notifications have been received, then we're
684          * done */
685         if (data_halt_reason && self->data_halt_reason) {
686             found = TRUE;
687             *data_halt_reason = self->data_halt_reason;
688             self->data_halt_reason = NDMP4_DATA_HALT_NA;
689         }
690
691         if (mover_halt_reason && self->mover_halt_reason) {
692             found = TRUE;
693             *mover_halt_reason = self->mover_halt_reason;
694             self->mover_halt_reason = NDMP4_MOVER_HALT_NA;
695         }
696
697         if (mover_pause_reason && self->mover_pause_reason) {
698             found = TRUE;
699             *mover_pause_reason = self->mover_pause_reason;
700             if (mover_pause_seek_position)
701                 *mover_pause_seek_position = self->mover_pause_seek_position;
702             self->mover_pause_reason = NDMP4_MOVER_PAUSE_NA;
703             self->mover_pause_seek_position = 0;
704         }
705
706         if (found)
707             return TRUE;
708
709         /* otherwise, wait for an incoming packet and handle it, then try
710          * again.  There's some select trickery here to avoid hogging the
711          * ndmlib_mutex - basically, we want to block as long as possible
712          * outside of the ndmlib_mutex critical section.  This will also be
713          * useful to allow the wait to be aborted. */
714         fd = self->conn->chan.fd;
715         FD_ZERO(&readset);
716         FD_SET(fd, &readset);
717         nfound = select(fd+1, &readset, NULL, NULL, NULL);
718
719         /* fall on through, blind to any errors - presumably the same error
720          * condition will be caught by ndmconn_recv_nmb. */
721
722         g_static_mutex_lock(&ndmlib_mutex);
723         NDMOS_MACRO_ZEROFILL(&nmb);
724         nmb.protocol_version = NDMP4VER;
725         self->last_rc = ndmconn_recv_nmb(self->conn, &nmb);
726         g_static_mutex_unlock(&ndmlib_mutex);
727
728         if (self->last_rc) {
729             /* (nothing to free) */
730             return FALSE;
731         }
732
733         ndmconn_handle_notify(self, &nmb);
734     }
735 }
736
737 /*
738  * Class Mechanics
739  */
740
741 static void
742 ndmp_connection_class_init(
743         NDMPConnectionClass * c)
744 {
745     GObjectClass *goc = (GObjectClass *)c;
746
747     goc->finalize =  finalize_impl;
748
749     parent_class = g_type_class_peek_parent(c);
750 }
751
752 GType
753 ndmp_connection_get_type(void)
754 {
755     static GType type = 0;
756     if G_UNLIKELY(type == 0) {
757         static const GTypeInfo info = {
758             sizeof (NDMPConnectionClass),
759             (GBaseInitFunc) NULL,
760             (GBaseFinalizeFunc) NULL,
761             (GClassInitFunc) ndmp_connection_class_init,
762             (GClassFinalizeFunc) NULL,
763             NULL /* class_data */,
764             sizeof (NDMPConnection),
765             0 /* n_preallocs */,
766             (GInstanceInitFunc) NULL,
767             NULL
768         };
769
770         type = g_type_register_static (G_TYPE_OBJECT, "NDMPConnection", &info,
771                                        (GTypeFlags)0);
772     }
773     return type;
774 }
775
776 /* Method stubs */
777
778 /*
779  * Constructor
780  */
781
782 NDMPConnection *
783 ndmp_connection_new(
784     gchar *hostname,
785     gint port,
786     gchar *username,
787     gchar *password,
788     gchar *auth)
789 {
790     NDMPConnection *self = NULL;
791     gchar *key = NULL;
792     gchar *errmsg = NULL;
793     struct ndmconn *conn = NULL;
794     int rc;
795     static int next_connid = 1;
796     static GStaticMutex next_connid_mutex = G_STATIC_MUTEX_INIT;
797
798     conn = ndmconn_initialize(NULL, "amanda-server");
799     if (!conn) {
800         errmsg = "could not initialize ndmconn";
801         goto out;
802     }
803
804     /* set up a handler for unexpected messages, which should generally
805      * be notifications */
806     conn->unexpected = ndmconn_unexpected_impl;
807
808     if (ndmconn_connect_host_port(conn, hostname, port, 0) != 0) {
809         errmsg = ndmconn_get_err_msg(conn);
810         ndmconn_destruct(conn);
811         goto out;
812     }
813
814     if (0 == g_ascii_strcasecmp(auth, "void")) {
815         rc = 0; /* don't authenticate */
816     } else if (0 == g_ascii_strcasecmp(auth, "none")) {
817         rc = ndmconn_auth_none(conn);
818     } else if (0 == g_ascii_strcasecmp(auth, "md5")) {
819         rc = ndmconn_auth_md5(conn, username, password);
820     } else if (0 == g_ascii_strcasecmp(auth, "text")) {
821         rc = ndmconn_auth_text(conn, username, password);
822     } else {
823         errmsg = "invalid auth type";
824         goto out;
825     }
826
827     if (rc != 0) {
828         errmsg = ndmconn_get_err_msg(conn);
829         ndmconn_destruct(conn);
830         goto out;
831     }
832
833     if (conn->protocol_version != NDMP4VER) {
834         errmsg = g_strdup_printf("Only NDMPv4 is supported; got NDMPv%d",
835             conn->protocol_version);
836         ndmconn_destruct(conn);
837         goto out;
838     }
839
840     self = NDMP_CONNECTION(g_object_new(TYPE_NDMP_CONNECTION, NULL));
841     self->conn = conn;
842     g_static_mutex_lock(&next_connid_mutex);
843     self->connid = next_connid++;
844     g_static_mutex_unlock(&next_connid_mutex);
845     conn->context = (void *)self;
846     g_debug("opening new NDMPConnection #%d: to %s:%d", self->connid, hostname, port);
847
848 out:
849     /* make a "fake" error connection if we have an error message.  Note that
850      * this object is not added to the instances hash */
851     if (errmsg) {
852         self = NDMP_CONNECTION(g_object_new(TYPE_NDMP_CONNECTION, NULL));
853         self->startup_err = errmsg;
854         errmsg = NULL;
855     }
856
857     return self;
858 }