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