Imported Upstream version 3.3.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 "sockaddr-util.h"
23 #include "ndmpconnobj.h"
24
25 /*
26  * NDMPConnection class implementation
27  */
28
29 /* level at which to snoop when VERBOSE is set; 8 = everything but hexdumps,
30  * and 5 = packets without details */
31 #define SNOOP_LEVEL 7
32
33 static GObjectClass *parent_class = NULL;
34
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;
39
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) */
42
43 #define NDMP_TRANS(SELF, TYPE) \
44   { \
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); \
54      {
55
56 #define NDMP_TRANS_NO_REQUEST(SELF, TYPE) \
57   { \
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); \
65      {
66
67 #define NDMP_CALL(SELF) \
68     do { \
69         (SELF)->last_rc = (*(SELF)->conn->call)((SELF)->conn, xa); \
70         if ((SELF)->last_rc) { \
71             NDMP_FREE(); \
72             g_static_mutex_unlock(&ndmlib_mutex); \
73             return FALSE; \
74         } \
75     } while (0);
76
77 #define NDMP_FREE() ndmconn_free_nmb(NULL, &xa->reply)
78
79 #define NDMP_END \
80         g_static_mutex_unlock(&ndmlib_mutex); \
81     } }
82
83 /*
84  * Methods
85  */
86
87 static void
88 finalize_impl(GObject *goself)
89 {
90     NDMPConnection *self = NDMP_CONNECTION(goself);
91
92     /* chain up first */
93     G_OBJECT_CLASS(parent_class)->finalize(goself);
94
95     g_debug("closing conn#%d", self->connid);
96
97     /* close this connection if necessary */
98     if (self->conn) {
99         ndmconn_destruct(self->conn);
100         self->conn = NULL;
101     }
102
103     if (self->log_state) {
104         g_free(self->log_state);
105         self->log_state = NULL;
106     }
107 }
108
109 /*
110  * Error handling
111  */
112
113 ndmp4_error
114 ndmp_connection_err_code(
115     NDMPConnection *self)
116 {
117     if (self->startup_err) {
118         return NDMP4_IO_ERR;
119     } else if (self->last_rc == NDMCONN_CALL_STATUS_REPLY_ERROR) {
120         return self->conn->last_reply_error;
121     } else {
122         return NDMP4_NO_ERR;
123     }
124 }
125
126 gchar *
127 ndmp_connection_err_msg(
128     NDMPConnection *self)
129 {
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));
138     } else {
139         return g_strdup_printf("No error");
140     }
141 }
142
143 static void
144 ndmp_connection_ndmlog_deliver(
145     struct ndmlog *log,
146     char *tag,
147     int lev G_GNUC_UNUSED,
148     char *msg)
149 {
150     NDMPConnection *self = NDMP_CONNECTION(log->cookie);
151     g_debug("conn#%d: %s: %s", self->connid, tag, msg);
152 }
153
154 void
155 ndmp_connection_set_verbose(
156     NDMPConnection *self,
157     gboolean verbose)
158 {
159     struct ndmlog *device_ndmlog;
160     g_assert(!self->startup_err);
161
162     device_ndmlog = g_new0(struct ndmlog, 1);
163
164     self->log_state = (gpointer)device_ndmlog;
165     device_ndmlog->deliver = ndmp_connection_ndmlog_deliver;
166     device_ndmlog->cookie = self;
167
168     if (verbose) {
169         ndmconn_set_snoop(self->conn,
170             device_ndmlog,
171             SNOOP_LEVEL);
172     } else {
173         ndmconn_clear_snoop(self->conn);
174     }
175 }
176
177 /*
178  * Operations
179  */
180
181 gboolean
182 ndmp_connection_scsi_open(
183         NDMPConnection *self,
184         gchar *device)
185 {
186     g_assert(!self->startup_err);
187
188     NDMP_TRANS(self, ndmp4_scsi_open)
189         request->device = device;
190         NDMP_CALL(self);
191         NDMP_FREE();
192     NDMP_END
193     return TRUE;
194 }
195
196 gboolean
197 ndmp_connection_scsi_close(
198         NDMPConnection *self)
199 {
200     g_assert(!self->startup_err);
201
202     NDMP_TRANS_NO_REQUEST(self, ndmp4_scsi_close)
203         NDMP_CALL(self);
204         NDMP_FREE();
205     NDMP_END
206     return TRUE;
207 }
208
209 gboolean
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 */
214         gpointer cdb,
215         gsize cdb_len,
216         gpointer dataout,
217         gsize dataout_len,
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 */
226         )
227 {
228     g_assert(!self->startup_err);
229
230     if (status)
231         *status = 0;
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;
238
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;
247
248         NDMP_CALL(self);
249
250         if (status)
251             *status = reply->status;
252         if (actual_dataout_len)
253             *actual_dataout_len = reply->dataout_len;
254
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);
260
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);
266
267         NDMP_FREE();
268     NDMP_END
269     return TRUE;
270 }
271
272 gboolean
273 ndmp_connection_tape_open(
274         NDMPConnection *self,
275         gchar *device,
276         ndmp9_tape_open_mode mode)
277 {
278     g_assert(!self->startup_err);
279
280     NDMP_TRANS(self, ndmp4_tape_open)
281         request->device = device;
282         request->mode = mode;
283         NDMP_CALL(self);
284         NDMP_FREE();
285     NDMP_END
286     return TRUE;
287 }
288
289 gboolean
290 ndmp_connection_tape_close(
291         NDMPConnection *self)
292 {
293     g_assert(!self->startup_err);
294
295     NDMP_TRANS_NO_REQUEST(self, ndmp4_tape_close)
296         NDMP_CALL(self);
297         NDMP_FREE();
298     NDMP_END
299     return TRUE;
300 }
301
302 gboolean
303 ndmp_connection_tape_mtio(
304         NDMPConnection *self,
305         ndmp9_tape_mtio_op tape_op,
306         gint count,
307         guint *resid_count)
308 {
309     g_assert(!self->startup_err);
310
311     NDMP_TRANS(self, ndmp4_tape_mtio)
312         request->tape_op = tape_op;
313         request->count = count;
314         NDMP_CALL(self);
315         *resid_count = reply->resid_count;
316         NDMP_FREE();
317     NDMP_END
318     return TRUE;
319 }
320
321 gboolean
322 ndmp_connection_tape_write(
323         NDMPConnection *self,
324         gpointer buf,
325         guint64 len,
326         guint64 *count)
327 {
328     g_assert(!self->startup_err);
329
330     *count = 0;
331
332     NDMP_TRANS(self, ndmp4_tape_write)
333         request->data_out.data_out_val = buf;
334         request->data_out.data_out_len = len;
335         NDMP_CALL(self);
336         *count = reply->count;
337         NDMP_FREE();
338     NDMP_END
339     return TRUE;
340 }
341
342 gboolean
343 ndmp_connection_tape_read(
344         NDMPConnection *self,
345         gpointer buf,
346         guint64 count,
347         guint64 *out_count)
348 {
349     g_assert(!self->startup_err);
350
351     *out_count = 0;
352
353     NDMP_TRANS(self, ndmp4_tape_read)
354         request->count = count;
355         NDMP_CALL(self);
356         *out_count = reply->data_in.data_in_len;
357         g_memmove(buf, reply->data_in.data_in_val, *out_count);
358         NDMP_FREE();
359     NDMP_END
360     return TRUE;
361 }
362
363 gboolean
364 ndmp_connection_tape_get_state(
365         NDMPConnection *self,
366         guint64 *blocksize,
367         guint64 *file_num,
368         guint64 *blockno)
369 {
370     g_assert(!self->startup_err);
371
372     NDMP_TRANS_NO_REQUEST(self, ndmp4_tape_get_state)
373         NDMP_CALL(self);
374
375         if (reply->unsupported & NDMP4_TAPE_STATE_BLOCK_SIZE_UNS)
376             *blocksize = 0;
377         else
378             *blocksize = reply->block_size;
379
380         if (reply->unsupported & NDMP4_TAPE_STATE_FILE_NUM_UNS)
381             *file_num = G_MAXUINT64;
382         else
383             *file_num = reply->file_num;
384
385         if (reply->unsupported & NDMP4_TAPE_STATE_BLOCKNO_UNS)
386             *blockno = G_MAXUINT64;
387         else
388             *blockno = reply->blockno;
389
390         NDMP_FREE();
391     NDMP_END
392     return TRUE;
393 }
394
395 gboolean
396 ndmp_connection_mover_set_record_size(
397         NDMPConnection *self,
398         guint32 record_size)
399 {
400     g_assert(!self->startup_err);
401
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;
405         NDMP_CALL(self);
406         NDMP_FREE();
407     NDMP_END
408     return TRUE;
409 }
410
411 gboolean
412 ndmp_connection_mover_set_window(
413         NDMPConnection *self,
414         guint64 offset,
415         guint64 length)
416 {
417     g_assert(!self->startup_err);
418
419     NDMP_TRANS(self, ndmp4_mover_set_window)
420         request->offset = offset;
421         request->length = length;
422         NDMP_CALL(self);
423         NDMP_FREE();
424     NDMP_END
425     return TRUE;
426 }
427
428 gboolean
429 ndmp_connection_mover_read(
430         NDMPConnection *self,
431         guint64 offset,
432         guint64 length)
433 {
434     g_assert(!self->startup_err);
435
436     NDMP_TRANS(self, ndmp4_mover_read)
437         request->offset = offset;
438         request->length = length;
439         NDMP_CALL(self);
440         NDMP_FREE();
441     NDMP_END
442     return TRUE;
443 }
444
445 gboolean
446 ndmp_connection_mover_continue(
447         NDMPConnection *self)
448 {
449     g_assert(!self->startup_err);
450
451     NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_continue)
452         NDMP_CALL(self);
453         NDMP_FREE();
454     NDMP_END
455     return TRUE;
456 }
457
458 gboolean
459 ndmp_connection_mover_listen(
460         NDMPConnection *self,
461         ndmp9_mover_mode mode,
462         ndmp9_addr_type addr_type,
463         DirectTCPAddr **addrs)
464 {
465     unsigned int naddrs, i;
466     *addrs = NULL;
467
468     g_assert(!self->startup_err);
469
470     NDMP_TRANS(self, ndmp4_mover_listen)
471         request->mode = mode;
472         request->addr_type = addr_type;
473         NDMP_CALL(self);
474
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);
477         }
478
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);
487             }
488         }
489         NDMP_FREE();
490     NDMP_END
491     return TRUE;
492 }
493
494 ndmp_connection_mover_connect(
495         NDMPConnection *self,
496         ndmp9_mover_mode mode,
497         DirectTCPAddr *addrs)
498 {
499     unsigned int naddrs, i;
500     ndmp4_tcp_addr *na;
501
502     g_assert(!self->startup_err);
503
504     /* count addrs */
505     g_assert(addrs);
506     for (naddrs = 0; SU_GET_FAMILY(&addrs[naddrs]) != 0; naddrs++) ;
507
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]);
513     }
514
515
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;
521         NDMP_CALL(self);
522         NDMP_FREE();
523     NDMP_END
524     return TRUE;
525 }
526
527 gboolean
528 ndmp_connection_mover_abort(
529         NDMPConnection *self)
530 {
531     g_assert(!self->startup_err);
532
533     NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_abort)
534         NDMP_CALL(self);
535         NDMP_FREE();
536     NDMP_END
537     return TRUE;
538 }
539
540 gboolean
541 ndmp_connection_mover_stop(
542         NDMPConnection *self)
543 {
544     g_assert(!self->startup_err);
545
546     NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_stop)
547         NDMP_CALL(self);
548         NDMP_FREE();
549     NDMP_END
550     return TRUE;
551 }
552
553 gboolean
554 ndmp_connection_mover_close(
555         NDMPConnection *self)
556 {
557     g_assert(!self->startup_err);
558
559     NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_close)
560         NDMP_CALL(self);
561         NDMP_FREE();
562     NDMP_END
563     return TRUE;
564 }
565
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)
572 {
573     g_assert(!self->startup_err);
574
575     NDMP_TRANS_NO_REQUEST(self, ndmp4_mover_get_state)
576         NDMP_CALL(self);
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;
581         NDMP_FREE();
582     NDMP_END
583     return TRUE;
584 }
585
586 static gboolean
587 ndmconn_handle_notify(
588     NDMPConnection *self,
589     struct ndmp_msg_buf *nmb)
590 {
591     g_assert(!self->startup_err);
592
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;
599                 break;
600             }
601
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;
606                 break;
607             }
608
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;
614                 break;
615             }
616
617             case NDMP4_LOG_FILE:
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);
626                 break;
627             }
628
629             default:
630                 self->last_rc = NDMCONN_CALL_STATUS_REPLY_ERROR;
631                 self->conn->last_reply_error = NDMP4_ILLEGAL_STATE_ERR;
632                 return FALSE;
633         }
634     } else {
635         self->last_rc = NDMCONN_CALL_STATUS_REPLY_ERROR;
636         self->conn->last_reply_error = NDMP4_ILLEGAL_STATE_ERR;
637         return FALSE;
638     }
639
640     return TRUE;
641 }
642
643 /* handler for "unexpected" messages.  This handles notifications which happen
644  * to arrive while the connection is reading the socket looking for a reply. */
645 static void
646 ndmconn_unexpected_impl (struct ndmconn *conn, struct ndmp_msg_buf *nmb)
647 {
648     NDMPConnection *self = NDMP_CONNECTION(conn->context);
649
650     if (!ndmconn_handle_notify(self, nmb)) {
651         g_warning("ignoring unrecognized, unexpected packet");
652     }
653
654     ndmconn_free_nmb(NULL, nmb);
655 }
656
657 gboolean
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)
664 {
665     struct ndmp_msg_buf nmb;
666
667     g_assert(!self->startup_err);
668
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;
678
679     while (1) {
680         gboolean found = FALSE;
681         int fd;
682         SELECT_ARG_TYPE readset;
683         int nfound;
684
685         /* if any desired notifications have been received, then we're
686          * done */
687         if (data_halt_reason && self->data_halt_reason) {
688             found = TRUE;
689             *data_halt_reason = self->data_halt_reason;
690             self->data_halt_reason = NDMP4_DATA_HALT_NA;
691         }
692
693         if (mover_halt_reason && self->mover_halt_reason) {
694             found = TRUE;
695             *mover_halt_reason = self->mover_halt_reason;
696             self->mover_halt_reason = NDMP4_MOVER_HALT_NA;
697         }
698
699         if (mover_pause_reason && self->mover_pause_reason) {
700             found = TRUE;
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;
706         }
707
708         if (found)
709             return TRUE;
710
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;
717         FD_ZERO(&readset);
718         FD_SET(fd, &readset);
719         nfound = select(fd+1, &readset, NULL, NULL, NULL);
720
721         /* fall on through, blind to any errors - presumably the same error
722          * condition will be caught by ndmconn_recv_nmb. */
723
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);
729
730         if (self->last_rc) {
731             /* (nothing to free) */
732             return FALSE;
733         }
734
735         ndmconn_handle_notify(self, &nmb);
736     }
737 }
738
739 /*
740  * Class Mechanics
741  */
742
743 static void
744 ndmp_connection_class_init(
745         NDMPConnectionClass * c)
746 {
747     GObjectClass *goc = (GObjectClass *)c;
748
749     goc->finalize =  finalize_impl;
750
751     parent_class = g_type_class_peek_parent(c);
752 }
753
754 GType
755 ndmp_connection_get_type(void)
756 {
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),
767             0 /* n_preallocs */,
768             (GInstanceInitFunc) NULL,
769             NULL
770         };
771
772         type = g_type_register_static (G_TYPE_OBJECT, "NDMPConnection", &info,
773                                        (GTypeFlags)0);
774     }
775     return type;
776 }
777
778 /* Method stubs */
779
780 /*
781  * Constructor
782  */
783
784 NDMPConnection *
785 ndmp_connection_new(
786     gchar *hostname,
787     gint port,
788     gchar *username,
789     gchar *password,
790     gchar *auth)
791 {
792     NDMPConnection *self = NULL;
793     gchar *key = NULL;
794     gchar *errmsg = NULL;
795     struct ndmconn *conn = NULL;
796     int rc;
797     static int next_connid = 1;
798     static GStaticMutex next_connid_mutex = G_STATIC_MUTEX_INIT;
799
800     conn = ndmconn_initialize(NULL, "amanda-server");
801     if (!conn) {
802         errmsg = "could not initialize ndmconn";
803         goto out;
804     }
805
806     /* set up a handler for unexpected messages, which should generally
807      * be notifications */
808     conn->unexpected = ndmconn_unexpected_impl;
809
810     if (ndmconn_connect_host_port(conn, hostname, port, 0) != 0) {
811         errmsg = ndmconn_get_err_msg(conn);
812         ndmconn_destruct(conn);
813         goto out;
814     }
815
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);
824     } else {
825         errmsg = "invalid auth type";
826         goto out;
827     }
828
829     if (rc != 0) {
830         errmsg = ndmconn_get_err_msg(conn);
831         ndmconn_destruct(conn);
832         goto out;
833     }
834
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);
839         goto out;
840     }
841
842     self = NDMP_CONNECTION(g_object_new(TYPE_NDMP_CONNECTION, NULL));
843     self->conn = conn;
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);
849
850 out:
851     /* make a "fake" error connection if we have an error message.  Note that
852      * this object is not added to the instances hash */
853     if (errmsg) {
854         self = NDMP_CONNECTION(g_object_new(TYPE_NDMP_CONNECTION, NULL));
855         self->startup_err = errmsg;
856         errmsg = NULL;
857     }
858
859     return self;
860 }