lintian doesn't like orphan packages with uploaders...
[debian/amanda] / ndmp-src / ndma_image_stream.c
1 /*
2  * Copyright (c) 1998,1999,2000
3  *      Traakan, Inc., Los Altos, CA
4  *      All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice unmodified, this list of conditions, and the following
11  *    disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 /*
30  * Project:  NDMJOB
31  * Ident:    $Id: $
32  *
33  * Description:
34  *
35  *
36  * ndmis_connect_status transitions
37  *
38  *   Event relative to  ------ before ------    ------ after -------
39  *   "mine" end point   MINE    PEER    REMO    MINE    PEER    REMO
40  *   ====================================================================
41  *   LISTEN/LOCAL       IDLE    IDLE    IDLE    LISTEN  IDLE    EXCLUDE
42  *   LISTEN/TCP         IDLE    IDLE    IDLE    LISTEN  REMOTE  LISTEN
43  *
44  *   CONNECT/LOCAL      IDLE    LISTEN  EXCLUDE CONN'ED ACC'ED  EXCLUDE
45  *   CONNECT/TCP        IDLE    IDLE    IDLE    CONN'ED REMOTE  CONN'ED
46  *
47  *   tcp_accept()       LISTEN  REMOTE  LISTEN  ACC'ED  REMOTE  ACC'ED
48  *
49  */
50
51
52 #if 0
53         DATA                                    TAPE
54         END     ========== LOCAL ============   END
55         POINT                                   POINT
56                         REMOTE
57 #endif
58
59
60
61
62 #include "ndmagents.h"
63
64
65 int
66 ndmis_reinit_remote (struct ndm_session *sess)
67 {
68         struct ndm_image_stream *is = &sess->plumb.image_stream;
69
70         NDMOS_MACRO_ZEROFILL(&is->remote);
71
72         ndmchan_initialize (&is->remote.listen_chan, "image-stream-listen");
73         ndmchan_initialize (&is->remote.sanity_chan, "image-stream-sanity");
74         ndmchan_initialize (&is->chan, "image-stream");
75         ndmchan_setbuf (&is->chan, is->buf, sizeof is->buf);
76
77         return 0;
78 }
79
80
81 /*
82  * Initialization and Cleanup
83  ****************************************************************
84  */
85
86 /* Initialize -- Set data structure to know value, ignore current value */
87 int
88 ndmis_initialize (struct ndm_session *sess)
89 {
90         struct ndm_image_stream *is = &sess->plumb.image_stream;
91
92         NDMOS_MACRO_ZEROFILL(is);
93         NDMOS_MACRO_ZEROFILL (&is->chan);
94
95         ndmis_reinit_remote (sess);
96
97         is->data_ep.name = "DATA";
98         is->tape_ep.name = "TAPE";
99
100         return 0;
101 }
102
103
104 /* Commission -- Get agent ready. Entire session has been initialize()d */
105 int
106 ndmis_commission (struct ndm_session *sess)
107 {
108         return 0;
109 }
110
111 /* Decommission -- Discard agent */
112 int
113 ndmis_decommission (struct ndm_session *sess)
114 {
115         return 0;
116 }
117
118 /* Belay -- Cancel partially issued activation/start */
119 int
120 ndmis_belay (struct ndm_session *sess)
121 {
122         return 0;
123 }
124
125
126
127
128 /*
129  * Semantic actions -- called from ndma_dispatch()
130  ****************************************************************
131  */
132
133 ndmp9_error
134 ndmis_audit_data_listen (struct ndm_session *sess,
135   ndmp9_addr_type addr_type, char *reason)
136 {
137         struct ndm_image_stream *is = &sess->plumb.image_stream;
138         struct ndmis_end_point  *mine_ep = &is->data_ep;
139         struct ndmis_end_point  *peer_ep  = &is->tape_ep;
140
141         return ndmis_audit_ep_listen (sess, addr_type, reason,
142                                                 mine_ep, peer_ep);
143 }
144
145 ndmp9_error
146 ndmis_audit_tape_listen (struct ndm_session *sess,
147   ndmp9_addr_type addr_type, char *reason)
148 {
149         struct ndm_image_stream *is = &sess->plumb.image_stream;
150         struct ndmis_end_point  *mine_ep = &is->tape_ep;
151         struct ndmis_end_point  *peer_ep  = &is->data_ep;
152
153         return ndmis_audit_ep_listen (sess, addr_type, reason,
154                                                 mine_ep, peer_ep);
155 }
156
157 ndmp9_error
158 ndmis_audit_data_connect (struct ndm_session *sess,
159   ndmp9_addr_type addr_type, char *reason)
160 {
161         struct ndm_image_stream *is = &sess->plumb.image_stream;
162         struct ndmis_end_point  *mine_ep = &is->data_ep;
163         struct ndmis_end_point  *peer_ep  = &is->tape_ep;
164
165         return ndmis_audit_ep_connect (sess, addr_type, reason,
166                                                 mine_ep, peer_ep);
167 }
168
169 ndmp9_error
170 ndmis_audit_tape_connect (struct ndm_session *sess,
171   ndmp9_addr_type addr_type, char *reason)
172 {
173         struct ndm_image_stream *is = &sess->plumb.image_stream;
174         struct ndmis_end_point  *mine_ep = &is->tape_ep;
175         struct ndmis_end_point  *peer_ep  = &is->data_ep;
176
177         return ndmis_audit_ep_connect (sess, addr_type, reason,
178                                                 mine_ep, peer_ep);
179 }
180
181 ndmp9_error
182 ndmis_data_listen (struct ndm_session *sess,
183   ndmp9_addr_type addr_type, ndmp9_addr *ret_addr, char *reason)
184 {
185         struct ndm_image_stream *is = &sess->plumb.image_stream;
186         struct ndmis_end_point  *mine_ep = &is->data_ep;
187         struct ndmis_end_point  *peer_ep  = &is->tape_ep;
188
189         return ndmis_ep_listen (sess, addr_type, ret_addr, reason,
190                                                 mine_ep, peer_ep);
191 }
192
193 ndmp9_error
194 ndmis_tape_listen (struct ndm_session *sess,
195   ndmp9_addr_type addr_type, ndmp9_addr *ret_addr, char *reason)
196 {
197         struct ndm_image_stream *is = &sess->plumb.image_stream;
198         struct ndmis_end_point  *mine_ep = &is->tape_ep;
199         struct ndmis_end_point  *peer_ep  = &is->data_ep;
200
201         return ndmis_ep_listen (sess, addr_type, ret_addr, reason,
202                                                 mine_ep, peer_ep);
203 }
204
205 ndmp9_error
206 ndmis_data_connect (struct ndm_session *sess,
207   ndmp9_addr *addr, char *reason)
208 {
209         struct ndm_image_stream *is = &sess->plumb.image_stream;
210         struct ndmis_end_point  *mine_ep = &is->data_ep;
211         struct ndmis_end_point  *peer_ep  = &is->tape_ep;
212         ndmp9_error             error;
213
214         error = ndmis_ep_connect (sess, addr, reason, mine_ep, peer_ep);
215 #ifndef NDMOS_OPTION_NO_TAPE_AGENT
216         if (error == NDMP9_NO_ERR) {
217                 if (peer_ep->connect_status == NDMIS_CONN_ACCEPTED
218                  && peer_ep->addr_type == NDMP9_ADDR_LOCAL) {
219                         ndmta_quantum (sess);
220                 }
221         }
222 #endif /* !NDMOS_OPTION_NO_TAPE_AGENT */
223         return error;
224 }
225
226 ndmp9_error
227 ndmis_tape_connect (struct ndm_session *sess,
228   ndmp9_addr *addr, char *reason)
229 {
230         struct ndm_image_stream *is = &sess->plumb.image_stream;
231         struct ndmis_end_point  *mine_ep = &is->tape_ep;
232         struct ndmis_end_point  *peer_ep  = &is->data_ep;
233
234         return ndmis_ep_connect (sess, addr, reason, mine_ep, peer_ep);
235 }
236
237 int
238 ndmis_ep_start (struct ndm_session *sess, int chan_mode,
239   struct ndmis_end_point *mine_ep, struct ndmis_end_point *peer_ep)
240 {
241         struct ndm_image_stream *is = &sess->plumb.image_stream;
242
243         if (mine_ep->connect_status != NDMIS_CONN_CONNECTED
244          && mine_ep->connect_status != NDMIS_CONN_ACCEPTED) {
245                 return -1;
246         }
247
248         if (mine_ep->transfer_mode != NDMCHAN_MODE_IDLE) {
249                 return -2;
250         }
251
252         if (mine_ep->addr_type == NDMP9_ADDR_LOCAL) {
253                 ndmchan_start_resident (&is->chan);
254                 if (chan_mode == NDMCHAN_MODE_WRITE) {
255                         peer_ep->transfer_mode = NDMCHAN_MODE_READ;
256                 } else {
257                         peer_ep->transfer_mode = NDMCHAN_MODE_WRITE;
258                 }
259         } else if (chan_mode == NDMCHAN_MODE_WRITE) {
260                 ndmchan_pending_to_write (&is->chan);
261         } else if (chan_mode == NDMCHAN_MODE_READ) {
262                 ndmchan_pending_to_read (&is->chan);
263         } else {
264                 return -3;
265         }
266
267         mine_ep->transfer_mode = chan_mode;
268
269         return 0;
270 }
271
272 int
273 ndmis_data_start (struct ndm_session *sess, int chan_mode)
274 {
275         struct ndm_image_stream *is = &sess->plumb.image_stream;
276         struct ndmis_end_point  *mine_ep = &is->data_ep;
277         struct ndmis_end_point  *peer_ep  = &is->tape_ep;
278
279         return ndmis_ep_start (sess, chan_mode, mine_ep, peer_ep);
280 }
281
282 int
283 ndmis_tape_start (struct ndm_session *sess, int chan_mode)
284 {
285         struct ndm_image_stream *is = &sess->plumb.image_stream;
286         struct ndmis_end_point  *mine_ep = &is->tape_ep;
287         struct ndmis_end_point  *peer_ep  = &is->data_ep;
288
289         return ndmis_ep_start (sess, chan_mode, mine_ep, peer_ep);
290 }
291
292 int
293 ndmis_data_close (struct ndm_session *sess)
294 {
295         struct ndm_image_stream *is = &sess->plumb.image_stream;
296         struct ndmis_end_point  *mine_ep = &is->data_ep;
297         struct ndmis_end_point  *peer_ep  = &is->tape_ep;
298
299         return ndmis_ep_close (sess, mine_ep, peer_ep);
300 }
301
302 int
303 ndmis_tape_close (struct ndm_session *sess)
304 {
305         struct ndm_image_stream *is = &sess->plumb.image_stream;
306         struct ndmis_end_point  *mine_ep = &is->tape_ep;
307         struct ndmis_end_point  *peer_ep  = &is->data_ep;
308
309         return ndmis_ep_close (sess, mine_ep, peer_ep);
310 }
311
312
313
314
315 /*
316  * Quantum -- get a bit of work done
317  ****************************************************************
318  */
319
320 int
321 ndmis_quantum (struct ndm_session *sess)
322 {
323         struct ndm_image_stream *is = &sess->plumb.image_stream;
324         struct ndmis_end_point *mine_ep;
325         int                     rc;
326
327         if (is->remote.connect_status != NDMIS_CONN_LISTEN)
328                 return 0;       /* did nothing */
329
330         if (!is->remote.listen_chan.ready)
331                 return 0;       /* did nothing */
332
333         /* now this is going to get hard */
334
335         if (is->data_ep.connect_status == NDMIS_CONN_LISTEN) {
336                 mine_ep = &is->data_ep;
337                 /* assert (is->tape_ep.connect_status == NDMIS_CONN_REMOTE); */
338         } else if (is->tape_ep.connect_status == NDMIS_CONN_LISTEN) {
339                 mine_ep = &is->tape_ep;
340                 /* assert (is->data_ep.connect_status == NDMIS_CONN_REMOTE); */
341         } else {
342                 assert(0);
343                 return -1;
344         }
345
346         rc = ndmis_tcp_accept (sess);
347         if (rc == 0) {
348                 mine_ep->connect_status = NDMIS_CONN_ACCEPTED;
349                 is->remote.connect_status = NDMIS_CONN_ACCEPTED;
350         } else {
351                 mine_ep->connect_status = NDMIS_CONN_BOTCHED;
352                 is->remote.connect_status = NDMIS_CONN_BOTCHED;
353         }
354
355         return 1;       /* did something */
356 }
357
358
359
360
361 /*
362  * ndmis_end_point oriented helper routines
363  ****************************************************************
364  */
365
366 ndmp9_error
367 ndmis_audit_ep_listen (
368   struct ndm_session *sess,
369   ndmp9_addr_type addr_type,
370   char *reason,
371   struct ndmis_end_point *mine_ep,
372   struct ndmis_end_point *peer_ep)
373 {
374         ndmp9_error             error = NDMP9_NO_ERR;
375         char *                  reason_end;
376
377         sprintf (reason, "IS %s_LISTEN: ", mine_ep->name);
378         reason_end = reason;
379         while (*reason_end) reason_end++;
380
381         if (mine_ep->connect_status != NDMIS_CONN_IDLE) {
382                 sprintf (reason_end, "%s not idle", mine_ep->name);
383                 error = NDMP9_ILLEGAL_STATE_ERR;
384                 goto out;
385         }
386         if (peer_ep->connect_status != NDMIS_CONN_IDLE) {
387                 sprintf (reason_end, "%s not idle", peer_ep->name);
388                 error = NDMP9_ILLEGAL_STATE_ERR;
389                 goto out;
390         }
391
392         switch (addr_type) {
393         case NDMP9_ADDR_LOCAL:
394                 break;
395
396         case NDMP9_ADDR_TCP:
397                 break;
398
399         default:
400                 strcpy (reason_end, "unknown addr_type");
401                 error = NDMP9_ILLEGAL_ARGS_ERR;
402                 goto out;
403         }
404
405   out:
406         if (error == NDMP9_NO_ERR)
407                 strcpy (reason_end, "OK");
408         else
409                 ndmalogf (sess, 0, 2, "listen %s messy mcs=%d pcs=%d",
410                         mine_ep->name,
411                         mine_ep->connect_status,
412                         peer_ep->connect_status);
413
414         return error;
415 }
416
417 ndmp9_error
418 ndmis_audit_ep_connect (
419   struct ndm_session *sess,
420   ndmp9_addr_type addr_type,
421   char *reason,
422   struct ndmis_end_point *mine_ep,
423   struct ndmis_end_point *peer_ep)
424 {
425         ndmp9_error             error = NDMP9_NO_ERR;
426         char *                  reason_end;
427
428         sprintf (reason, "IS %s_CONNECT: ", mine_ep->name);
429         reason_end = reason;
430         while (*reason_end) reason_end++;
431
432         if (mine_ep->connect_status != NDMIS_CONN_IDLE) {
433                 sprintf (reason_end, "%s not idle", mine_ep->name);
434                 error = NDMP9_ILLEGAL_STATE_ERR;
435                 goto out;
436         }
437
438         switch (addr_type) {
439         case NDMP9_ADDR_LOCAL:
440                 if (peer_ep->connect_status != NDMIS_CONN_LISTEN) {
441                         sprintf (reason_end, "LOCAL %s not LISTEN",
442                                         peer_ep->name);
443                         error = NDMP9_ILLEGAL_STATE_ERR;
444                         goto out;
445                 }
446                 if (peer_ep->addr_type != NDMP9_ADDR_LOCAL) {
447                         sprintf (reason_end, "LOCAL %s not LOCAL",
448                                         peer_ep->name);
449                         error = NDMP9_ILLEGAL_STATE_ERR;
450                         goto out;
451                 }
452                 break;
453
454         case NDMP9_ADDR_TCP:
455                 if (peer_ep->connect_status != NDMIS_CONN_IDLE) {
456                         sprintf (reason_end, "LOCAL %s not IDLE",
457                                         peer_ep->name);
458                         error = NDMP9_ILLEGAL_STATE_ERR;
459                         goto out;
460                 }
461                 break;
462
463         default:
464                 strcpy (reason_end, "unknown addr_type");
465                 error = NDMP9_ILLEGAL_ARGS_ERR;
466                 goto out;
467         }
468
469   out:
470         if (error == NDMP9_NO_ERR)
471                 strcpy (reason_end, "OK");
472
473         return error;
474 }
475
476
477 ndmp9_error
478 ndmis_ep_listen (
479   struct ndm_session *sess,
480   ndmp9_addr_type addr_type,
481   ndmp9_addr *ret_addr,
482   char *reason,
483   struct ndmis_end_point *mine_ep,
484   struct ndmis_end_point *peer_ep)
485 {
486         struct ndm_image_stream *is = &sess->plumb.image_stream;
487         char *                  reason_end;
488         ndmp9_error             error;
489
490         error = ndmis_audit_ep_listen (sess, addr_type, reason,
491                                                 mine_ep, peer_ep);
492         if (error != NDMP9_NO_ERR)
493                 return error;
494
495         reason_end = reason;
496         while (*reason_end && *reason_end != ':') reason_end++;
497         *reason_end++ = ':'; *reason_end++ = ' '; *reason_end = 0;
498
499         NDMOS_MACRO_ZEROFILL (ret_addr);
500         ret_addr->addr_type = addr_type;
501
502         switch (addr_type) {
503         case NDMP9_ADDR_LOCAL:
504                 mine_ep->addr_type = NDMP9_ADDR_LOCAL;
505                 mine_ep->connect_status = NDMIS_CONN_LISTEN;
506                 is->remote.connect_status = NDMIS_CONN_EXCLUDE;
507                 break;
508
509         case NDMP9_ADDR_TCP:
510                 if (ndmis_tcp_listen (sess, ret_addr) != 0) {
511                         strcpy (reason_end, "TCP listen() failed");
512                         error = NDMP9_CONNECT_ERR;
513                         goto out;
514                 }
515                 mine_ep->addr_type = NDMP9_ADDR_TCP;
516                 mine_ep->connect_status = NDMIS_CONN_LISTEN;
517                 peer_ep->connect_status = NDMIS_CONN_REMOTE;
518                 break;
519
520         default:
521                 reason = "unknown addr_type (bad)";
522                 error = NDMP9_ILLEGAL_ARGS_ERR;
523                 goto out;
524         }
525
526   out:
527         if (error == NDMP9_NO_ERR)
528                 strcpy (reason_end, "OK");
529
530         return error;
531 }
532
533 ndmp9_error
534 ndmis_ep_connect (
535   struct ndm_session *sess,
536   ndmp9_addr *addr,
537   char *reason,
538   struct ndmis_end_point *mine_ep,
539   struct ndmis_end_point *peer_ep)
540 {
541         struct ndm_image_stream *is = &sess->plumb.image_stream;
542         ndmp9_addr_type         addr_type = addr->addr_type;
543         char *                  reason_end;
544         ndmp9_error             error;
545
546         error = ndmis_audit_ep_connect (sess, addr_type, reason,
547                                                 mine_ep, peer_ep);
548         if (error != NDMP9_NO_ERR)
549                 return error;
550
551         reason_end = reason;
552         while (*reason_end && *reason_end != ':') reason_end++;
553         *reason_end++ = ':'; *reason_end++ = ' '; *reason_end = 0;
554
555         switch (addr_type) {
556         case NDMP9_ADDR_LOCAL:
557                 mine_ep->addr_type = NDMP9_ADDR_LOCAL;
558                 mine_ep->connect_status = NDMIS_CONN_CONNECTED;
559                 peer_ep->connect_status = NDMIS_CONN_ACCEPTED;
560                 is->remote.connect_status = NDMIS_CONN_EXCLUDE;
561                 break;
562
563         case NDMP9_ADDR_TCP:
564                 if (ndmis_tcp_connect (sess, addr) != 0) {
565                         strcpy (reason_end, "TCP connect() failed");
566                         error = NDMP9_CONNECT_ERR;
567                         goto out;
568                 }
569                 mine_ep->addr_type = NDMP9_ADDR_TCP;
570                 mine_ep->connect_status = NDMIS_CONN_CONNECTED;
571                 peer_ep->connect_status = NDMIS_CONN_REMOTE;
572                 break;
573
574         default:
575                 reason = "unknown addr_type (bad)";
576                 error = NDMP9_ILLEGAL_ARGS_ERR;
577                 goto out;
578         }
579
580   out:
581         return error;
582 }
583
584 int
585 ndmis_ep_close (struct ndm_session *sess,
586   struct ndmis_end_point *mine_ep, struct ndmis_end_point *peer_ep)
587 {
588         struct ndm_image_stream *is = &sess->plumb.image_stream;
589         char *                  save_name = mine_ep->name;
590
591         switch (mine_ep->connect_status) {
592         case NDMIS_CONN_IDLE:
593                 return 0;
594
595         case NDMIS_CONN_BOTCHED:
596         case NDMIS_CONN_REMOTE:
597         case NDMIS_CONN_EXCLUDE:
598                 goto messy;
599
600         case NDMIS_CONN_LISTEN:
601                 switch (mine_ep->addr_type) {
602                 default:
603                         goto messy;
604
605                 case NDMP9_ADDR_LOCAL:
606                         ndmis_reinit_remote (sess);
607                         if (peer_ep->connect_status != NDMIS_CONN_IDLE)
608                                 goto messy;
609                         break;
610
611                 case NDMP9_ADDR_TCP:
612                         ndmis_tcp_close (sess);
613                         if (peer_ep->connect_status != NDMIS_CONN_REMOTE)
614                                 goto messy;
615                         peer_ep->connect_status = NDMIS_CONN_IDLE;
616                         break;
617                 }
618                 break;
619
620         case NDMIS_CONN_ACCEPTED:
621                 switch (mine_ep->addr_type) {
622                 default:
623                         goto messy;
624
625                 case NDMP9_ADDR_LOCAL:
626                         if (peer_ep->connect_status != NDMIS_CONN_CONNECTED)
627                                 goto messy;
628                         peer_ep->connect_status = NDMIS_CONN_DISCONNECTED;
629                         is->chan.eof = 1;
630                         if (mine_ep->transfer_mode == NDMCHAN_MODE_READ)
631                                 is->chan.error = 1; /* EPIPE */
632                         break;
633
634                 case NDMP9_ADDR_TCP:
635                         ndmis_tcp_close (sess);
636                         if (peer_ep->connect_status != NDMIS_CONN_REMOTE)
637                                 goto messy;
638                         peer_ep->connect_status = NDMIS_CONN_IDLE;
639                         break;
640                 }
641                 break;
642
643         case NDMIS_CONN_CONNECTED:
644                 switch (mine_ep->addr_type) {
645                 default:
646                         goto messy;
647
648                 case NDMP9_ADDR_LOCAL:
649                         if (peer_ep->connect_status != NDMIS_CONN_ACCEPTED)
650                                 goto messy;
651                         peer_ep->connect_status = NDMIS_CONN_DISCONNECTED;
652                         is->chan.eof = 1;
653                         if (mine_ep->transfer_mode == NDMCHAN_MODE_READ)
654                                 is->chan.error = 1; /* EPIPE */
655                         break;
656
657                 case NDMP9_ADDR_TCP:
658                         ndmis_tcp_close (sess);
659                         if (peer_ep->connect_status != NDMIS_CONN_REMOTE)
660                                 goto messy;
661                         peer_ep->connect_status = NDMIS_CONN_IDLE;
662                         break;
663                 }
664                 break;
665
666         case NDMIS_CONN_DISCONNECTED:   /* peer close()d first */
667                 ndmis_reinit_remote (sess);
668                 break;
669
670         case NDMIS_CONN_CLOSED:
671                 goto messy;
672         }
673
674         NDMOS_MACRO_ZEROFILL (mine_ep);
675         mine_ep->name = save_name;
676
677         return 0;
678
679   messy:
680         ndmalogf (sess, 0, 2, "close %s messy mcs=%d pcs=%d",
681                         mine_ep->name,
682                         mine_ep->connect_status,
683                         peer_ep->connect_status);
684         NDMOS_MACRO_ZEROFILL (mine_ep);
685         mine_ep->name = save_name;
686         return -1;
687 }
688
689 #if 0
690 ndmp9_error
691 ndmis_ep_could_start (
692   struct ndm_session *sess,
693   int want_transfer_mode,       /* NDMCHAN_MODE_{IDLE|READ|WRITE} */
694   char *reason,
695   struct ndmis_end_point *mine_ep,
696   struct ndmis_end_point *peer_ep)
697 {
698         ndmp9_error             error = NDMP9_NO_ERR;
699         char *                  reason_end;
700
701         sprintf (reason, "IS %s_START: ", mine_ep->name);
702         reason_end = reason;
703         while (*reason_end) reason_end++;
704
705         if (want_transfer_mode == NDMCHAN_MODE_IDLE)
706                 want_transfer_mode = mine_ep->transfer_mode;
707
708         switch (want_transfer_mode) {
709         case NDMCHAN_MODE_IDLE:
710                 /* can't check much  */
711                 break;
712
713         case NDMCHAN_MODE_READ:
714                 /* what to check? */
715                 break;
716
717         case NDMCHAN_MODE_WRITE:
718                 /* what to check? */
719                 break;
720
721         default:
722                 strcpy (reason_end, "unknown chan_mode");
723                 error = NDMP9_ILLEGAL_ARGS_ERR;
724                 goto out;
725         }
726
727   out:
728         if (error == NDMP9_NO_ERR)
729                 strcpy (reason_end, "OK");
730
731         return error;
732 }
733 #endif /* 0 */
734
735
736
737
738 /*
739  * ADDR_TCP helper routines
740  ****************************************************************
741  */
742
743
744 /*
745  * ndmis_tcp_listen()
746  *
747  * The tricky part of listen()ing is determining the IP
748  * address to offer, which ultimately will be used by
749  * the other (peer) side for connect()ing.
750  *
751  * We can't just bind() with INADDR_ANY (0's) because
752  * that results in a local socket with INADDR_ANY, and
753  * any inbound connection to the right port will be
754  * accept()ed by the networking system. That doesn't
755  * help us here, though, because we have to have a
756  * real IP address to offer. INADDR_ANY ain't sufficient.
757  *
758  * There is also the issue of systems with multiple
759  * network connections. We of course would like to
760  * use the network data link that is most advantageous
761  * on both sides. This may vary from job run to job
762  * run, and so any method of specifying just one
763  * is IP address ain't sufficient.
764  *
765  * The approach here uses the existing control connections,
766  * which normally precede the image stream connection,
767  * as cues for which IP address to use. So, for example,
768  * if a TAPE or DATA host has four network connections,
769  * the CONTROL agent can coax them to use a specific one
770  * of the four by connecting to the IP address of the
771  * network wanted for the image stream.
772  *
773  * If the clever rules don't work out, the fallback is to
774  * look up the host name. Right now we use ndmhost_lookup()
775  * of sess->local_info.host_name because both must work
776  * before things would progress to this point.
777  */
778
779 int
780 ndmis_tcp_listen (struct ndm_session *sess, struct ndmp9_addr *listen_addr)
781 {
782         struct ndm_image_stream *is = &sess->plumb.image_stream;
783         ndmp9_tcp_addr *        tcp_addr = &listen_addr->ndmp9_addr_u.tcp_addr;
784         struct ndmconn *        conn;
785         struct sockaddr         c_sa;
786         struct sockaddr         l_sa;
787         struct sockaddr_in *    sin;
788         socklen_t               len;
789         int                     listen_sock = -1;
790         char *                  what = "???";
791
792         /*
793          * Get the IP address thru which the CONTROL agent connected
794          * to this session. The CONTROL agent may influence the
795          * network used for the image-stream on multi-homed hosts
796          * simply by connecting to the prefered IP address.
797          */
798         what = "determine-conn";
799         conn = sess->plumb.control;
800         if (!conn || conn->conn_type != NDMCONN_TYPE_REMOTE) {
801                 /*
802                  * If CONTROL is resident, try the other
803                  * control connections in hopes of finding
804                  * a clue about what IP address to offer.
805                  */
806                 conn = sess->plumb.data;
807                 if (!conn || conn->conn_type != NDMCONN_TYPE_REMOTE) {
808                         conn = sess->plumb.tape;
809                         if (!conn || conn->conn_type != NDMCONN_TYPE_REMOTE) {
810                                 conn = 0;
811                         }
812                 }
813         }
814
815         if (conn) {
816                 /*
817                  * We found a connection to use for determining
818                  * what IP address to offer.
819                  */
820                 what = "getsockname-ctrl";
821                 len = sizeof c_sa;
822                 if (getsockname (ndmconn_fileno(conn), &c_sa, &len) < 0) {
823                         /* we'll try the fallback rules */
824                         conn = 0;
825                 }
826         }
827
828         if (!conn) {
829                 /*
830                  * For whatever reason, we can't determine a good
831                  * IP address from the connections. Try the boring
832                  * fallback rules.
833                  */
834                 ndmos_sync_config_info (sess);
835
836                 sin = (struct sockaddr_in *) &c_sa;
837
838                 what = "ndmhost_lookup";
839                 if (ndmhost_lookup (sess->config_info.hostname, sin) != 0)
840                         goto fail;
841         }
842
843         /* c_sa is a sockaddr_in for the IP address to use */
844
845         what = "socket";
846         listen_sock = socket (AF_INET, SOCK_STREAM, 0);
847         if (listen_sock < 0) goto fail;
848
849         /* could bind() to more restrictive addr based on c_sa */
850         NDMOS_MACRO_SET_SOCKADDR(&l_sa, 0, 0);
851         what = "bind";
852         if (bind (listen_sock, &l_sa, sizeof l_sa) < 0) goto fail;
853
854         what = "listen";
855         if (listen (listen_sock, 1) < 0) goto fail;
856
857         ndmos_condition_listen_socket (sess, listen_sock);
858
859         /* Get the port */
860         what = "getsockname-listen";
861         len = sizeof l_sa;
862         if (getsockname (listen_sock, &l_sa, &len) < 0) goto fail;
863
864         /*
865          * Fill in the return address
866          */
867
868         listen_addr->addr_type = NDMP9_ADDR_TCP;
869         tcp_addr = &listen_addr->ndmp9_addr_u.tcp_addr;
870
871         /* IP addr from CONTROL connection, or where ever c_sa came from */
872         sin = (struct sockaddr_in *) &c_sa;
873         tcp_addr->ip_addr = ntohl (sin->sin_addr.s_addr);
874
875         /* port from the bind() and getsockname() above */
876         sin = (struct sockaddr_in *) &l_sa;
877         tcp_addr->port = ntohs (sin->sin_port);
878
879         /*
880          * Start the listen channel
881          */
882
883         ndmchan_start_listen (&is->remote.listen_chan, listen_sock);
884
885         is->remote.connect_status = NDMIS_CONN_LISTEN;
886         is->remote.listen_addr = *listen_addr;
887
888         return 0;
889
890   fail:
891         ndmalogf (sess, 0, 2, "ndmis_tcp_listen(): %s failed", what);
892         if (listen_sock >= 0) close (listen_sock);
893
894         return -1;
895 }
896
897 int
898 ndmis_tcp_accept (struct ndm_session *sess)
899 {
900         struct ndm_image_stream *is = &sess->plumb.image_stream;
901         char *                  what = "???";
902         ndmp9_tcp_addr *        tcp_addr;
903         struct sockaddr         sa;
904         struct sockaddr_in *    sin = (struct sockaddr_in *) &sa;
905         socklen_t               len;
906         int                     accept_sock = -1;
907
908         what = "remote-conn-stat";
909         if (is->remote.connect_status != NDMIS_CONN_LISTEN)
910                 goto fail;
911
912         what = "remote-list-ready";
913         if (!is->remote.listen_chan.ready)
914                 goto fail;
915
916         what = "accept";
917         len = sizeof sa;
918         accept_sock = accept (is->remote.listen_chan.fd, &sa, &len);
919
920         ndmchan_cleanup (&is->remote.listen_chan);
921
922         if (accept_sock < 0) {
923                 is->remote.connect_status = NDMIS_CONN_BOTCHED;
924                 goto fail;
925         }
926
927         /* write what we know, ndmis...addrs() will update if possible */
928         is->remote.peer_addr.addr_type = NDMP9_ADDR_TCP;
929         tcp_addr = &is->remote.peer_addr.ndmp9_addr_u.tcp_addr;
930         tcp_addr->ip_addr = ntohl (sin->sin_addr.s_addr);
931         tcp_addr->port = ntohs (sin->sin_port);
932
933         ndmis_tcp_green_light (sess, accept_sock, NDMIS_CONN_ACCEPTED);
934
935         return 0;
936
937   fail:
938         ndmalogf (sess, 0, 2, "ndmis_tcp_accept(): %s failed", what);
939         if (accept_sock >= 0) close (accept_sock);
940
941         return -1;
942 }
943
944 int
945 ndmis_tcp_connect (struct ndm_session *sess, struct ndmp9_addr *connect_addr)
946 {
947         struct ndm_image_stream *is = &sess->plumb.image_stream;
948         ndmp9_tcp_addr *        tcp_addr=&connect_addr->ndmp9_addr_u.tcp_addr;
949         char *                  what = "???";
950         struct sockaddr         sa;
951         int                     connect_sock;
952
953         NDMOS_MACRO_SET_SOCKADDR (&sa, tcp_addr->ip_addr, tcp_addr->port);
954
955         what = "socket";
956         connect_sock = socket (AF_INET, SOCK_STREAM, 0);
957         if (connect_sock < 0)
958                 goto fail;
959
960         what = "connect";
961         if (connect (connect_sock, &sa, sizeof sa) < 0)
962                 goto fail;
963
964
965         /* write what we know, ndmis...addrs() will update if possible */
966         is->remote.peer_addr = *connect_addr;
967
968         ndmis_tcp_green_light (sess, connect_sock, NDMIS_CONN_CONNECTED);
969
970         return 0;
971
972   fail:
973         ndmalogf (sess, 0, 2, "ndmis_tcp_connect(): %s failed", what);
974         if (connect_sock >= 0) close (connect_sock);
975
976         return -1;
977 }
978
979 int
980 ndmis_tcp_green_light (struct ndm_session *sess, int sock,
981   ndmis_connect_status new_status)
982 {
983         struct ndm_image_stream *is = &sess->plumb.image_stream;
984
985         ndmos_condition_image_stream_socket (sess, sock);
986
987         ndmchan_start_pending (&is->chan, sock);
988
989         is->remote.connect_status = new_status;
990
991         ndmis_tcp_get_local_and_peer_addrs (sess);
992
993         return 0;
994 }
995
996 int
997 ndmis_tcp_close (struct ndm_session *sess)
998 {
999         struct ndm_image_stream *is = &sess->plumb.image_stream;
1000
1001         switch (is->remote.connect_status) {
1002         case NDMIS_CONN_LISTEN:
1003                 ndmchan_cleanup (&is->remote.listen_chan);
1004                 break;
1005
1006         case NDMIS_CONN_CONNECTED:
1007         case NDMIS_CONN_ACCEPTED:
1008                 ndmchan_cleanup (&is->chan);
1009                 break;
1010
1011         default:
1012                 break;
1013         }
1014
1015         ndmis_reinit_remote (sess);
1016
1017         return 0;
1018 }
1019
1020 int
1021 ndmis_tcp_get_local_and_peer_addrs (struct ndm_session *sess)
1022 {
1023         struct ndm_image_stream *is = &sess->plumb.image_stream;
1024         char *                  what = "???";
1025         struct sockaddr         sa;
1026         struct sockaddr_in *    sin = (struct sockaddr_in *) &sa;
1027         ndmp9_tcp_addr *        tcp_addr;
1028         socklen_t               len;
1029         int                     rc = 0;
1030
1031         len = sizeof sa;
1032         what = "getpeername";
1033         if (getpeername (is->chan.fd, &sa, &len) < 0) {
1034                 /* this is best effort */
1035                 ndmalogf (sess, 0, 2, "ndmis_tcp..._addrs(): %s failed", what);
1036                 rc = -1;
1037         } else {
1038                 is->remote.peer_addr.addr_type = NDMP9_ADDR_TCP;
1039                 tcp_addr = &is->remote.peer_addr.ndmp9_addr_u.tcp_addr;
1040                 tcp_addr->ip_addr = ntohl (sin->sin_addr.s_addr);
1041                 tcp_addr->port = ntohs (sin->sin_port);
1042         }
1043
1044         len = sizeof sa;
1045         what = "getsockname";
1046         if (getsockname (is->chan.fd, &sa, &len) < 0) {
1047                 /* this is best effort */
1048                 ndmalogf (sess, 0, 2, "ndmis_tcp..._addrs(): %s failed", what);
1049                 rc = -1;
1050         } else {
1051                 is->remote.local_addr.addr_type = NDMP9_ADDR_TCP;
1052                 tcp_addr = &is->remote.peer_addr.ndmp9_addr_u.tcp_addr;
1053                 tcp_addr->ip_addr = ntohl (sin->sin_addr.s_addr);
1054                 tcp_addr->port = ntohs (sin->sin_port);
1055         }
1056
1057         return rc;
1058 }
1059