2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
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)
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
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
23 /* $TOG: connection.c /main/4 1999/10/14 17:47:12 mgreess $ */
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.
32 * This file manages server connections.
35 #include <EUSCompat.h>
43 #include <sys/resource.h>
45 #include "connection.h"
52 #include "convert2-4.h"
53 #include "convert3-4.h"
62 static struct timeval timeout_tv;
63 static struct timeval retry_tv;
64 static AUTH *unix_credential = NULL;
67 static _DtCm_Client_Info *client_cache_head = NULL;
68 static _DtCm_Client_Info *client_cache_tail = NULL;
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 static CSA_return_code get_client_handle(const char *host, const u_long prognum,
84 u_long *vers_outp, const u_long vers_low,
85 const u_long vers_high, char *nettype,
87 static CSA_return_code regstat4_to_dtcmstatus(Registration_Status_4 stat4);
89 extern CSA_return_code
90 _DtCm_create_udp_client(
94 _DtCm_Client_Info **clnt)
97 _DtCm_Client_Info *ci;
101 if (host == NULL || clnt == NULL)
102 return (CSA_E_INVALID_PARAMETER);
104 /* if client info is found, we have at least the udp handle */
105 if (((*clnt) = get_client_info(host, version)) != NULL) {
106 return (CSA_SUCCESS);
110 cl = clnt_create_vers(host, TABLEPROG,
111 &vers_out, TABLEVERS_2, version, "udp");
113 _DtCm_print_errmsg(clnt_spcreateerror(host));
114 return (_DtCm_clntstat_to_csastat(rpc_createerr.cf_stat));
117 stat = get_client_handle(host, (u_int)TABLEPROG, &vers_out, TABLEVERS_2,
118 version, "udp", &cl);
119 if (stat != CSA_SUCCESS)
123 /* if version is lower than requested, check the list again */
124 if (vers_out < version) {
125 if ((ci = get_client_info(host, vers_out)) != NULL) {
128 return (CSA_SUCCESS);
135 if (timeout==0) timeout = _DtCM_DEFAULT_TIMEOUT;
136 timeout_tv.tv_sec = timeout;
137 timeout_tv.tv_usec = 0;
138 clnt_control(cl, CLSET_TIMEOUT, (char*)&timeout_tv);
141 time rpc waits for server to reply before retransmission =
142 'timeout'. since the retry timeout is set to timeout + 10;
143 this guarantees there won't
144 be any retransmisssions resulting in duplicate
145 transactions in the database.
148 retry_tv.tv_sec = timeout + 10;
149 retry_tv.tv_usec = 0;
150 clnt_control(cl, CLSET_RETRY_TIMEOUT, (char*)&retry_tv);
152 if ((ci = (_DtCm_Client_Info *)calloc(1, sizeof(_DtCm_Client_Info))) == NULL) {
155 return (CSA_E_INSUFFICIENT_MEMORY);
158 if ((ci->host = strdup(host)) == NULL) {
162 return (CSA_E_INSUFFICIENT_MEMORY);
166 ci->vers_out = vers_out;
167 insert_client_info(ci);
169 return (CSA_SUCCESS);
173 * Creates tcp client handle. Used for calls that potentially return
174 * large amount of data. If it fails to create a tcp client handle,
175 * a udp client handle will be returned.
177 extern CSA_return_code
178 _DtCm_create_tcp_client(
182 _DtCm_Client_Info **clnt)
184 CSA_return_code stat;
185 _DtCm_Client_Info *ci;
189 if (host == NULL || clnt == NULL)
190 return (CSA_E_INVALID_PARAMETER);
192 /* Get a udp client handle. This serves two purposes: */
193 /* - to get a udp handle for an old server which talks only udp */
194 /* - to invoke a server through inetd since only udp is registered.*/
196 if ((stat = _DtCm_create_udp_client(host, version, timeout, &ci))
199 } else if (ci->tcpcl) {
201 return (CSA_SUCCESS);
203 /* create tcp connection */
205 cl = clnt_create_vers(host, TABLEPROG, &vers_out,
206 TABLEVERS_2, version, "tcp");
208 stat = get_client_handle(host, (u_int)TABLEPROG, &vers_out,
209 TABLEVERS_2, version, "tcp", &cl);
212 /* if can't create tcp connection, use udp */
214 _DtCm_print_errmsg(clnt_spcreateerror(host));
216 return (CSA_SUCCESS);
222 if (timeout==0) timeout = _DtCM_DEFAULT_TIMEOUT;
223 timeout_tv.tv_sec = timeout;
224 timeout_tv.tv_usec = 0;
225 clnt_control(cl, CLSET_TIMEOUT, (char*)&timeout_tv);
227 /* dont need to set vers_out since it should
228 * be the same as that of the udp transport
231 if (++tcp_count > MAX_COUNT)
232 /* clean up tcp connections */
233 cleanup_some_connection(ci);
235 return (CSA_SUCCESS);
240 * Used instead of clnt_call by rtableX_clnt.c
242 * Might need locking for the client handle here since
243 * it might be purged if something's wrong
245 extern enum clnt_stat
247 _DtCm_Connection *conn,
255 _DtCm_Client_Info *ci;
256 _DtCm_Transport_Type ttype;
257 enum clnt_stat status = RPC_FAILED;
258 int retry = conn->retry;
261 if (conn->ci == NULL)
265 ci->last_used = time(0);
268 if (conn->use == udp_transport || ci->tcpcl == NULL)
269 ttype = udp_transport;
271 ttype = tcp_transport;
273 status = clnt_call((ttype == tcp_transport ? ci->tcpcl :
274 ci->udpcl), proc, inproc, in,
277 if ((ttype == udp_transport && status == RPC_TIMEDOUT) ||
278 (status == RPC_CANTRECV)) {
283 /* don't retry when stat is RPC_TIMEDOUT
284 * and transpart is tcp since if the server
285 * is down, stat would be something else
289 /* get new client handle */
290 if (get_new_client_handle(conn) == NULL)
294 /* purge the client handle */
295 delete_client_info(conn->ci);
303 if (status != RPC_SUCCESS && conn->ci != NULL) {
304 _DtCm_print_errmsg(clnt_sperror((ttype == tcp_transport ? ci->tcpcl :
305 ci->udpcl), ci->host));
311 extern CSA_return_code
312 _DtCm_add_registration(
313 _DtCm_Client_Info *ci,
315 unsigned long update_type)
317 _DtCm_Target_List *listp, *prev;
318 _DtCm_Target_List *listitem;
321 if (ci == NULL || cal == NULL)
322 return (CSA_E_INVALID_PARAMETER);
324 for (listp = prev = ci->tlist; listp != NULL;
325 prev = listp, listp = listp->next) {
326 if ((result = strcmp(listp->cal, cal)) == 0) {
327 /* registered already */
328 return (CSA_SUCCESS);
329 } else if (result > 0)
333 /* register the first time, insert in list in ascending order */
334 if ((listitem = (_DtCm_Target_List *)calloc(1, sizeof(_DtCm_Target_List))) == NULL)
335 return (CSA_E_INSUFFICIENT_MEMORY);
337 if ((listitem->cal = strdup(cal)) == NULL) {
339 return (CSA_E_INSUFFICIENT_MEMORY);
342 listitem->update_type = update_type;
344 if (prev == NULL || listp == prev)
345 ci->tlist = listitem;
347 prev->next = listitem;
348 listitem->next = listp;
352 return (CSA_SUCCESS);
356 _DtCm_remove_registration(_DtCm_Client_Info *ci, char *cal)
358 _DtCm_Target_List *listp, *prev;
359 _DtCm_Client_Info *c;
362 if (cal == NULL) return;
364 /* if found, just increment the number of registration */
365 for (listp = prev = ci->tlist; listp != NULL;
366 prev = listp, listp = listp->next) {
367 if ((result = strcmp(listp->cal, cal)) == 0) {
369 ci->tlist = listp->next;
371 prev->next = listp->next;
373 /* free target item */
378 * if no calendar is registered, close tcp connection
380 if (--(ci->nregistered) == 0) {
382 destroy_auth(ci->tcpcl);
383 clnt_destroy(ci->tcpcl);
388 /* find other tcp connection for the
391 for (c = client_cache_head; c != NULL;
393 if ((result = strcmp(c->host,
395 if (c->nregistered == 0 &&
397 destroy_auth(c->tcpcl);
398 clnt_destroy(c->tcpcl);
402 } else if (result > 0)
407 } else if (result > 0)
410 /* not found; impossible */
413 extern CSA_return_code
414 _DtCm_get_server_rpc_version(char *host, int *version)
416 CSA_return_code stat;
417 _DtCm_Client_Info *ci;
420 return (CSA_E_INVALID_PARAMETER);
423 if ((stat = _DtCm_create_tcp_client(host, TABLEVERS,
424 _DtCM_INITIAL_TIMEOUT, &ci)) == CSA_SUCCESS)
425 *version = ci->vers_out;
430 extern CSA_return_code
431 _DtCm_clntstat_to_csastat(enum clnt_stat clntstat)
435 case RPC_N2AXLATEFAILURE:
437 case RPC_UNKNOWNHOST:
438 return (CSA_X_DT_E_INVALID_SERVER_LOCATION);
439 case RPC_PROGNOTREGISTERED:
440 return (CSA_X_DT_E_SERVICE_NOT_REGISTERED);
442 return (CSA_X_DT_E_SERVER_TIMEOUT);
444 return (CSA_E_SERVICE_UNAVAILABLE);
448 /*****************************************************************************
449 * static functions used within the file
450 *****************************************************************************/
453 create_auth(CLIENT *cl)
455 /* Always cache the Unix style credentials. */
456 if (unix_credential == NULL)
458 unix_credential = authsys_create_default ();
460 unix_credential = authunix_create_default ();
463 cl->cl_auth = unix_credential;
467 destroy_auth(CLIENT *cl)
469 /* It is a no-op for unix-authentication because we always cache it.
470 * But we have to destroy it when secure RPC is used.
475 * Given a host name, find the _DtCm_Client_Info structure which contains
476 * both udp and tcp handle to the server running in the host.
478 static _DtCm_Client_Info *
479 get_client_info(char *host, int version)
481 _DtCm_Client_Info *ci;
484 if (host==NULL) return(NULL);
485 for (ci = client_cache_head; ci != NULL; ci = ci->next) {
486 if ((result = strcmp(ci->host, host)) == 0) {
487 if (ci->vers_out <= version)
489 } else if (result > 0)
496 destroy_target_list(_DtCm_Target_List *tlist)
498 _DtCm_Target_List *listp, *listitem;
500 for (listp = tlist; listp != NULL; ) {
511 destroy_client_info(_DtCm_Client_Info *ci)
513 if (ci==NULL) return;
515 if (ci->host != NULL)
518 destroy_auth(ci->tcpcl);
519 clnt_destroy(ci->tcpcl);
523 destroy_auth(ci->udpcl);
524 clnt_destroy(ci->udpcl);
526 destroy_target_list(ci->tlist);
532 * Dont limit the number of cached connections right now.
533 * Udp client handle does not use up file descriptor only space.
534 * Tcp client handle is kept open only when there's at least one
535 * calendar registered with the host and the user probably won't
536 * be browsing more than 50 calendar at the same time.
539 insert_client_info(_DtCm_Client_Info *ci)
541 _DtCm_Client_Info *citem;
543 if (++cl_count > MAX_COUNT)
544 cleanup_some_connection(ci);
546 /* insert new item alphabetically */
547 for (citem = client_cache_head; citem != NULL; citem = citem->next) {
548 /* there shouldn't be an entry with the same host name
549 * if there's, it would be picked up in get_client_info()
551 if (strcmp(citem->host, ci->host) > 0)
556 if (client_cache_head == NULL)
557 client_cache_head = client_cache_tail = ci;
559 ci->prev = client_cache_tail;
560 client_cache_tail->next = ci;
561 client_cache_tail = ci;
565 ci->prev = citem->prev;
566 if (citem == client_cache_head)
567 client_cache_head = ci;
569 citem->prev->next = ci;
574 fprintf(stderr, "%s: head = %d, tail = %d, newitem = %d\n",
575 "insert_client_info", client_cache_head,
576 client_cache_tail, ci);
577 fprintf(stderr, "tcp_count = %d, cl_count = %d\n", tcp_count, cl_count);
583 * remove the client info structure from the list
586 delete_client_info(_DtCm_Client_Info *oldci)
588 if (oldci == NULL) return;
590 if (oldci == client_cache_head) {
591 client_cache_head = oldci->next;
592 if (client_cache_head)
593 client_cache_head->prev = NULL;
594 } else if (oldci == client_cache_tail) {
595 client_cache_tail = oldci->prev;
596 if (client_cache_tail)
597 client_cache_tail->next = NULL;
599 oldci->prev->next = oldci->next;
600 oldci->next->prev = oldci->prev;
603 if (oldci == client_cache_tail)
604 client_cache_tail = NULL;
606 destroy_client_info(oldci);
609 fprintf(stderr, "%s: head = %d, tail = %d, olditem = %d\n",
610 "delete_client_info", client_cache_head,
611 client_cache_tail, oldci);
616 * Number of open tcp connections reaches the maximum.
617 * This is very unlikely in the normal case since
618 * a tcp connection is kept open if at least one calendar
619 * is registered with the host and a user would not be
620 * browsing a large number of calendars at one time.
621 * However, when a calendar is deselected in the calendar
622 * list on the multi-browser window, a lookup call using
623 * the tcp connection is made after the calendar is
624 * deregistered. This keeps the tcp connection open
625 * even if that's the last calendar registered with the
626 * host. This routine is used to clean up such tcp connections.
627 * This is a good time to clean up connections that are not
628 * used for a long time.
631 cleanup_some_connection(_DtCm_Client_Info *dontclose)
633 _DtCm_Client_Info *ci, *oldci;
634 int total = 0, deleted = 0, done = 0;
636 for (ci = client_cache_head; ci != NULL; )
640 /* clean up whole list */
641 if (ci != dontclose && ci->nregistered == 0) {
644 if (ci != dontclose && ci->nregistered == 0 &&
645 (ci->tcpcl || (!done && ci->tcpcl == NULL) ||
646 (ci->tcpcl==NULL && (time(NULL) - ci->last_used)>DAYSEC)))
654 delete_client_info(oldci);
659 fprintf(stderr, "%s: total = %d, deleted = %d\n",
660 "cleanup_tcp_connection", total, deleted);
666 * Deergister the first target:
667 * if it succeeded, the old server is still running, just re-register it;
668 * else assume that it's a new server so re-register the whole list again.
671 check_registration(_DtCm_Connection *conn)
673 _DtCm_Target_List *listp, *prev;
674 _DtCm_Transport_Type olduse;
675 CSA_return_code stat;
677 if (conn->ci->tlist == NULL)
681 conn->use = udp_transport;
682 conn->retry = B_FALSE;
683 if ((stat = _DtCm_do_unregistration(conn, conn->ci->tlist->cal,
684 conn->ci->tlist->update_type)) == CSA_SUCCESS) {
685 if (_DtCm_do_registration(conn, conn->ci->tlist->cal,
686 conn->ci->tlist->update_type) != CSA_SUCCESS)
688 conn->ci->nregistered--;
689 listp = conn->ci->tlist;
690 conn->ci->tlist = listp->next;
694 } else if (stat == CSA_E_CALLBACK_NOT_REGISTERED || stat == CSA_E_FAILURE) {
695 for (listp = prev = conn->ci->tlist; listp != NULL; ) {
696 if (_DtCm_do_registration(conn, listp->cal,
697 listp->update_type) != CSA_SUCCESS)
699 conn->ci->nregistered--;
701 conn->ci->tlist = prev = listp->next;
703 prev->next = listp->next;
704 /* free target item */
707 listp = (prev ? prev->next : NULL);
717 static _DtCm_Client_Info *
718 get_new_client_handle(_DtCm_Connection *conn)
723 if (conn == NULL) return(NULL);
725 oldver = conn->ci->vers_out;
727 /* always get a udp client handle first */
729 cl = clnt_create_vers(conn->ci->host, TABLEPROG, &(conn->ci->vers_out),
730 TABLEVERS_2, oldver, "udp");
732 _DtCm_print_errmsg(clnt_spcreateerror(conn->ci->host));
735 (void) get_client_handle(conn->ci->host, (u_int)TABLEPROG,
736 &(conn->ci->vers_out), TABLEVERS_2, oldver,
741 delete_client_info(conn->ci);
748 timeout_tv.tv_sec = _DtCM_INITIAL_TIMEOUT;
749 timeout_tv.tv_usec = 0;
750 clnt_control(cl, CLSET_TIMEOUT, (char *)&timeout_tv);
751 retry_tv.tv_sec = _DtCM_INITIAL_TIMEOUT + 10;
752 retry_tv.tv_usec = 0;
753 clnt_control(cl, CLSET_RETRY_TIMEOUT, (char *)&retry_tv);
755 destroy_auth(conn->ci->udpcl);
756 clnt_destroy(conn->ci->udpcl);
757 conn->ci->udpcl = cl;
760 /* check registration */
761 /* if there's anything wrong, nregistered could be zero */
762 check_registration(conn);
764 /* ci might be set to NULL if an rpc call failed */
765 if (conn->ci == NULL)
768 /* now deal with tcp handle */
770 /* get rid of old handle first */
771 if (conn->ci->tcpcl) {
772 destroy_auth(conn->ci->tcpcl);
773 clnt_destroy(conn->ci->tcpcl);
775 conn->ci->tcpcl = NULL;
778 if (conn->use == udp_transport) {
782 /* get a tcp client handle */
783 oldver = conn->ci->vers_out;
785 cl = clnt_create_vers(conn->ci->host, TABLEPROG,
786 &(conn->ci->vers_out), TABLEVERS_2, oldver, "tcp");
788 _DtCm_print_errmsg(clnt_spcreateerror(conn->ci->host));
790 (void) get_client_handle(conn->ci->host, (u_int)TABLEPROG,
791 &(conn->ci->vers_out), TABLEVERS_2, oldver, "tcp",
796 conn->ci->vers_out = oldver;
802 timeout_tv.tv_sec = _DtCM_INITIAL_TIMEOUT;
803 timeout_tv.tv_usec = 0;
804 clnt_control(cl, CLSET_TIMEOUT, (char *)&timeout_tv);
806 conn->ci->tcpcl = cl;
814 * Get a client handle to a server that supports the highest
815 * version between the given range.
817 static CSA_return_code
820 const u_long prognum,
822 const u_long vers_low,
823 const u_long vers_high,
830 enum clnt_stat status;
833 static int bumped = 0;
839 /* raise the soft limit of number of file descriptor */
840 getrlimit(RLIMIT_NOFILE, &rl);
841 rl.rlim_cur = rl.rlim_max;
842 setrlimit(RLIMIT_NOFILE, &rl);
850 for (vers = vers_high; vers >= vers_low; vers--) {
852 if ((cl = clnt_create((char *)host, prognum, vers, nettype)) != NULL) {
855 if ((cl = clnt_create(host, prognum, vers, nettype)) != NULL) {
857 clnt_control(cl, CLSET_TIMEOUT, (char *)&tv);
858 status = clnt_call(cl, 0, (xdrproc_t) xdr_void,
859 (char *)NULL, (xdrproc_t) xdr_void,
862 if (status == RPC_SUCCESS) {
865 return (CSA_SUCCESS);
866 } else if (status != RPC_PROGVERSMISMATCH) {
867 return (_DtCm_clntstat_to_csastat(status));
870 _DtCm_print_errmsg(clnt_spcreateerror((char *) host));
871 return (_DtCm_clntstat_to_csastat(rpc_createerr.cf_stat));
875 /* cannot find a server that supports a version in the given range */
876 /* Probably will never get here */
877 return (CSA_E_SERVICE_UNAVAILABLE);
880 static CSA_return_code
881 regstat4_to_dtcmstatus(Registration_Status_4 stat4)
885 return (CSA_SUCCESS);
888 return (CSA_SUCCESS);
891 return (CSA_E_CALENDAR_NOT_EXIST);
895 return (CSA_E_FAILURE);