1
2/*
3Copyright 1996, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included
12in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
18OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall
23not be used in advertising or otherwise to promote the sale, use or
24other dealings in this Software without prior written authorization
25from The Open Group.
26*/
27
28#include "pmint.h"
29#include "pmdb.h"
30#include "config.h"
31#include <assert.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <signal.h>
35
36typedef void (*Signal_Handler)(int);
37
38static proxy_service *proxyServiceList = NULL;
39
40static Signal_Handler
41Signal (int sig, Signal_Handler handler)
42{
43#ifndef X_NOT_POSIX
44    struct sigaction sigact, osigact;
45    sigact.sa_handler = handler;
46    sigemptyset(&sigact.sa_mask);
47    sigact.sa_flags = 0;
48    sigaction(sig, &sigact, &osigact);
49    return osigact.sa_handler;
50#else
51    return signal(sig, handler);
52#endif
53}
54
55proxy_service *
56FindProxyService (
57    char *serviceName,
58    Bool createIf)
59
60{
61    proxy_service *service = proxyServiceList;
62    int nameLen = strlen (serviceName);
63
64    while (service)
65    {
66	if (strcmp (service->serviceName, serviceName) == 0)
67	    return service;
68	else if (ncasecmp (service->serviceName, serviceName, nameLen) == 0)
69	    return service;
70	else
71	    service = service->next;
72    }
73
74    if (createIf) {
75	service = malloc (sizeof (proxy_service));
76	if (!service)
77	    return NULL;
78
79	service->serviceName = strdup (serviceName);
80	if (!service->serviceName)
81	{
82	    free (service);
83	    return NULL;
84	}
85
86	service->proxyCount = 0;
87	service->proxyList = NULL;
88
89	if (proxyServiceList == NULL)
90	{
91	    proxyServiceList = service;
92	    service->next = NULL;
93	}
94	else
95	{
96	    service->next = proxyServiceList;
97	    proxyServiceList = service;
98	}
99    }
100
101    return service;
102
103}
104
105
106running_proxy *
107StartNewProxy (
108    char *serviceName,
109    char *startCommand)
110
111{
112    proxy_service *service = FindProxyService (serviceName, True);
113    running_proxy *proxy;
114
115    if (!service)
116	return NULL;
117
118    proxy = malloc (sizeof (running_proxy));
119    if (!proxy)
120	return NULL;
121
122    proxy->active = 0;
123    proxy->pmConn = NULL;
124    proxy->requests = NULL;
125    proxy->servers = NULL;
126    proxy->refused_service = False;
127
128    if (service->proxyList == NULL)
129    {
130	service->proxyList = proxy;
131	proxy->next = NULL;
132    }
133    else
134    {
135	proxy->next = service->proxyList;
136	service->proxyList = proxy;
137    }
138
139    if (system (startCommand) == -1)
140    {
141	printf ("unable to start managed proxy: %s\n", startCommand);
142	service->proxyList = proxy->next;
143	free (proxy);
144	return NULL;
145    }
146
147    if (verbose) {
148	printf ("started managed proxy: %s\n", startCommand);
149	printf ("waiting for StartProxy message\n");
150    }
151
152    service->proxyCount++;
153
154    return proxy;
155}
156
157/*
158 * ConnectToProxy( pmOpcode, serviceName, proxyAddress )
159 *
160 * Connects to an unmanaged proxy to forward the GetProxyAddr request
161 * to it to handle.
162 *
163 * Ideally this would be non-blocking but there is no non-blocking
164 * variant of IceOpenConnection/IceProtocolSetup.  So we sit here a while.
165 */
166running_proxy *
167ConnectToProxy (
168    int pmOpcode,
169    char *serviceName,
170    char *proxyAddress)
171
172{
173    proxy_service *service = FindProxyService (serviceName, True);
174    running_proxy *proxy;
175    IceConn proxy_iceConn;
176    PMconn *pmConn;
177
178    if (!service)
179	return NULL;
180
181    {
182	int majorVersion, minorVersion;
183	char *vendor, *release;
184	char errorString[256];
185
186	/*
187	 * IceOpenConnection will do more than one write to the proxy.
188	 * If the proxy closes the connection before the second write,
189	 * the second write may generate a SIGPIPE (empirically this
190	 * happens on at least AIX).  So, temporarily ignore this signal.
191	 */
192
193	Signal (SIGPIPE, SIG_IGN);
194
195	proxy_iceConn = IceOpenConnection( proxyAddress, NULL,
196					   False, pmOpcode,
197					   sizeof(errorString), errorString);
198
199	Signal (SIGPIPE, SIG_DFL);
200
201	if (! proxy_iceConn) {
202	    printf("unable to open connection to unmanaged proxy \"%s\" at %s\n",
203		   serviceName, proxyAddress);
204	    return NULL;
205	}
206
207        /*
208	 * Mark this fd to be closed upon exec
209	 */
210        SetCloseOnExec (IceConnectionNumber (proxy_iceConn));
211
212	/* See PMprotocolSetupProc */
213	pmConn = malloc (sizeof (PMconn));
214
215	if (pmConn == NULL) {
216	    IceCloseConnection (proxy_iceConn);
217	    return NULL;
218	}
219
220	if (IceProtocolSetup (proxy_iceConn, pmOpcode,
221			      (IcePointer)pmConn, /* client_data */
222			      False, /* must_authenticate */
223			      &majorVersion, &minorVersion,
224			      &vendor, &release,
225			      sizeof(errorString), errorString)
226	        != IceProtocolSetupSuccess) {
227	    IceCloseConnection (proxy_iceConn);
228	    free (pmConn);
229	    printf ("Could not initialize proxy management protocol with\n  unmanaged proxy \"%s\" at address %s:\n  %s\n",
230		    serviceName, proxyAddress, errorString);
231	    return NULL;
232	}
233
234	pmConn->iceConn = proxy_iceConn;
235	pmConn->pmOpcode = pmOpcode;
236	pmConn->proto_major_version = majorVersion;
237	pmConn->proto_minor_version = minorVersion;
238	pmConn->vendor = vendor;
239	pmConn->release = release;
240    }
241
242    proxy = malloc (sizeof (running_proxy));
243    if (!proxy) {
244	IceCloseConnection (proxy_iceConn);
245	free (pmConn);
246	return NULL;
247    }
248
249    proxy->active = 1;
250    proxy->pmConn = pmConn;
251    proxy->requests = NULL;
252    proxy->servers = NULL;
253    proxy->refused_service = False;
254
255    if (service->proxyList == NULL)
256    {
257	service->proxyList = proxy;
258	proxy->next = NULL;
259    }
260    else
261    {
262	proxy->next = service->proxyList;
263	service->proxyList = proxy;
264    }
265
266    if (verbose)
267	printf ("connected to unmanaged proxy: %s at %s\n",
268		serviceName, proxyAddress);
269
270    service->proxyCount++;
271
272    return proxy;
273}
274
275
276Status
277ActivateProxyService (
278    char *serviceName,
279    PMconn *pmConn)
280
281{
282    proxy_service *service = FindProxyService (serviceName, False);
283    running_proxy *proxy;
284
285    if (!service)
286	return 0;
287
288    proxy = service->proxyList;
289
290    while (proxy)
291    {
292	if (!proxy->active)
293	{
294	    proxy->active = 1;
295	    proxy->pmConn = pmConn;
296	    return 1;
297	}
298	else
299	    proxy = proxy->next;
300    }
301
302    return 0;
303}
304
305
306void
307ProxyGone (
308    IceConn proxyIceConn,
309    Bool *activeReqs)
310
311{
312    proxy_service *service = proxyServiceList;
313
314    while (service)
315    {
316	running_proxy *proxy = service->proxyList;
317	running_proxy *prevProxy = NULL;
318
319	while (proxy)
320	{
321	    if (proxy->pmConn && (proxy->pmConn->iceConn == proxyIceConn))
322	    {
323		server_list *server;
324		request_list *req;
325
326		if (verbose)
327		    printf ("Proxy disconnected on fd %d",
328			    IceConnectionNumber(proxyIceConn));
329
330		server = proxy->servers;
331		if (verbose && server)
332		    fputs (" for server", stdout);
333
334		while (server)
335		{
336		    server_list *next_server = server->next;
337		    if (verbose) {
338			fputc (' ', stdout);
339			fputs (server->serverAddress, stdout);
340		    }
341		    free (server->serverAddress);
342		    free (server);
343		    server = next_server;
344		}
345
346		if (verbose)
347		    fputc ('\n', stdout);
348
349		if (prevProxy == NULL)
350		    service->proxyList = proxy->next;
351		else
352		    prevProxy->next = proxy->next;
353
354		service->proxyCount--;
355
356		*activeReqs = proxy->requests != NULL;
357		req = proxy->requests;
358		while (req)
359		{
360		    request_list *nextreq = req->next;
361
362		    if (req->requestor) {
363			assert (req->requestor->iceConn != NULL);
364			if (verbose)
365			    printf ("Reprocessing request from fd %d for service %s at %s\n",
366				    IceConnectionNumber (req->requestor->iceConn),
367				    req->serviceName, req->serverAddress);
368
369			ForwardRequest( req->requestor,
370					req->serviceName, req->serverAddress,
371					req->hostAddress, req->startOptions,
372					req->authLen, req->authName,
373					req->authData);
374		    }
375		    free (req->serviceName);
376		    free (req->serverAddress);
377		    free (req->hostAddress);
378		    free (req->startOptions);
379		    free (req->listData);	/* proxyList */
380		    free (req->authName);
381		    free (req->authData);
382		    free (req);
383		    req = nextreq;
384		}
385
386		free (proxy);
387		return;
388	    }
389	    else if (proxy->requests) {
390		/*
391		 * If it wasn't a proxy that disconnected, so it might
392		 * have been a requestor.  Search through all the requests
393		 * while we're here and look for a match.  If found, delete
394		 * the request.
395		 */
396		request_list **prev_reqP = &proxy->requests;
397		request_list *req = proxy->requests;
398		while (req) {
399		    if (req->requestor->iceConn == proxyIceConn) {
400			if (verbose) {
401			    printf ("Requestor disconnected on fd %d while awaiting reply\n  for service %s (%s)",
402				    IceConnectionNumber(proxyIceConn),
403				    req->serviceName, req->serverAddress);
404			    if (proxy->pmConn && proxy->pmConn->iceConn) {
405				printf (" from proxy on fd %d\n",
406					IceConnectionNumber(proxy->pmConn->iceConn));
407			    }
408			    else
409				fputc ('\n', stdout);
410			}
411
412			*prev_reqP = req->next;
413
414			free (req->serviceName);
415			free (req->serverAddress);
416			free (req->hostAddress);
417			free (req->startOptions);
418			free (req->listData);	/* proxyList */
419			free (req->authName);
420			free (req->authData);
421			free (req);
422
423			/* return; */ /* should but only one req, but... */
424		    }
425		    else
426			prev_reqP = &req->next;
427
428		    req = *prev_reqP;
429		}
430	    }
431
432	    prevProxy = proxy;
433	    proxy = proxy->next;
434	}
435
436	service = service->next;
437    }
438}
439
440
441/*
442 * GetRuningProxyList returns a list of current proxies for a given
443 * service.  The list is ordered, with proxies serving an address that
444 * matches the argument appearing first on the list and all others
445 * appearing at the end.  If a proxy ever refused a request for additional
446 * service then it is excluded from the list if it doesn't match the
447 * server address.
448 */
449running_proxy_list *
450GetRunningProxyList (
451    char *serviceName, char *serverAddress)
452
453{
454    proxy_service *service = FindProxyService (serviceName, False);
455    running_proxy **proxyList, *proxy;
456    running_proxy_list *runList;
457    int headIndex, tailIndex;
458
459    if (!service || !service->proxyCount)
460	return NULL;
461
462    runList = malloc (sizeof (running_proxy_list) +
463	service->proxyCount * sizeof (running_proxy *));
464
465    if (!runList)
466	return NULL;
467
468    runList->count = 0;
469    runList->current = 0;
470    runList->list = proxyList = (running_proxy **) (runList + 1);
471
472    proxy = service->proxyList;
473    headIndex = 0;
474    tailIndex = service->proxyCount - 1;
475
476    while (proxy)
477    {
478	server_list *server = proxy->servers;
479	int match = 0;
480
481	while (server)
482	{
483	    if (strcmp (server->serverAddress, serverAddress) == 0)
484	    {
485		match = 1;
486		break;
487	    }
488
489	    server = server->next;
490	}
491
492	if (match) {
493	    proxyList[headIndex++] = proxy;
494	    runList->count++;
495	}
496	else if (! proxy->refused_service) {
497	    proxyList[tailIndex--] = proxy;
498	    runList->count++;
499	}
500
501	proxy = proxy->next;
502    }
503
504    if (!runList->count) {
505	free ((char*)runList);
506	return NULL;
507    }
508
509    /* if we didn't fill the list due to skipping proxies that had previously
510     * refused to service a new address, then remove the gaps in the list
511     * between the matched and unmatched server names
512     */
513    if (runList->count < service->proxyCount) {
514	while (tailIndex < service->proxyCount - 1)
515	    proxyList[headIndex++] = proxyList[++tailIndex];
516    }
517
518    return runList;
519}
520
521
522void
523FreeProxyList (running_proxy_list *list)
524
525{
526    free (list);
527}
528
529
530Status
531PushRequestorQueue (
532    running_proxy *proxy,
533    PMconn *requestor,
534    running_proxy_list *runList,
535    char *serviceName,
536    char *serverAddress,
537    char *hostAddress,
538    char *startOptions,
539    int authLen,
540    char *authName,
541    char *authData)
542
543{
544    request_list *newreq = malloc (sizeof (request_list));
545
546    if (!newreq)
547	return 0;
548
549    newreq->serviceName = strdup (serviceName);
550    newreq->serverAddress = strdup (serverAddress);
551    newreq->hostAddress = strdup (hostAddress);
552    newreq->startOptions = strdup (startOptions);
553    if (authLen > 0)
554    {
555	newreq->authName = strdup (authName);
556	newreq->authData = malloc (authLen);
557    }
558    else
559	newreq->authName = newreq->authData = NULL;
560
561    if (!newreq->serviceName ||
562	!newreq->serverAddress ||
563	!newreq->hostAddress ||
564	!newreq->startOptions ||
565	(authLen > 0 && (!newreq->authName || !newreq->authData)))
566    {
567	free (newreq->serviceName);
568	free (newreq->serverAddress);
569	free (newreq->hostAddress);
570	free (newreq->startOptions);
571	free (newreq->authName);
572	free (newreq->authData);
573	free (newreq);
574	return 0;
575    }
576
577    if (authLen > 0)
578    {
579	memcpy (newreq->authData, authData, authLen);
580    }
581
582    newreq->requestor = requestor;
583    newreq->listData = (char *) runList;
584    newreq->authLen = authLen;
585    newreq->next = NULL;
586
587    if (proxy->requests == NULL)
588	proxy->requests = newreq;
589    else
590    {
591	request_list *p = proxy->requests;
592
593	while (p->next)
594	    p = p->next;
595
596	p->next = newreq;
597    }
598
599    return 1;
600}
601
602
603Status
604PeekRequestorQueue (
605    PMconn *pmConn,
606    PMconn **requestor,
607    running_proxy_list **runList,
608    char **serviceName,
609    char **serverAddress,
610    char **hostAddress,
611    char **startOptions,
612    int *authLen,
613    char **authName,
614    char **authData)
615{
616    running_proxy *proxy = ProxyForPMconn (pmConn);
617
618    if (proxy && proxy->requests) {
619	if (requestor)
620	    *requestor = proxy->requests->requestor;
621	if (runList)
622	    *runList = (running_proxy_list *)
623		proxy->requests->listData;
624	if (serviceName)
625	    *serviceName = proxy->requests->serviceName;
626	if (serverAddress)
627	    *serverAddress = proxy->requests->serverAddress;
628	if (hostAddress)
629	    *hostAddress = proxy->requests->hostAddress;
630	if (startOptions)
631	    *startOptions = proxy->requests->startOptions;
632	if (authLen)
633	    *authLen = proxy->requests->authLen;
634	if (authName)
635	    *authName = proxy->requests->authName;
636	if (authData)
637	    *authData = proxy->requests->authData;
638
639	return 1;
640    }
641    else
642	return 0;
643}
644
645
646running_proxy *
647ProxyForPMconn (
648    PMconn *pmConn)
649{
650    proxy_service *service = proxyServiceList;
651
652    while (service)
653    {
654	running_proxy *proxy = service->proxyList;
655
656	while (proxy)
657	{
658	    if (proxy->pmConn == pmConn)
659		return proxy;
660	    else
661		proxy = proxy->next;
662	}
663
664	service = service->next;
665    }
666
667    return NULL;
668}
669
670
671PMconn*
672PopRequestorQueue (
673    PMconn *pmConn,
674    Bool addServer,		/* record this server address */
675    Bool freeProxyList)
676{
677    running_proxy *proxy = ProxyForPMconn (pmConn);
678
679    if (proxy) {
680	PMconn *requestor;
681	server_list *server;
682	request_list *nextreq;
683	Bool newServer = False;
684
685	if (addServer) {
686	    newServer = True;
687	    server = proxy->servers;
688
689	    while (server)
690	    {
691		if (strcmp (server->serverAddress,
692		    proxy->requests->serverAddress) == 0)
693		{
694		    newServer = False;
695		    break;
696		}
697
698		server = server->next;
699	    }
700
701	    if (newServer)
702	    {
703		server = malloc (sizeof (server_list));
704		server->serverAddress = proxy->requests->serverAddress;
705		server->next = proxy->servers;
706		proxy->servers = server;
707	    }
708	}
709
710	if (!newServer)
711	    free (proxy->requests->serverAddress);
712
713	free (proxy->requests->serviceName);
714	free (proxy->requests->hostAddress);
715	free (proxy->requests->startOptions);
716	free (proxy->requests->listData);	/* proxyList */
717	free (proxy->requests->authName);
718	free (proxy->requests->authData);
719
720	requestor = proxy->requests->requestor;
721
722	nextreq = proxy->requests->next;
723	free (proxy->requests);
724	proxy->requests = nextreq;
725
726	return requestor;
727    }
728
729    return NULL;
730}
731