Imported Upstream version 3.1.0
[debian/amanda] / ndmp-src / ndml_chan.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
37
38 #include "ndmlib.h"
39
40
41
42 /*
43  * Initialize a channel. Make sure it won't be confused for active.
44  */
45 void
46 ndmchan_initialize (struct ndmchan *ch, char *name)
47 {
48         NDMOS_MACRO_ZEROFILL(ch);
49         ch->name = name ? name : "???";
50         ch->fd = -1;
51         ch->mode = NDMCHAN_MODE_IDLE;
52 }
53
54 /*
55  * Set the data buffer
56  */
57 int
58 ndmchan_setbuf (struct ndmchan *ch, char *data, unsigned data_size)
59 {
60         ch->data = data;
61         ch->data_size = data_size;
62
63         ch->beg_ix = 0;
64         ch->end_ix = 0;
65
66         return 0;
67 }
68
69
70
71
72 /*
73  * Interfaces for starting a channel in various modes.
74  */
75 int
76 ndmchan_start_mode (struct ndmchan *ch, int fd, int chan_mode)
77 {
78         ch->fd = fd;
79         ch->mode = chan_mode;
80         return 0;
81 }
82
83 int
84 ndmchan_start_read (struct ndmchan *ch, int fd)
85 {
86         return ndmchan_start_mode (ch, fd, NDMCHAN_MODE_READ);
87 }
88
89 int
90 ndmchan_start_write (struct ndmchan *ch, int fd)
91 {
92         return ndmchan_start_mode (ch, fd, NDMCHAN_MODE_WRITE);
93 }
94
95 int
96 ndmchan_start_readchk (struct ndmchan *ch, int fd)
97 {
98         return ndmchan_start_mode (ch, fd, NDMCHAN_MODE_READCHK);
99 }
100
101 int
102 ndmchan_start_listen (struct ndmchan *ch, int fd)
103 {
104         return ndmchan_start_mode (ch, fd, NDMCHAN_MODE_LISTEN);
105 }
106
107 int
108 ndmchan_start_resident (struct ndmchan *ch)
109 {
110         return ndmchan_start_mode (ch, -1, NDMCHAN_MODE_RESIDENT);
111 }
112
113 int
114 ndmchan_start_pending (struct ndmchan *ch, int fd)
115 {
116         return ndmchan_start_mode (ch, fd, NDMCHAN_MODE_PENDING);
117 }
118
119 /*
120  * Change a PENDING channel to an active (READ/WRITE) channel
121  */
122 int
123 ndmchan_pending_to_mode (struct ndmchan *ch, int chan_mode)
124 {
125         ch->mode = chan_mode;
126         return 0;
127 }
128
129 int
130 ndmchan_pending_to_read (struct ndmchan *ch)
131 {
132         return ndmchan_pending_to_mode (ch, NDMCHAN_MODE_READ);
133 }
134
135 int
136 ndmchan_pending_to_write (struct ndmchan *ch)
137 {
138         return ndmchan_pending_to_mode (ch, NDMCHAN_MODE_WRITE);
139 }
140
141
142
143
144 /*
145  * Interfaces for stopping (close()ing) a channel.
146  * This is a bit of a hodge-podge. Could probably be cleaner.
147  */
148
149 void
150 ndmchan_set_eof (struct ndmchan *ch)
151 {
152         ch->eof = 1;
153 }
154
155 void
156 ndmchan_close_set_errno (struct ndmchan *ch, int err_no)
157 {
158         ch->eof = 1;
159         if (ch->fd >= 0) {
160                 close (ch->fd);
161                 ch->fd = -1;
162         }
163         ch->mode = NDMCHAN_MODE_CLOSED;
164         ch->saved_errno = err_no;
165         ch->beg_ix = ch->end_ix = 0;
166 }
167
168 void
169 ndmchan_close (struct ndmchan *ch) {
170         ndmchan_close_set_errno (ch, 0);
171 }
172
173 void
174 ndmchan_abort (struct ndmchan *ch) {
175         ndmchan_close_set_errno (ch,
176                 ch->saved_errno == 0 ? EINTR : ch->saved_errno);
177 }
178
179 void
180 ndmchan_close_as_is (struct ndmchan *ch) {
181         ndmchan_close_set_errno (ch, ch->saved_errno);
182 }
183
184 void
185 ndmchan_cleanup (struct ndmchan *ch) {
186         if (ch->mode != NDMCHAN_MODE_IDLE) {
187                 ndmchan_close_as_is (ch);
188         }
189 }
190
191
192
193
194 /*
195  * CPU Quantum for a set of channels. There are three
196  * phases:
197  * 1) Identify the channels to check for ready to do I/O.
198  *    For example, a READ channel with no buffer space
199  *    need not be checked.
200  * 2) Call the OS dependent function that performs the
201  *    actual select()/poll()/whatever.
202  * 3) Based on the results, perform actual read()/write().
203  *    EOF and errors are detected.
204  *
205  * This is constructed so that applications which can not use
206  * ndmchan_quantum() directly have access to the helper functions
207  * ndmchan_pre_poll() and ndmchan_post_poll().
208  */
209
210 int
211 ndmchan_quantum (struct ndmchan *chtab[], unsigned n_chtab, int milli_timo)
212 {
213         int                     rc;
214
215         ndmchan_pre_poll (chtab, n_chtab);
216
217         rc = ndmos_chan_poll (chtab, n_chtab, milli_timo);
218         if (rc <= 0)
219                 return rc;
220
221         rc = ndmchan_post_poll (chtab, n_chtab);
222
223         return rc;
224 }
225
226 int
227 ndmchan_pre_poll (struct ndmchan *chtab[], unsigned n_chtab)
228 {
229         struct ndmchan *        ch;
230         unsigned int            i, n_check;
231
232         n_check = 0;
233         for (i = 0; i < n_chtab; i++) {
234                 ch = chtab[i];
235                 ch->ready = 0;
236                 ch->check = 0;
237
238                 if (ch->error)
239                         continue;
240
241                 switch (ch->mode) {
242                 default:
243                 case NDMCHAN_MODE_IDLE:
244                 case NDMCHAN_MODE_PENDING:
245                 case NDMCHAN_MODE_RESIDENT:
246                 case NDMCHAN_MODE_CLOSED:
247                         continue;
248
249                 case NDMCHAN_MODE_LISTEN:
250                 case NDMCHAN_MODE_READCHK:
251                         break;
252
253                 case NDMCHAN_MODE_READ:
254                         if (ch->eof)
255                                 continue;
256                         if (ndmchan_n_avail (ch) == 0)
257                                 continue;
258                         break;
259
260                 case NDMCHAN_MODE_WRITE:
261                         if (ndmchan_n_ready (ch) == 0)
262                                 continue;
263                         break;
264                 }
265
266                 ch->check = 1;
267                 n_check++;
268         }
269
270         return n_check;
271 }
272
273 int
274 ndmchan_post_poll (struct ndmchan *chtab[], unsigned n_chtab)
275 {
276         struct ndmchan *        ch;
277         unsigned int            i;
278         int                     rc, len, n_ready;
279
280         n_ready = 0;
281
282         for (i = 0; i < n_chtab; i++) {
283                 ch = chtab[i];
284
285                 if (!ch->ready)
286                         continue;
287
288                 switch (ch->mode) {
289                 case NDMCHAN_MODE_READ:
290                         len = ndmchan_n_avail (ch);
291                         if (len <= 0) continue;
292
293                         n_ready++;
294                         rc = read (ch->fd, &ch->data[ch->end_ix], len);
295                         if (rc < 0) {
296                                 if (errno != NDMOS_CONST_EWOULDBLOCK) {
297                                         ch->error = ch->eof = 1;
298                                         ch->saved_errno = errno;
299                                         if (!ch->saved_errno)
300                                                 ch->saved_errno = -1;
301                                 } else {
302                                         /* no bytes read */
303                                 }
304                         } else if (rc == 0) {
305                                 ch->eof = 1;
306                                 ch->error = 0;
307                                 ch->saved_errno = 0;
308                         } else {
309                                 ch->end_ix += rc;
310                         }
311                         break;
312
313                 case NDMCHAN_MODE_WRITE:
314                         len = ndmchan_n_ready (ch);
315                         if (len <= 0) continue;
316
317                         n_ready++;
318                         rc = write (ch->fd, &ch->data[ch->beg_ix], len);
319                         if (rc < 0) {
320                                 if (errno != NDMOS_CONST_EWOULDBLOCK) {
321                                         ch->eof = 1;
322                                         ch->error = 1;
323                                         ch->saved_errno = errno;
324                                         if (!ch->saved_errno)
325                                                 ch->saved_errno = -1;
326                                 } else {
327                                         /* no bytes written */
328                                         /* EWOULDBLOCK but ready? */
329                                 }
330                         } else if (rc == 0) {
331                                 /* NDMOS_CONST_EWOULDBLOCK? */
332                                 ch->eof = 1;
333                                 ch->error = 1;
334                                 ch->saved_errno = 0;
335                         } else {
336                                 ch->beg_ix += rc;
337                         }
338                         break;
339                 }
340         }
341
342         return n_ready;
343 }
344
345
346
347
348 /*
349  * Channel data buffer space manipulation.
350  */
351
352 void
353 ndmchan_compress (struct ndmchan *ch) {
354         unsigned        len = ch->end_ix - ch->beg_ix;
355
356         if (ch->beg_ix > 0 && len > 0) {
357                 bcopy (&ch->data[ch->beg_ix], ch->data, len);
358         } else {
359                 if (len > ch->data_size)
360                         len = 0;
361         }
362         ch->beg_ix = 0;
363         ch->end_ix = len;
364 }
365
366 int
367 ndmchan_n_avail (struct ndmchan *ch) {
368         if (ch->end_ix == ch->beg_ix)
369                 ch->end_ix = ch->beg_ix = 0;
370
371         if (ch->end_ix >= ch->data_size) {
372                 ndmchan_compress (ch);
373         }
374         return ch->data_size - ch->end_ix;
375 }
376
377 int
378 ndmchan_n_avail_total (struct ndmchan *ch) {
379         if (ch->end_ix == ch->beg_ix)
380                 ch->end_ix = ch->beg_ix = 0;
381
382         if (ch->end_ix >= ch->data_size) {
383                 ndmchan_compress (ch);
384         }
385         return ch->data_size - ch->end_ix + ch->beg_ix;
386 }
387
388 int
389 ndmchan_n_ready (struct ndmchan *ch) {
390         return ch->end_ix - ch->beg_ix;
391 }
392
393
394
395
396 /*
397  * Interfaces for interpreting channel state, obtaining pointers, lengths
398  */
399
400 enum ndmchan_read_interpretation
401 ndmchan_read_interpret (struct ndmchan *ch, char **data_p,
402   unsigned *n_ready_p)
403 {
404         unsigned        n_ready;
405
406         n_ready = *n_ready_p = ndmchan_n_ready (ch);
407         *data_p = &ch->data[ch->beg_ix];
408
409         if (ch->error) {
410                 if (n_ready == 0) {
411                         return NDMCHAN_RI_DONE_ERROR;
412                 } else {
413                         return NDMCHAN_RI_DRAIN_ERROR;
414                 }
415         }
416
417         if (ch->eof) {
418                 if (n_ready == 0) {
419                         return NDMCHAN_RI_DONE_EOF;
420                 } else {
421                         return NDMCHAN_RI_DRAIN_EOF;
422                 }
423         }
424
425         if (n_ready == 0) {
426                 return NDMCHAN_RI_EMPTY;
427         }
428
429         if (n_ready == ch->data_size) {
430                 return NDMCHAN_RI_READY_FULL;
431         }
432
433         return NDMCHAN_RI_READY;
434 }
435
436 enum ndmchan_write_interpretation
437 ndmchan_write_interpret (struct ndmchan *ch, char **data_p,
438   unsigned *n_avail_p)
439 {
440         unsigned        n_avail;
441
442         n_avail = *n_avail_p = ndmchan_n_avail (ch);
443         *data_p = &ch->data[ch->end_ix];
444
445         if (ch->error) {
446                 /* We don't use WI_DRAIN_ERROR. If it's kaput, it's kaput */
447                 return NDMCHAN_WI_DONE_ERROR;
448         }
449
450         if (ch->eof) {
451                 if (n_avail == ch->data_size) {
452                         return NDMCHAN_WI_DONE_EOF;
453                 } else {
454                         return NDMCHAN_WI_DRAIN_EOF;
455                 }
456         }
457
458         if (n_avail == 0) {
459                 return NDMCHAN_WI_FULL;
460         }
461
462         if (n_avail == ch->data_size) {
463                 return NDMCHAN_WI_AVAIL_EMPTY;
464         }
465
466         return NDMCHAN_WI_AVAIL;
467 }
468
469
470
471
472 /*
473  * Pretty printer
474  */
475 void
476 ndmchan_pp (struct ndmchan *ch, char *buf)
477 {
478         int             show_ra = 0;
479         char *          bp = buf;
480         char *          p;
481
482         sprintf (bp, "name=%s", ch->name); while (*bp) bp++;
483
484         switch (ch->mode) {
485         case NDMCHAN_MODE_IDLE:         p = "idle"; break;
486         case NDMCHAN_MODE_RESIDENT:     p = "resident"; show_ra = 1; break;
487         case NDMCHAN_MODE_READ:         p = "read"; show_ra = 1; break;
488         case NDMCHAN_MODE_WRITE:        p = "write"; show_ra = 1; break;
489         case NDMCHAN_MODE_READCHK:      p = "readchk"; break;
490         case NDMCHAN_MODE_LISTEN:       p = "listen"; break;
491         case NDMCHAN_MODE_PENDING:      p = "pending"; break;
492         case NDMCHAN_MODE_CLOSED:       p = "closed"; break;
493         default:                        p = "mode=???"; break;
494         }
495
496         sprintf (bp, " %s ", p);
497         while (*bp) bp++;
498
499         if (show_ra) {
500                 sprintf (bp, "ready=%d avail=%d ",
501                         ndmchan_n_ready(ch), ndmchan_n_avail(ch));
502                 while (*bp) bp++;
503         }
504
505         if (ch->ready)  strcat (bp, "-rdy");
506         if (ch->check)  strcat (bp, "-chk");
507         if (ch->eof)    strcat (bp, "-eof");
508         if (ch->error)  strcat (bp, "-err");
509 }
510
511
512
513
514 #ifdef NDMOS_OPTION_USE_SELECT_FOR_CHAN_POLL
515 /*
516  * Here because it is almost always used
517  */
518
519 int
520 ndmos_chan_poll (struct ndmchan *chtab[], unsigned n_chtab, int milli_timo)
521 {
522         struct ndmchan *        ch;
523         fd_set                  rfds, wfds;
524         int                     nfd = 0, rc;
525         unsigned                i;
526         struct timeval          timo;
527
528         FD_ZERO(&rfds);
529         FD_ZERO(&wfds);
530
531         timo.tv_sec = milli_timo / 1000;
532         timo.tv_usec = (milli_timo%1000) * 1000;
533
534         for (i = 0; i < n_chtab; i++) {
535                 ch = chtab[i];
536                 if (!ch->check)
537                         continue;
538
539                 switch (ch->mode) {
540                 case NDMCHAN_MODE_LISTEN:
541                 case NDMCHAN_MODE_READCHK:
542                 case NDMCHAN_MODE_READ:
543                         FD_SET (ch->fd, &rfds);
544                         break;
545
546                 case NDMCHAN_MODE_WRITE:
547                         FD_SET (ch->fd, &wfds);
548                         break;
549                 }
550                 if (nfd < ch->fd+1)
551                         nfd = ch->fd+1;
552         }
553
554         rc = select (nfd, &rfds, &wfds, (void*)0, &timo);
555         if (rc <= 0)
556                 return rc;
557
558         for (i = 0; i < n_chtab; i++) {
559                 ch = chtab[i];
560                 if (!ch->check)
561                         continue;
562
563                 switch (ch->mode) {
564                 case NDMCHAN_MODE_LISTEN:
565                 case NDMCHAN_MODE_READCHK:
566                 case NDMCHAN_MODE_READ:
567                         if (FD_ISSET (ch->fd, &rfds))
568                                 ch->ready = 1;
569                         break;
570
571                 case NDMCHAN_MODE_WRITE:
572                         if (FD_ISSET (ch->fd, &wfds))
573                                 ch->ready = 1;
574                         break;
575                 }
576         }
577
578         return rc;
579 }
580
581 #endif /* NDMOS_OPTION_USE_SELECT_FOR_CHAN_POLL */
582
583 #ifdef NDMOS_OPTION_USE_POLL_FOR_CHAN_POLL
584 /*
585  * Here because it is common, and because poll(2) is
586  * INFINITELY SUPERIOR to select(2).
587  */
588
589 int
590 ndmos_chan_poll (struct ndmchan *chtab[], unsigned n_chtab, int milli_timo)
591 {
592         struct ndmchan *        ch;
593         struct pollfd           pfdtab[20];
594         int                     n_pfdtab = 0;
595         int                     rc, i;
596
597         NDMOS_MACRO_ZEROFILL (pfdtab);
598
599         for (i = 0; i < n_chtab; i++) {
600                 ch = chtab[i];
601                 if (!ch->check)
602                         continue;
603
604                 switch (ch->mode) {
605                 case NDMCHAN_MODE_LISTEN:
606                 case NDMCHAN_MODE_READCHK:
607                 case NDMCHAN_MODE_READ:
608                         pfdtab[n_pfdtab].fd = ch->fd;
609                         pfdtab[n_pfdtab].events = POLLIN;
610                         break;
611
612                 case NDMCHAN_MODE_WRITE:
613                         pfdtab[n_pfdtab].fd = ch->fd;
614                         pfdtab[n_pfdtab].events = POLLOUT;
615                         break;
616                 }
617                 n_pfdtab++;
618         }
619
620         rc = poll (pfdtab, n_pfdtab, milli_timo);
621
622         @@@ TODO: post them back. Not easy @@@
623
624         return rc;
625 }
626
627 #endif /* NDMOS_OPTION_USE_POLL_FOR_CHAN_POLL */
628