Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / lib / csa / connection.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: connection.c /main/4 1999/10/14 17:47:12 mgreess $ */
24 /*
25  *  (c) Copyright 1993, 1994 Hewlett-Packard Company
26  *  (c) Copyright 1993, 1994 International Business Machines Corp.
27  *  (c) Copyright 1993, 1994 Novell, Inc.
28  *  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
29  */
30
31 /*
32  * This file manages server connections.
33  */
34
35 #include <EUSCompat.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <time.h>
40 #include <unistd.h>
41 #include <fcntl.h>
42 #ifdef HPUX
43 #include <sys/resource.h>
44 #endif
45 #include "connection.h"
46 #include "rtable2.h"
47 #include "rtable3.h"
48 #include "rtable4.h"
49 #include "cm.h"
50 #include "debug.h"
51 #include "agent.h"
52 #include "convert2-4.h"
53 #include "convert3-4.h"
54 #include "rpccalls.h"
55
56 #ifdef HPUX
57 #define MAX_COUNT       10
58 #else
59 #define MAX_COUNT       40
60 #endif
61
62 static struct timeval timeout_tv;
63 static struct timeval retry_tv;
64 static AUTH *unix_credential = NULL;
65 static tcp_count = 0;
66 static cl_count = 0;
67 static _DtCm_Client_Info *client_cache_head = NULL;
68 static _DtCm_Client_Info *client_cache_tail = NULL;
69 static char *svcfmt = "Error on server %s\n";
70
71 /*****************************************************************************
72  * forward declaration of static functions.
73  *****************************************************************************/
74 static void create_auth(CLIENT *cl);
75 static void destroy_auth(CLIENT *cl);
76 static _DtCm_Client_Info * get_client_info(char *host, int version);
77 static void destroy_target_list(_DtCm_Target_List *tlist);
78 static void destroy_client_info(_DtCm_Client_Info *ci);
79 static void insert_client_info(_DtCm_Client_Info *ci);
80 static void delete_client_info(_DtCm_Client_Info *oldci);
81 static void cleanup_some_connection(_DtCm_Client_Info *dontclose);
82 static void check_registration(_DtCm_Connection *conn);
83 static _DtCm_Client_Info * get_new_client_handle(_DtCm_Connection *conn);
84 #ifdef __osf__
85 static CSA_return_code get_client_handle(const char *host, const u_int prognum,
86                         u_long *vers_outp, const u_long vers_low,
87                         const u_long vers_high, char *nettype,
88                         CLIENT **clnt);
89 #else
90 static CSA_return_code get_client_handle(const char *host, const u_long prognum,
91                         u_long *vers_outp, const u_long vers_low,
92                         const u_long vers_high, char *nettype,
93                         CLIENT **clnt);
94 #endif
95 static CSA_return_code regstat4_to_dtcmstatus(Registration_Status_4 stat4);
96
97 extern CSA_return_code
98 _DtCm_create_udp_client(
99         char *host,
100         int version,
101         int timeout,
102         _DtCm_Client_Info **clnt)
103 {
104         CSA_return_code stat;
105         _DtCm_Client_Info       *ci;
106         u_long          vers_out;
107         CLIENT          *cl=NULL;
108
109         if (host == NULL || clnt == NULL)
110                 return (CSA_E_INVALID_PARAMETER);
111
112         /* if client info is found, we have at least the udp handle */
113         if (((*clnt) = get_client_info(host, version)) != NULL) {
114                 return (CSA_SUCCESS);
115         }
116
117 #if defined(SunOS) || defined(USL) || defined(__uxp__)
118         cl = clnt_create_vers(host, TABLEPROG, 
119                 &vers_out, TABLEVERS_2, version, "udp");
120         if (cl==NULL) {
121                 _DtCm_print_errmsg(clnt_spcreateerror(host));
122                 return (_DtCm_clntstat_to_csastat(rpc_createerr.cf_stat));
123         }
124 #else
125         stat = get_client_handle(host, (u_int)TABLEPROG, &vers_out, TABLEVERS_2,
126                 version, "udp", &cl);
127         if (stat != CSA_SUCCESS)
128                 return (stat);
129 #endif
130
131         /* if version is lower than requested, check the list again */
132         if (vers_out < version) {
133                 if ((ci = get_client_info(host, vers_out)) != NULL) {
134                         clnt_destroy(cl);
135                         *clnt = ci;
136                         return (CSA_SUCCESS);
137                 }
138         }
139
140         create_auth(cl);
141
142         /* Adjust Timeout */
143         if (timeout==0) timeout = _DtCM_DEFAULT_TIMEOUT;
144         timeout_tv.tv_sec =  timeout;
145         timeout_tv.tv_usec = 0;
146         clnt_control(cl, CLSET_TIMEOUT, (char*)&timeout_tv);            
147
148         /*      UDP only!
149                 time rpc waits for server to reply before retransmission =
150                 'timeout'. since the retry timeout is set to timeout + 10;
151                 this guarantees there won't
152                 be any retransmisssions resulting in duplicate 
153                 transactions in the database.
154         */
155
156         retry_tv.tv_sec =  timeout + 10;
157         retry_tv.tv_usec = 0;
158         clnt_control(cl, CLSET_RETRY_TIMEOUT, (char*)&retry_tv);
159
160         if ((ci  = (_DtCm_Client_Info *)calloc(1, sizeof(_DtCm_Client_Info))) == NULL) {
161                 destroy_auth(cl);
162                 clnt_destroy(cl);
163                 return (CSA_E_INSUFFICIENT_MEMORY);
164         }
165
166         if ((ci->host = strdup(host)) == NULL) {
167                 destroy_auth(cl);
168                 clnt_destroy(cl);
169                 free(ci);
170                 return (CSA_E_INSUFFICIENT_MEMORY);
171         }
172
173         ci->udpcl = cl;
174         ci->vers_out = vers_out;
175         insert_client_info(ci);
176         *clnt = ci;
177         return (CSA_SUCCESS);
178 }
179
180 /*
181  * Creates tcp client handle.  Used for calls that potentially return
182  * large amount of data.  If it fails to create a tcp client handle,
183  * a udp client handle will be returned.
184  */
185 extern CSA_return_code
186 _DtCm_create_tcp_client(
187         char *host,
188         int version,
189         int timeout,
190         _DtCm_Client_Info **clnt)
191 {
192         CSA_return_code stat;
193         _DtCm_Client_Info       *ci;
194         u_long          vers_out;
195         CLIENT          *cl=NULL;
196
197         if (host == NULL || clnt == NULL)
198                 return (CSA_E_INVALID_PARAMETER);
199
200         /* Get a udp client handle.  This serves two purposes:             */ 
201         /* - to get a udp handle for an old server which talks only udp    */
202         /* - to invoke a server through inetd since only udp is registered.*/
203
204         if ((stat = _DtCm_create_udp_client(host, version, timeout, &ci))
205             != CSA_SUCCESS) {
206                 return (stat);
207         } else if (ci->tcpcl) {
208                 *clnt = ci;
209                 return (CSA_SUCCESS);
210         } else {
211                 /* create tcp connection */
212 #if defined(SunOS) || defined(USL) || defined(__uxp__)
213                 cl = clnt_create_vers(host, TABLEPROG, &vers_out,
214                         TABLEVERS_2, version, "tcp");
215 #else
216                 stat = get_client_handle(host, (u_int)TABLEPROG, &vers_out,
217                         TABLEVERS_2, version, "tcp", &cl);
218 #endif
219
220                 /* if can't create tcp connection, use udp */
221                 if (cl==NULL) {
222                         _DtCm_print_errmsg(clnt_spcreateerror(host));
223                         *clnt = ci;
224                         return (CSA_SUCCESS);
225                 }
226
227                 create_auth(cl);
228
229                 /* Adjust Timeout */
230                 if (timeout==0) timeout = _DtCM_DEFAULT_TIMEOUT;
231                 timeout_tv.tv_sec =  timeout;
232                 timeout_tv.tv_usec = 0;
233                 clnt_control(cl, CLSET_TIMEOUT, (char*)&timeout_tv);            
234
235                 /* dont need to set vers_out since it should
236                  * be the same as that of the udp transport
237                  */
238                 ci->tcpcl = cl;
239                 if (++tcp_count > MAX_COUNT)
240                         /* clean up tcp connections */
241                         cleanup_some_connection(ci);
242                 *clnt = ci;
243                 return (CSA_SUCCESS);
244         }
245 }
246
247 /*
248  * Used instead of clnt_call by rtableX_clnt.c
249  *
250  * Might need locking for the client handle here since
251  * it might be purged if something's wrong
252  */
253 extern enum clnt_stat
254 _DtCm_clnt_call(
255         _DtCm_Connection *conn,
256         u_long proc,
257         xdrproc_t inproc,
258         caddr_t in,
259         xdrproc_t outproc,
260         caddr_t out,
261         struct timeval tout)
262 {
263         _DtCm_Client_Info       *ci;
264         _DtCm_Transport_Type    ttype;
265         enum clnt_stat status = RPC_FAILED;
266         char errbuffer[100];
267         int retry = conn->retry;
268
269         while (B_TRUE) {
270                 if (conn->ci == NULL)
271                         break;
272                 else {
273                         ci = conn->ci;
274                         ci->last_used = time(0);
275                 }
276
277                 if (conn->use == udp_transport || ci->tcpcl == NULL)
278                         ttype = udp_transport;
279                 else
280                         ttype = tcp_transport;
281
282                 status = clnt_call((ttype == tcp_transport ? ci->tcpcl :
283                                 ci->udpcl), proc, inproc, in,
284                                 outproc, out, tout);
285
286                 if ((ttype == udp_transport && status == RPC_TIMEDOUT) ||
287                      (status == RPC_CANTRECV)) {
288
289                         if (retry) {
290                                 retry--;
291
292                                 /* don't retry when stat is RPC_TIMEDOUT
293                                  * and transpart is tcp since if the server
294                                  * is down, stat would be something else
295                                  * like RPC_CANTRECV
296                                  */
297
298                                 /* get new client handle */
299                                 if (get_new_client_handle(conn) == NULL)
300                                         break;
301
302                         } else {
303                                 /* purge the client handle */
304                                 delete_client_info(conn->ci);
305                                 conn->ci = NULL;
306                                 break;
307                         }
308                 } else
309                         break;
310         }
311
312         if (status != RPC_SUCCESS && conn->ci != NULL) {
313                 _DtCm_print_errmsg(clnt_sperror((ttype == tcp_transport ? ci->tcpcl :
314                                 ci->udpcl), ci->host));
315         }
316         conn->stat = status;
317         return status;
318 }
319
320 extern CSA_return_code
321 _DtCm_add_registration(
322         _DtCm_Client_Info *ci,
323         char *cal,
324         unsigned long update_type)
325 {
326         _DtCm_Target_List *listp, *prev;
327         _DtCm_Target_List *listitem;
328         char *host;
329         int result;
330
331         if (ci == NULL || cal == NULL)
332                 return (CSA_E_INVALID_PARAMETER);
333
334         for (listp = prev = ci->tlist; listp != NULL;
335              prev = listp, listp = listp->next) {
336                 if ((result = strcmp(listp->cal, cal)) == 0) {
337                         /* registered already */
338                         return (CSA_SUCCESS);
339                 } else if (result > 0)
340                         break;
341         }
342
343         /* register the first time, insert in list in ascending order */
344         if ((listitem = (_DtCm_Target_List *)calloc(1, sizeof(_DtCm_Target_List))) == NULL)
345                 return (CSA_E_INSUFFICIENT_MEMORY);
346
347         if ((listitem->cal = strdup(cal)) == NULL) {
348                 free(listitem);
349                 return (CSA_E_INSUFFICIENT_MEMORY);
350         }
351
352         listitem->update_type = update_type;
353
354         if (prev == NULL || listp == prev)
355                 ci->tlist = listitem;
356         else
357                 prev->next = listitem;
358         listitem->next = listp;
359
360         ci->nregistered++;
361
362         return (CSA_SUCCESS);
363 }
364
365 extern void
366 _DtCm_remove_registration(_DtCm_Client_Info *ci, char *cal)
367 {
368         _DtCm_Target_List *listp, *prev;
369         _DtCm_Client_Info *c;
370         int result;
371
372         if (cal == NULL) return;
373
374         /* if found, just increment the number of registration */
375         for (listp = prev = ci->tlist; listp != NULL;
376              prev = listp, listp = listp->next) {
377                 if ((result = strcmp(listp->cal, cal)) == 0) {
378                         if (listp == prev)
379                                 ci->tlist = listp->next;
380                         else
381                                 prev->next = listp->next;
382
383                         /* free target item */
384                         free(listp->cal);
385                         free(listp);
386
387                         /*
388                          * if no calendar is registered, close tcp connection
389                          */
390                         if (--(ci->nregistered) == 0) {
391                                 if (ci->tcpcl) {
392                                         destroy_auth(ci->tcpcl);
393                                         clnt_destroy(ci->tcpcl);
394                                         ci->tcpcl = NULL;
395                                         tcp_count--;
396                                 }
397
398                                 /* find other tcp connection for the
399                                  * same host
400                                  */
401                                 for (c = client_cache_head; c != NULL;
402                                     c = c->next) {
403                                         if ((result = strcmp(c->host,
404                                             ci->host)) == 0) {
405                                                 if (c->nregistered == 0 &&
406                                                     c->tcpcl) {
407                                                         destroy_auth(c->tcpcl);
408                                                         clnt_destroy(c->tcpcl);
409                                                         c->tcpcl = NULL;
410                                                         tcp_count--;
411                                                 }
412                                         } else if (result > 0)
413                                                 break;
414                                 }
415                         }
416                         return;
417                 } else if (result > 0)
418                         break;
419         }
420         /* not found; impossible */
421 }
422
423 extern CSA_return_code
424 _DtCm_get_server_rpc_version(char *host, int *version)
425 {
426         CSA_return_code stat;
427         _DtCm_Client_Info *ci;
428
429         if (host == NULL) {
430                 return (CSA_E_INVALID_PARAMETER);
431         }
432
433         if ((stat = _DtCm_create_tcp_client(host, TABLEVERS,
434             _DtCM_INITIAL_TIMEOUT, &ci)) == CSA_SUCCESS)
435                 *version = ci->vers_out;
436
437         return (stat);
438 }
439
440 extern CSA_return_code
441 _DtCm_clntstat_to_csastat(enum clnt_stat clntstat)
442 {
443         switch (clntstat) {
444 #if defined(SunOS) || defined(USL) || defined(__uxp__)
445         case RPC_N2AXLATEFAILURE:
446 #endif
447         case RPC_UNKNOWNHOST:
448                 return (CSA_X_DT_E_INVALID_SERVER_LOCATION);
449         case RPC_PROGNOTREGISTERED:
450                 return (CSA_X_DT_E_SERVICE_NOT_REGISTERED);
451         case RPC_TIMEDOUT:
452                 return (CSA_X_DT_E_SERVER_TIMEOUT);
453         default:
454                 return (CSA_E_SERVICE_UNAVAILABLE);
455         }
456 }
457
458 /*****************************************************************************
459  * static functions used within the file
460  *****************************************************************************/
461
462 static void
463 create_auth(CLIENT *cl)
464 {
465         /* Always cache the Unix style credentials. */
466         if (unix_credential == NULL)
467 #if defined(SunOS) || defined(USL) || defined(__uxp__)
468                 unix_credential = authsys_create_default ();
469 #else
470                 unix_credential = authunix_create_default ();
471 #endif
472
473         cl->cl_auth = unix_credential;
474 }
475
476 static void
477 destroy_auth(CLIENT *cl)
478 {
479         /* It is a no-op for unix-authentication because we always cache it.
480          * But we have to destroy it when secure RPC is used.
481          */
482 }
483
484 /*
485  * Given a host name, find the _DtCm_Client_Info structure which contains
486  * both udp and tcp handle to the server running in the host.
487  */
488 static _DtCm_Client_Info *
489 get_client_info(char *host, int version)
490 {
491         _DtCm_Client_Info *ci;
492         int result;
493
494         if (host==NULL) return(NULL);
495         for (ci = client_cache_head; ci != NULL; ci = ci->next) {
496                 if ((result = strcmp(ci->host, host)) == 0) {
497                         if (ci->vers_out <= version)
498                                 return(ci); 
499                 } else if (result > 0)
500                         break;
501         }
502         return(NULL);
503 }
504
505 static void
506 destroy_target_list(_DtCm_Target_List *tlist)
507 {
508         _DtCm_Target_List *listp, *listitem;
509
510         for (listp = tlist; listp != NULL; ) {
511                 listitem = listp;
512                 listp = listp->next;
513
514                 if (listitem->cal)
515                         free(listitem->cal);
516                 free(listitem);
517         }
518 }
519
520 static void
521 destroy_client_info(_DtCm_Client_Info *ci)
522 {
523         if (ci==NULL) return;
524
525         if (ci->host != NULL)
526                 free(ci->host);
527         if (ci->tcpcl) {
528                 destroy_auth(ci->tcpcl);
529                 clnt_destroy(ci->tcpcl);
530                 tcp_count--;
531         }
532         if (ci->udpcl) {
533                 destroy_auth(ci->udpcl);
534                 clnt_destroy(ci->udpcl);
535         }
536         destroy_target_list(ci->tlist);
537         free(ci);
538         cl_count--;
539 }
540
541 /*
542  * Dont limit the number of cached connections right now.
543  * Udp client handle does not use up file descriptor only space.
544  * Tcp client handle is kept open only when there's at least one
545  * calendar registered with the host and the user probably won't
546  * be browsing more than 50 calendar at the same time.
547  */
548 static void
549 insert_client_info(_DtCm_Client_Info *ci)
550 {
551         _DtCm_Client_Info *citem;
552
553         if (++cl_count > MAX_COUNT)
554                 cleanup_some_connection(ci);
555
556         /* insert new item alphabetically */
557         for (citem = client_cache_head; citem != NULL; citem = citem->next) {
558                 /* there shouldn't be an entry with the same host name
559                  * if there's, it would be picked up in get_client_info()
560                  */
561                 if (strcmp(citem->host, ci->host) > 0)
562                         break;
563         }
564
565         if (citem == NULL) {
566                 if (client_cache_head == NULL)
567                         client_cache_head = client_cache_tail = ci;
568                 else {
569                         ci->prev = client_cache_tail;
570                         client_cache_tail->next = ci;
571                         client_cache_tail = ci;
572                 }
573         } else {
574                 ci->next = citem;
575                 ci->prev = citem->prev;
576                 if (citem == client_cache_head)
577                         client_cache_head = ci;
578                 else
579                         citem->prev->next = ci;
580                 citem->prev = ci;
581         }
582
583 #ifdef CM_DEBUG
584         fprintf(stderr, "%s: head = %d, tail = %d, newitem = %d\n",
585                 "insert_client_info", client_cache_head,
586                 client_cache_tail, ci);
587         fprintf(stderr, "tcp_count = %d, cl_count = %d\n", tcp_count, cl_count);
588 #endif
589
590 }
591
592 /*
593  * remove the client info structure from the list
594  */
595 static void
596 delete_client_info(_DtCm_Client_Info *oldci)
597 {
598         if (oldci == NULL) return;
599
600         if (oldci == client_cache_head) {
601                 client_cache_head = oldci->next;
602                 if (client_cache_head)
603                         client_cache_head->prev = NULL;
604         } else if (oldci == client_cache_tail) {
605                 client_cache_tail = oldci->prev;
606                 if (client_cache_tail)
607                         client_cache_tail->next = NULL;
608         } else {
609                 oldci->prev->next = oldci->next;
610                 oldci->next->prev = oldci->prev;
611         }
612
613         if (oldci == client_cache_tail)
614                 client_cache_tail = NULL;
615
616         destroy_client_info(oldci);
617
618 #ifdef CM_DEBUG
619         fprintf(stderr, "%s: head = %d, tail = %d, olditem = %d\n",
620                 "delete_client_info", client_cache_head,
621                 client_cache_tail, oldci);
622 #endif
623 }
624
625 /*
626  * Number of open tcp connections reaches the maximum.
627  * This is very unlikely in the normal case since
628  * a tcp connection is kept open if at least one calendar
629  * is registered with the host and a user would not be
630  * browsing a large number of calendars at one time.
631  * However, when a calendar is deselected in the calendar
632  * list on the multi-browser window, a lookup call using
633  * the tcp connection is made after the calendar is
634  * deregistered.  This keeps the tcp connection open
635  * even if that's the last calendar registered with the
636  * host.  This routine is used to clean up such tcp connections.
637  * This is a good time to clean up connections that are not
638  * used for a long time.
639  */
640 static void
641 cleanup_some_connection(_DtCm_Client_Info *dontclose)
642 {
643         _DtCm_Client_Info *ci, *oldci;
644         int total = 0, deleted = 0, done = 0;
645
646         for (ci = client_cache_head; ci != NULL; )
647         {
648                 total++;
649 #ifdef HPUX
650                 /* clean up whole list */
651                 if (ci != dontclose && ci->nregistered == 0) {
652 #else
653
654                 if (ci != dontclose && ci->nregistered == 0 &&
655                     (ci->tcpcl || (!done && ci->tcpcl == NULL) ||
656                      (ci->tcpcl==NULL && (time(NULL) - ci->last_used)>DAYSEC)))
657                 {
658                         if (!done) done = 1;
659 #endif
660
661                         deleted++;
662                         oldci = ci;
663                         ci = ci->next;
664                         delete_client_info(oldci);
665                 } else
666                         ci = ci->next;
667         }
668 #ifdef CM_DEBUG
669         fprintf(stderr, "%s: total = %d, deleted = %d\n",
670                 "cleanup_tcp_connection", total, deleted);
671 #endif
672 }
673
674 /*
675  * check registration
676  * Deergister the first target:
677  * if it succeeded, the old server is still running, just re-register it;
678  * else assume that it's a new server so re-register the whole list again.
679  */
680 static void
681 check_registration(_DtCm_Connection *conn)
682 {
683         _DtCm_Target_List *listp, *prev;
684         _DtCm_Transport_Type olduse;
685         CSA_return_code stat;
686
687         if (conn->ci->tlist == NULL)
688                 return;
689
690         olduse = conn->use;
691         conn->use = udp_transport;
692         conn->retry = B_FALSE;
693         if ((stat = _DtCm_do_unregistration(conn, conn->ci->tlist->cal,
694             conn->ci->tlist->update_type)) == CSA_SUCCESS) {
695                 if (_DtCm_do_registration(conn, conn->ci->tlist->cal,
696                         conn->ci->tlist->update_type) != CSA_SUCCESS)
697                 {
698                         conn->ci->nregistered--;
699                         listp =  conn->ci->tlist;
700                         conn->ci->tlist = listp->next;
701                         free(listp->cal);
702                         free(listp);
703                 }
704         } else if (stat == CSA_E_CALLBACK_NOT_REGISTERED || stat == CSA_E_FAILURE) {
705                 for (listp = prev = conn->ci->tlist; listp != NULL; ) {
706                         if (_DtCm_do_registration(conn, listp->cal,
707                             listp->update_type) != CSA_SUCCESS)
708                         {
709                                 conn->ci->nregistered--;
710                                 if (listp == prev)
711                                         conn->ci->tlist = prev = listp->next;
712                                 else
713                                         prev->next = listp->next;
714                                 /* free target item */
715                                 free(listp->cal);
716                                 free(listp);
717                                 listp = (prev ? prev->next : NULL);
718                         } else {
719                                 prev = listp;
720                                 listp = listp->next;
721                         }
722                 }
723         }
724         conn->use = olduse;
725 }
726
727 static _DtCm_Client_Info *
728 get_new_client_handle(_DtCm_Connection *conn)
729 {
730         CLIENT *cl;
731         int oldver;
732
733         if (conn == NULL) return(NULL);
734
735         oldver = conn->ci->vers_out;
736
737         /* always get a udp client handle first */
738 #if defined(SunOS) || defined(USL) || defined(__uxp__)
739         cl = clnt_create_vers(conn->ci->host, TABLEPROG, &(conn->ci->vers_out),
740                         TABLEVERS_2, oldver, "udp");
741         if (cl == NULL) {
742                 _DtCm_print_errmsg(clnt_spcreateerror(conn->ci->host));
743         }
744 #else
745         (void) get_client_handle(conn->ci->host, (u_int)TABLEPROG,
746                         &(conn->ci->vers_out), TABLEVERS_2, oldver,
747                         "udp", &cl);
748 #endif
749
750         if (cl == NULL) {
751                 delete_client_info(conn->ci);
752                 conn->ci = NULL;
753                 return(NULL);
754         } else {
755                 create_auth(cl);
756
757                 /* adjust timeout */
758                 timeout_tv.tv_sec = _DtCM_INITIAL_TIMEOUT;
759                 timeout_tv.tv_usec = 0;
760                 clnt_control(cl, CLSET_TIMEOUT, (char *)&timeout_tv);
761                 retry_tv.tv_sec = _DtCM_INITIAL_TIMEOUT + 10;
762                 retry_tv.tv_usec = 0;
763                 clnt_control(cl, CLSET_RETRY_TIMEOUT, (char *)&retry_tv);
764
765                 destroy_auth(conn->ci->udpcl);
766                 clnt_destroy(conn->ci->udpcl);
767                 conn->ci->udpcl = cl;
768         }
769
770         /* check registration */
771         /* if there's anything wrong, nregistered could be zero */
772         check_registration(conn);
773
774         /* ci might be set to NULL if an rpc call failed */
775         if (conn->ci == NULL)
776                 return (NULL);
777
778         /* now deal with tcp handle */
779
780         /* get rid of old handle first */
781         if (conn->ci->tcpcl) {
782                 destroy_auth(conn->ci->tcpcl);
783                 clnt_destroy(conn->ci->tcpcl);
784                 tcp_count--;
785                 conn->ci->tcpcl = NULL;
786         }
787
788         if (conn->use == udp_transport) {
789                 return(conn->ci);
790         } else {
791
792                 /* get a tcp client handle */
793                 oldver = conn->ci->vers_out;
794 #if defined(SunOS) || defined(USL) || defined(__uxp__)
795                 cl = clnt_create_vers(conn->ci->host, TABLEPROG,
796                         &(conn->ci->vers_out), TABLEVERS_2, oldver, "tcp");
797                 if (cl == NULL)
798                         _DtCm_print_errmsg(clnt_spcreateerror(conn->ci->host));
799 #else
800                 (void) get_client_handle(conn->ci->host, (u_int)TABLEPROG,
801                         &(conn->ci->vers_out), TABLEVERS_2, oldver, "tcp",
802                         &cl);
803 #endif
804
805                 if (cl == NULL) {
806                         conn->ci->vers_out = oldver;
807                         return(NULL);
808                 } else {
809                         create_auth(cl);
810
811                         /* adjust timeout */
812                         timeout_tv.tv_sec = _DtCM_INITIAL_TIMEOUT;
813                         timeout_tv.tv_usec = 0;
814                         clnt_control(cl, CLSET_TIMEOUT, (char *)&timeout_tv);
815
816                         conn->ci->tcpcl = cl;
817                         tcp_count++;
818                         return(conn->ci);
819                 }
820         }
821 }
822
823 /*
824  * Get a client handle to a server that supports the highest
825  * version between the given range.
826  */
827 static CSA_return_code
828 get_client_handle(
829         const char *host,
830 #ifdef __osf__
831         const u_int prognum,
832 #else
833         const u_long prognum,
834 #endif  
835         u_long *vers_outp,
836         const u_long vers_low,
837         const u_long vers_high,
838         char *nettype,
839         CLIENT **clnt)
840 {
841         CLIENT  *cl;
842         u_int   vers;
843         struct timeval tv;
844         enum clnt_stat status;
845
846 #ifdef HPUX
847         static int bumped = 0;
848         struct rlimit rl;
849
850         if (bumped == 0) {
851                 bumped = 1;
852
853                 /* raise the soft limit of number of file descriptor */
854                 getrlimit(RLIMIT_NOFILE, &rl);
855                 rl.rlim_cur = rl.rlim_max;
856                 setrlimit(RLIMIT_NOFILE, &rl);
857         }
858 #endif
859
860 #ifdef __osf__
861         /*
862          * A longer timeout value may be necessay - for example if
863          * the system is bogged down and/or rpc.cmsd is not running.
864          *
865          * The value below is the same value used when a ToolTalk app connects
866          * to the ToolTalk database server (lib/tt/lib/db/tt_db_client.C).
867          */
868         tv.tv_sec = 4;
869 #else
870         tv.tv_sec = 1;
871 #endif
872         tv.tv_usec = 0;
873
874         *clnt = NULL;
875         for (vers = vers_high; vers >= vers_low; vers--) {
876 #if defined(__osf__) || defined(__hpux)
877                 if ((cl = clnt_create((char *)host, prognum, vers, nettype)) != NULL) {
878 #else
879
880                 if ((cl = clnt_create(host, prognum, vers, nettype)) != NULL) {
881 #endif
882                         clnt_control(cl, CLSET_TIMEOUT, (char *)&tv);
883                         status = clnt_call(cl, 0, (xdrproc_t) xdr_void, 
884                                            (char *)NULL, (xdrproc_t) xdr_void,
885                                            (char *)NULL, tv);
886
887                         if (status == RPC_SUCCESS) {
888                                 *vers_outp = vers;
889                                 *clnt = cl;
890 #ifdef __osf__
891                                 /*
892                                  * Set the timeout back to the original.
893                                  */
894                                 tv.tv_sec = 1;
895                                 clnt_control(cl, CLSET_TIMEOUT, (char *)&tv);
896 #endif
897                                 return (CSA_SUCCESS);
898                         } else if (status != RPC_PROGVERSMISMATCH) {
899                                 return (_DtCm_clntstat_to_csastat(status));
900                         }
901                 } else {
902                         _DtCm_print_errmsg(clnt_spcreateerror((char *) host));
903                         return (_DtCm_clntstat_to_csastat(rpc_createerr.cf_stat));
904                 }
905         }
906
907         /* cannot find a server that supports a version in the given range */
908         /* Probably will never get here */
909         return (CSA_E_SERVICE_UNAVAILABLE);
910 }
911
912 static CSA_return_code
913 regstat4_to_dtcmstatus(Registration_Status_4 stat4)
914 {
915         switch (stat4) {
916         case registered_4:
917                 return (CSA_SUCCESS);
918
919         case deregistered_4:
920                 return (CSA_SUCCESS);
921
922         case reg_notable_4:
923                 return (CSA_E_CALENDAR_NOT_EXIST);
924
925         case failed_4:
926         case confused_4:
927                 return (CSA_E_FAILURE);
928         }
929 }
930