Imported Upstream version 3.3.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_record (struct ndmchan *ch, unsigned long size) {
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 - size) {
383                 ndmchan_compress (ch);
384         }
385         return ch->data_size - ch->end_ix;
386 }
387
388 int
389 ndmchan_n_avail_total (struct ndmchan *ch) {
390         if (ch->end_ix == ch->beg_ix)
391                 ch->end_ix = ch->beg_ix = 0;
392
393         if (ch->end_ix >= ch->data_size) {
394                 ndmchan_compress (ch);
395         }
396         return ch->data_size - ch->end_ix + ch->beg_ix;
397 }
398
399 int
400 ndmchan_n_ready (struct ndmchan *ch) {
401         return ch->end_ix - ch->beg_ix;
402 }
403
404
405
406
407 /*
408  * Interfaces for interpreting channel state, obtaining pointers, lengths
409  */
410
411 enum ndmchan_read_interpretation
412 ndmchan_read_interpret (struct ndmchan *ch, char **data_p,
413   unsigned *n_ready_p)
414 {
415         unsigned        n_ready;
416
417         n_ready = *n_ready_p = ndmchan_n_ready (ch);
418         *data_p = &ch->data[ch->beg_ix];
419
420         if (ch->error) {
421                 if (n_ready == 0) {
422                         return NDMCHAN_RI_DONE_ERROR;
423                 } else {
424                         return NDMCHAN_RI_DRAIN_ERROR;
425                 }
426         }
427
428         if (ch->eof) {
429                 if (n_ready == 0) {
430                         return NDMCHAN_RI_DONE_EOF;
431                 } else {
432                         return NDMCHAN_RI_DRAIN_EOF;
433                 }
434         }
435
436         if (n_ready == 0) {
437                 return NDMCHAN_RI_EMPTY;
438         }
439
440         if (n_ready == ch->data_size) {
441                 return NDMCHAN_RI_READY_FULL;
442         }
443
444         return NDMCHAN_RI_READY;
445 }
446
447 enum ndmchan_write_interpretation
448 ndmchan_write_interpret (struct ndmchan *ch, char **data_p,
449   unsigned *n_avail_p)
450 {
451         unsigned        n_avail;
452
453         n_avail = *n_avail_p = ndmchan_n_avail (ch);
454         *data_p = &ch->data[ch->end_ix];
455
456         if (ch->error) {
457                 /* We don't use WI_DRAIN_ERROR. If it's kaput, it's kaput */
458                 return NDMCHAN_WI_DONE_ERROR;
459         }
460
461         if (ch->eof) {
462                 if (n_avail == ch->data_size) {
463                         return NDMCHAN_WI_DONE_EOF;
464                 } else {
465                         return NDMCHAN_WI_DRAIN_EOF;
466                 }
467         }
468
469         if (n_avail == 0) {
470                 return NDMCHAN_WI_FULL;
471         }
472
473         if (n_avail == ch->data_size) {
474                 return NDMCHAN_WI_AVAIL_EMPTY;
475         }
476
477         return NDMCHAN_WI_AVAIL;
478 }
479
480
481
482
483 /*
484  * Pretty printer
485  */
486 void
487 ndmchan_pp (struct ndmchan *ch, char *buf)
488 {
489         int             show_ra = 0;
490         char *          bp = buf;
491         char *          p;
492
493         sprintf (bp, "name=%s", ch->name); while (*bp) bp++;
494
495         switch (ch->mode) {
496         case NDMCHAN_MODE_IDLE:         p = "idle"; break;
497         case NDMCHAN_MODE_RESIDENT:     p = "resident"; show_ra = 1; break;
498         case NDMCHAN_MODE_READ:         p = "read"; show_ra = 1; break;
499         case NDMCHAN_MODE_WRITE:        p = "write"; show_ra = 1; break;
500         case NDMCHAN_MODE_READCHK:      p = "readchk"; break;
501         case NDMCHAN_MODE_LISTEN:       p = "listen"; break;
502         case NDMCHAN_MODE_PENDING:      p = "pending"; break;
503         case NDMCHAN_MODE_CLOSED:       p = "closed"; break;
504         default:                        p = "mode=???"; break;
505         }
506
507         sprintf (bp, " %s ", p);
508         while (*bp) bp++;
509
510         if (show_ra) {
511                 sprintf (bp, "ready=%d avail=%d ",
512                         ndmchan_n_ready(ch), ndmchan_n_avail(ch));
513                 while (*bp) bp++;
514         }
515
516         if (ch->ready)  strcat (bp, "-rdy");
517         if (ch->check)  strcat (bp, "-chk");
518         if (ch->eof)    strcat (bp, "-eof");
519         if (ch->error)  strcat (bp, "-err");
520 }
521
522
523
524
525 #ifdef NDMOS_OPTION_USE_SELECT_FOR_CHAN_POLL
526 /*
527  * Here because it is almost always used
528  */
529
530 int
531 ndmos_chan_poll (struct ndmchan *chtab[], unsigned n_chtab, int milli_timo)
532 {
533         struct ndmchan *        ch;
534         fd_set                  rfds, wfds;
535         int                     nfd = 0, rc;
536         unsigned                i;
537         struct timeval          timo;
538
539         FD_ZERO(&rfds);
540         FD_ZERO(&wfds);
541
542         timo.tv_sec = milli_timo / 1000;
543         timo.tv_usec = (milli_timo%1000) * 1000;
544
545         for (i = 0; i < n_chtab; i++) {
546                 ch = chtab[i];
547                 if (!ch->check)
548                         continue;
549
550                 switch (ch->mode) {
551                 case NDMCHAN_MODE_LISTEN:
552                 case NDMCHAN_MODE_READCHK:
553                 case NDMCHAN_MODE_READ:
554                         FD_SET (ch->fd, &rfds);
555                         break;
556
557                 case NDMCHAN_MODE_WRITE:
558                         FD_SET (ch->fd, &wfds);
559                         break;
560                 }
561                 if (nfd < ch->fd+1)
562                         nfd = ch->fd+1;
563         }
564
565         rc = select (nfd, &rfds, &wfds, (void*)0, &timo);
566         if (rc <= 0)
567                 return rc;
568
569         for (i = 0; i < n_chtab; i++) {
570                 ch = chtab[i];
571                 if (!ch->check)
572                         continue;
573
574                 switch (ch->mode) {
575                 case NDMCHAN_MODE_LISTEN:
576                 case NDMCHAN_MODE_READCHK:
577                 case NDMCHAN_MODE_READ:
578                         if (FD_ISSET (ch->fd, &rfds))
579                                 ch->ready = 1;
580                         break;
581
582                 case NDMCHAN_MODE_WRITE:
583                         if (FD_ISSET (ch->fd, &wfds))
584                                 ch->ready = 1;
585                         break;
586                 }
587         }
588
589         return rc;
590 }
591
592 #endif /* NDMOS_OPTION_USE_SELECT_FOR_CHAN_POLL */
593
594 #ifdef NDMOS_OPTION_USE_POLL_FOR_CHAN_POLL
595 /*
596  * Here because it is common, and because poll(2) is
597  * INFINITELY SUPERIOR to select(2).
598  */
599
600 int
601 ndmos_chan_poll (struct ndmchan *chtab[], unsigned n_chtab, int milli_timo)
602 {
603         struct ndmchan *        ch;
604         struct pollfd           pfdtab[20];
605         int                     n_pfdtab = 0;
606         int                     rc, i;
607
608         NDMOS_MACRO_ZEROFILL (pfdtab);
609
610         for (i = 0; i < n_chtab; i++) {
611                 ch = chtab[i];
612                 if (!ch->check)
613                         continue;
614
615                 switch (ch->mode) {
616                 case NDMCHAN_MODE_LISTEN:
617                 case NDMCHAN_MODE_READCHK:
618                 case NDMCHAN_MODE_READ:
619                         pfdtab[n_pfdtab].fd = ch->fd;
620                         pfdtab[n_pfdtab].events = POLLIN;
621                         break;
622
623                 case NDMCHAN_MODE_WRITE:
624                         pfdtab[n_pfdtab].fd = ch->fd;
625                         pfdtab[n_pfdtab].events = POLLOUT;
626                         break;
627                 }
628                 n_pfdtab++;
629         }
630
631         rc = poll (pfdtab, n_pfdtab, milli_timo);
632
633         @@@ TODO: post them back. Not easy @@@
634
635         return rc;
636 }
637
638 #endif /* NDMOS_OPTION_USE_POLL_FOR_CHAN_POLL */
639