clean up headers, obsd socket.h needs types.h
[arla.git] / arlad / conn.c
blob8dcd055bcd261b1a62271910f09c8cfb8c3b2c34
1 /*
2 * Copyright (c) 1995 - 2002 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
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 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
35 * Cache of connections
39 #include "arla_local.h"
40 #ifdef RCSID
41 RCSID("$Id$") ;
42 #endif
44 #define CONNCACHESIZE 101
46 #define CONNFREELISTINC 17
48 /* Hashtable of connections */
49 static Hashtab *connhtab;
51 /* A list with free connections */
52 static List *connfreelist;
54 /* # of connections */
55 static unsigned nconnections;
57 /* # of active connections */
58 static unsigned nactive_connections;
60 /* List of connections to probe */
61 static List *connprobelist;
63 #ifdef KERBEROS
64 int conn_rxkad_level = rxkad_crypt;
65 #endif
68 * Random factor to add to rtts when comparing them.
69 * This is in microseconds/8
72 int RTT_FUZZ = 400000;
75 * Functions for handling entries into the connection cache.
78 static int
79 conncmp (void *a, void *b)
81 ConnCacheEntry *c1 = (ConnCacheEntry*)a;
82 ConnCacheEntry *c2 = (ConnCacheEntry*)b;
84 return c1->cred != c2->cred
85 || c1->host != c2->host
86 || c1->service != c2->service
87 || c1->port != c2->port
88 || c1->securityindex != c2->securityindex;
91 static unsigned int
92 connhash (void *a)
94 ConnCacheEntry *c = (ConnCacheEntry*)a;
96 return c->cred + c->host + c->service + c->port + c->securityindex;
99 /* 2^MAX_RETRIES is the maximum number of seconds between probes */
101 #define MAX_RETRIES 8
104 * Add this entry again to the probe list but without restarting ntries.
107 static void
108 re_probe (ConnCacheEntry *e)
110 Listitem *item;
111 struct timeval tv;
113 assert (e->probe != NULL);
115 gettimeofday (&tv, NULL);
116 if (e->probe_le) {
117 listdel (connprobelist, e->probe_le);
118 e->probe_next = min(tv.tv_sec + (1 << e->ntries), e->probe_next);
119 } else
120 e->probe_next = tv.tv_sec + (1 << e->ntries);
122 if (e->ntries <= MAX_RETRIES)
123 ++e->ntries;
125 for (item = listhead (connprobelist);
126 item;
127 item = listnext (connprobelist, item)) {
128 ConnCacheEntry *this = (ConnCacheEntry *)listdata (item);
130 if (e->probe_next < this->probe_next) {
131 e->probe_le = listaddbefore (connprobelist, item, e);
132 LWP_NoYieldSignal (connprobelist);
133 return;
136 e->probe_le = listaddtail (connprobelist, e);
137 LWP_NoYieldSignal (connprobelist);
141 * Initial add to probe list.
144 static void
145 add_to_probe_list (ConnCacheEntry *e, int ntries)
147 e->ntries = ntries;
148 re_probe (e);
155 static PROCESS pinger_pid;
158 * Loop waiting for things servers to probe.
161 static void
162 pinger (char *arg)
164 for (;;) {
165 struct timeval tv;
166 Listitem *item;
167 ConnCacheEntry *e;
168 struct in_addr addr;
169 const char *port_str;
171 arla_warnx(ADEBCONN, "running pinger");
173 while (listemptyp (connprobelist))
174 LWP_WaitProcess (connprobelist);
176 item = listhead (connprobelist);
177 e = (ConnCacheEntry *)listdata (item);
179 assert (e->probe_le == item);
181 gettimeofday (&tv, NULL);
182 if (tv.tv_sec < e->probe_next) {
183 unsigned long t = e->probe_next - tv.tv_sec;
185 arla_warnx(ADEBCONN,
186 "pinger: sleeping %lu second(s)", t);
187 IOMGR_Sleep (t);
188 continue;
191 listdel (connprobelist, item);
192 e->probe_le = NULL;
194 if (e->flags.alivep)
195 continue;
197 addr.s_addr = e->host;
198 port_str = ports_num2name (e->port);
200 if (port_str != NULL)
201 arla_warnx (ADEBCONN, "pinger: probing %s/%s",
202 inet_ntoa(addr), port_str);
203 else
204 arla_warnx (ADEBCONN, "pinger: probing %s/%d",
205 inet_ntoa(addr), e->port);
206 ++e->refcount;
207 if (e->probe == NULL)
208 arla_warnx(ADEBWARN, "pinger: probe function is NULL, "
209 "host: %s cell: %d port: %d",
210 inet_ntoa(addr), e->cell, e->port);
212 if (connected_mode == DISCONNECTED) {
213 arla_warnx(ADEBCONN, "pinger: ignoring host in disconnected mode");
214 } else if (e->probe && ((*(e->probe))(e->connection) == 0)) {
215 conn_alive (e);
216 } else {
217 re_probe (e);
220 conn_free (e);
225 * Create `n' ConnCacheEntry's and add them to `connfreelist'
228 static void
229 create_new_connections (unsigned n)
231 unsigned i;
232 ConnCacheEntry *entries;
234 entries = (ConnCacheEntry*)calloc (n, sizeof (ConnCacheEntry));
235 if (entries == NULL)
236 arla_errx (1, ADEBERROR, "conncache: calloc failed");
237 for (i = 0; i < n; ++i) {
238 entries[i].connection = NULL;
239 entries[i].refcount = 0;
240 entries[i].parent = NULL;
241 entries[i].probe_le = NULL;
242 listaddhead (connfreelist, &entries[i]);
244 nconnections += n;
248 * Initialize the connection cache.
251 void
252 conn_init (unsigned nentries)
254 arla_warnx (ADEBCONN, "initconncache");
256 connhtab = hashtabnew (CONNCACHESIZE, conncmp, connhash);
257 if (connhtab == NULL)
258 arla_errx (1, ADEBERROR, "conn_init: hashtabnew failed");
259 connfreelist = listnew ();
260 if (connfreelist == NULL)
261 arla_errx (1, ADEBERROR, "conn_init: listnew failed");
262 connprobelist = listnew ();
263 if (connprobelist == NULL)
264 arla_errx (1, ADEBERROR, "conn_init: listnew failed");
265 nconnections = 0;
267 if (LWP_CreateProcess (pinger, 0, 1, NULL, "pinger", &pinger_pid))
268 arla_errx (1, ADEBERROR,
269 "conn: cannot create pinger thread");
271 create_new_connections (nentries);
275 * Re-cycle an entry:
276 * remove it from the hashtab, clear it out and place it on the freelist.
279 static void
280 recycle_conn (ConnCacheEntry *e)
282 assert (e->refcount == 0);
284 if (e->parent != NULL) {
285 conn_free (e->parent);
286 e->parent = NULL;
288 if (e->probe_le != NULL) {
289 listdel (connprobelist, e->probe_le);
290 e->probe_le = NULL;
292 if (!e->flags.killme)
293 hashtabdel (connhtab, e);
294 rx_DestroyConnection (e->connection);
295 memset (e, 0, sizeof(*e));
296 listaddhead (connfreelist, e);
297 --nactive_connections;
301 * Remove this connection from the hashtab and add it to the freelist
302 * iff refcount == 0.
305 static Bool
306 clear_conn (void *ptr, void *arg)
308 ConnCacheEntry *e = (ConnCacheEntry *)ptr;
310 if (e->refcount == 0)
311 recycle_conn (e);
312 return FALSE;
316 * Get a free connection to use. Try to pick it from `connfreelist'.
317 * If there are no there, it's time to go through `connhtab' and GC
318 * unused connections. If that fails, allocate some more.
319 * And if that fails, give up.
322 static ConnCacheEntry *
323 get_free_connection (void)
325 ConnCacheEntry *e;
327 e = (ConnCacheEntry *)listdelhead (connfreelist);
328 if (e != NULL)
329 return e;
331 hashtabforeach (connhtab, clear_conn, NULL);
333 e = (ConnCacheEntry *)listdelhead (connfreelist);
334 if (e != NULL)
335 return e;
337 create_new_connections (CONNFREELISTINC);
339 e = (ConnCacheEntry *)listdelhead (connfreelist);
340 if (e != NULL)
341 return e;
343 arla_errx (1, ADEBERROR,
344 "conncache: there was no way of getting a connection");
348 * Get a free connection, fill in all parameters and create a
349 * rx_connection.
352 static ConnCacheEntry *
353 new_connection (int32_t cell,
354 uint32_t host,
355 uint16_t port,
356 uint16_t service,
357 nnpfs_pag_t cred,
358 int securityindex,
359 int (*probe)(struct rx_connection *),
360 struct rx_securityClass *securityobject)
362 ConnCacheEntry *e;
364 assert (probe != NULL);
366 e = get_free_connection ();
368 e->cell = cell;
369 e->host = host;
370 e->port = port;
371 e->service = service;
372 e->flags.alivep = TRUE;
373 e->flags.old = FALSE;
374 e->refcount = 0;
375 e->cred = cred;
376 e->securityindex = securityindex;
377 e->probe = probe;
379 e->connection = rx_NewConnection (host,
380 htons (port),
381 service,
382 securityobject,
383 securityindex);
384 if (e->connection == NULL)
385 arla_errx (1, ADEBERROR, "rx_NewConnection failed");
386 return e;
390 * Create a new connection and add it to `connhtab'.
393 static ConnCacheEntry *
394 add_connection(int32_t cell,
395 uint32_t host,
396 uint16_t port,
397 uint16_t service,
398 int (*probe)(struct rx_connection *),
399 CredCacheEntry *ce)
401 ConnCacheEntry *e;
402 struct rx_securityClass *securityobj;
403 int securityindex;
404 nnpfs_pag_t cred;
406 if (ce) {
407 securityindex = ce->securityindex;
408 cred = ce->cred;
410 switch (ce->type) {
411 case CRED_RXGK : {
412 struct cred_rxgk *cred = (struct cred_rxgk *)ce->cred_data;
413 unsigned char *base = (unsigned char *)ce->cred_data;
414 RXGK_Ticket_Crypt ticket;
415 struct rxgk_keyblock key;
417 ticket.len = cred->tokenlen;
418 ticket.val = (void*)(base + sizeof(struct cred_rxgk));
420 key.enctype = cred->enctype;
421 key.length = cred->keylen;
422 key.data = (void *)(base + sizeof(struct cred_rxgk) + ticket.len);
424 securityobj = rxgk_NewClientSecurityObject(cred->level,
425 &ticket, &key);
426 break;
428 #ifdef KERBEROS
429 case CRED_KRB4 : {
430 struct cred_rxkad *cred = (struct cred_rxkad *)ce->cred_data;
432 securityobj = rxkad_NewClientSecurityObject(conn_rxkad_level,
433 cred->ct.HandShakeKey,
434 cred->ct.AuthHandle,
435 cred->ticket_len,
436 (void *)cred->ticket);
437 break;
439 #endif
440 case CRED_NONE :
441 securityobj = rxnull_NewClientSecurityObject ();
442 break;
443 default :
444 abort();
446 } else {
447 securityobj = rxnull_NewClientSecurityObject ();
448 securityindex = 0;
449 cred = 0;
452 e = new_connection (cell, host, port, service,
453 cred, securityindex, probe, securityobj);
455 hashtabadd (connhtab, (void *)e);
456 ++nactive_connections;
458 return e;
463 * Find a connection from the cache given:
464 * (cell, host, port, service, cred).
465 * If there's no connection at all, create one.
468 static ConnCacheEntry *
469 internal_get (int32_t cell,
470 uint32_t host,
471 uint16_t port,
472 uint16_t service,
473 int (*probe)(struct rx_connection *),
474 CredCacheEntry *ce)
476 ConnCacheEntry *e;
477 ConnCacheEntry key;
479 #if 0
480 if (connected_mode == DISCONNECTED)
481 return NULL;
482 #endif
484 key.host = host;
485 key.port = port;
486 key.service = service;
487 key.cred = ce->cred;
488 key.securityindex = ce->securityindex;
490 e = (ConnCacheEntry *)hashtabsearch (connhtab, (void *)&key);
492 if (e == NULL) {
493 ConnCacheEntry *parent = NULL;
495 if (ce->securityindex || ce->cred) {
496 key.cred = 0;
497 key.securityindex = 0;
498 parent = (ConnCacheEntry *)hashtabsearch (connhtab, (void *)&key);
499 if (parent == NULL) {
500 parent = add_connection (cell, host, port, service,
501 probe, NULL);
503 ++parent->refcount;
506 e = add_connection (cell, host, port, service, probe, ce);
507 if (parent != NULL)
508 e->parent = parent;
512 * Since we only probe the parent entry (ie noauth), we make sure
513 * the status from the parent entry is pushed down to the
514 * children.
516 if(e->parent != NULL) {
517 e->flags.alivep = e->parent->flags.alivep;
520 return e;
524 * Return a connection to (cell, host, port, service, ce)
527 ConnCacheEntry *
528 conn_get (int32_t cell,
529 uint32_t host,
530 uint16_t port,
531 uint16_t service,
532 int (*probe)(struct rx_connection *),
533 CredCacheEntry *ce)
535 ConnCacheEntry *e = internal_get (cell, host, port, service, probe, ce);
537 ++e->refcount;
538 return e;
542 * Add a new reference to a connection
545 void
546 conn_ref(ConnCacheEntry *e)
548 assert(e->refcount > 0);
549 e->refcount++;
553 * Free a reference to a ConnCacheEntry.
554 * If refcount drops to zero, it makes it eligible for re-use.
557 void
558 conn_free (ConnCacheEntry *e)
560 if (e == NULL) /* When in disconnected mode conn sets to NULL */
561 return;
563 assert (e->refcount > 0);
565 --e->refcount;
566 if (e->refcount == 0 && e->flags.killme)
567 recycle_conn (e);
571 * Given a host try to figure out what cell it's in.
574 int32_t
575 conn_host2cell (uint32_t host, uint16_t port, uint16_t service)
577 ConnCacheEntry *e;
578 ConnCacheEntry key;
580 key.host = host;
581 key.port = port;
582 key.service = service;
583 key.cred = 0;
584 key.securityindex = 0;
586 e = (ConnCacheEntry *)hashtabsearch(connhtab, (void *)&key);
587 if (e == NULL)
588 return -1;
589 else
590 return e->cell;
594 * Mark the server in `e' as being down.
597 void
598 conn_dead (ConnCacheEntry *e)
600 struct in_addr a;
601 char buf[10];
602 const char *port;
604 assert (e->probe != NULL);
606 e->flags.alivep = FALSE;
607 if (e->parent != NULL) {
608 e = e->parent;
609 e->flags.alivep = FALSE;
611 add_to_probe_list (e, 0);
612 a.s_addr = e->host;
613 port = ports_num2name (e->port);
614 if (port == NULL) {
615 snprintf(buf, sizeof(buf), "%d", e->port);
616 port = buf;
619 arla_warnx (ADEBWARN, "Lost connection to %s/%s in cell %s",
620 inet_ntoa(a), port, cell_num2name (e->cell));
624 * Mark the server in `e' as being up.
627 void
628 conn_alive (ConnCacheEntry *e)
630 struct in_addr a;
631 const char *s;
633 a.s_addr = e->host;
634 s = ports_num2name (e->port);
635 if (s != NULL)
636 arla_warnx (ADEBWARN, "Server %s/%s up again", inet_ntoa(a), s);
637 else
638 arla_warnx (ADEBWARN, "Server %s/%d up again", inet_ntoa(a), e->port);
639 e->flags.alivep = TRUE;
640 if (e->parent != NULL)
641 e->parent->flags.alivep = TRUE;
645 * Is this server known to be up?
648 Bool
649 conn_isalivep (ConnCacheEntry *e)
651 if (e->parent != NULL)
652 e->flags.alivep = e->parent->flags.alivep;
654 return e->flags.alivep;
658 * Probe the service in `e'
661 void
662 conn_probe (ConnCacheEntry *e)
664 ++e->refcount;
666 struct in_addr a;
667 a.s_addr = e->host;
669 if (e->probe == NULL)
670 arla_warnx(ADEBWARN, "conn_probe: probe function is NULL, "
671 "host: %s cell: %d port: %d",
672 inet_ntoa(a), e->cell, e->port);
674 if (e->probe && ((*(e->probe))(e->connection) == 0)) {
675 if (!e->flags.alivep)
676 conn_alive (e);
677 } else {
678 if (e->flags.alivep)
679 conn_dead (e);
681 conn_free (e);
685 * Is the service at (cell, host, port, service) up?
688 Bool
689 conn_serverupp (uint32_t host, uint16_t port, uint16_t service)
691 ConnCacheEntry *e;
692 ConnCacheEntry key;
694 key.host = host;
695 key.port = port;
696 key.service = service;
697 key.cred = 0;
698 key.securityindex = 0;
700 e = (ConnCacheEntry *)hashtabsearch (connhtab, (void *)&key);
701 if (e != NULL)
702 return e->flags.alivep;
703 else
704 return TRUE;
708 * Print an entry.
711 static Bool
712 print_conn (void *ptr, void *arg)
714 ConnCacheEntry *e = (ConnCacheEntry *)ptr;
715 struct in_addr tmp;
717 tmp.s_addr = e->host;
719 arla_log(ADEBVLOG, "host = %s, port = %d, service = %d, "
720 "cell = %d (%s), "
721 "securityindex = %d, cred = %u, "
722 "conn = %p, alive = %d, "
723 "killme = %d, refcount = %d",
724 inet_ntoa(tmp), e->port, e->service, e->cell,
725 cell_num2name (e->cell),
726 e->securityindex, e->cred, e->connection,
727 e->flags.alivep, e->flags.killme, e->refcount);
729 return FALSE;
733 * Print the status of the connection cache.
736 void
737 conn_status (void)
739 arla_log(ADEBVLOG, "%u(%u) connections",
740 nactive_connections, nconnections);
741 hashtabforeach (connhtab, print_conn, NULL);
744 Bool
745 conn_get_fs_support64(const ConnCacheEntry *e)
747 return e->flags.no64support ? FALSE : TRUE;
750 void
751 conn_set_fs_support64(ConnCacheEntry *e, Bool status)
753 e->flags.no64support = status == TRUE ? 0 : 1;
760 struct clear_state {
761 clear_state_mask mask;
762 int32_t cell;
763 nnpfs_pag_t cred;
764 int securityindex;
767 static Bool
768 clear_cred (void *ptr, void *arg)
770 ConnCacheEntry *e = (ConnCacheEntry *)ptr;
771 struct clear_state *s = (struct clear_state *)arg;
773 if ((s->mask & CONN_CS_CRED) && s->cred != e->cred)
774 return FALSE;
775 if ((s->mask & CONN_CS_CELL) && s->cell != e->cell)
776 return FALSE;
777 if ((s->mask & CONN_CS_SECIDX) && s->securityindex != e->securityindex)
778 return FALSE;
780 if (e->refcount > 0) {
781 e->flags.killme = 1;
782 hashtabdel (connhtab, e);
783 } else
784 recycle_conn (e);
786 return FALSE;
790 * Remove all connections matching mask + (cell, cred, securityindex).
793 void
794 conn_clearcred(clear_state_mask mask,
795 int32_t cell, nnpfs_pag_t cred, int securityindex)
797 struct clear_state s;
799 s.mask = mask;
800 s.cell = cell;
801 s.cred = cred;
802 s.securityindex = securityindex;
804 hashtabforeach (connhtab, clear_cred, (void *)&s);
808 * check if servers are up for cell `cell'
811 struct down_state {
812 int32_t cell;
813 uint32_t *hosts;
814 int len;
815 int i;
816 int flags;
819 static Bool
820 host_down (void *ptr, void *arg)
822 ConnCacheEntry *e = (ConnCacheEntry *)ptr;
823 struct down_state *s = (struct down_state *)arg;
824 int i;
826 if (s->cell == e->cell) {
828 if (!(s->flags & arla_CKSERV_DONTPING)) {
829 conn_probe (e);
832 if (e->flags.alivep)
833 return FALSE;
835 if (s->flags & arla_CKSERV_FSONLY && e->port != afsport)
836 return FALSE;
838 for (i = 0; i < s->i; ++i)
839 if (s->hosts[i] == e->host)
840 return FALSE;
842 s->hosts[s->i] = e->host;
843 ++s->i;
845 if (s->i == s->len)
846 return TRUE;
848 return FALSE;
852 * Check what hosts are down.
854 * Flags is VIOCCKFLAGS
857 void
858 conn_downhosts(int32_t cell, uint32_t *hosts, int *num, int flags)
860 struct down_state s;
862 if (*num == 0)
863 return;
865 s.cell = cell;
866 s.hosts = hosts;
867 s.len = *num;
868 s.i = 0;
869 s.flags = flags;
871 hashtabforeach (connhtab, host_down, (void *)&s);
873 *num = s.i;
877 * Compare two ConnCacheEntries rtt-wise. Typically used when sorting
878 * entries.
882 conn_rtt_cmp (const void *v1, const void *v2)
884 ConnCacheEntry **e1 = (ConnCacheEntry **)v1;
885 ConnCacheEntry **e2 = (ConnCacheEntry **)v2;
887 return (*e1)->rtt - (*e2)->rtt;
891 * Return true iff this error means we should mark the host as down
892 * due to network errors
895 Bool
896 host_downp (int error)
898 switch (error) {
899 case ARLA_CALL_DEAD :
900 case ARLA_INVALID_OPERATION :
901 case ARLA_CALL_TIMEOUT :
902 case ARLA_EOF :
903 case ARLA_PROTOCOL_ERROR :
904 case ARLA_USER_ABORT :
905 case ARLA_ADDRINUSE :
906 case ARLA_MSGSIZE :
907 case RXGEN_CC_MARSHAL :
908 case RXGEN_CC_UNMARSHAL :
909 case RXGEN_SS_MARSHAL :
910 case RXGEN_SS_UNMARSHAL :
911 case RXGEN_DECODE :
912 case RXGEN_OPCODE :
913 case RXGEN_SS_XDRFREE :
914 case RXGEN_CC_XDRFREE :
915 return TRUE;
916 default :
917 return FALSE;