1    	/*
2    	 * Copyright (c) 1995-2002,2004,2006,2008 Silicon Graphics, Inc.  All Rights Reserved.
3    	 * Copyright (c) 2007-2008 Aconex.  All Rights Reserved.
4    	 * 
5    	 * This library is free software; you can redistribute it and/or modify it
6    	 * under the terms of the GNU Lesser General Public License as published
7    	 * by the Free Software Foundation; either version 2.1 of the License, or
8    	 * (at your option) any later version.
9    	 * 
10   	 * This library is distributed in the hope that it will be useful, but
11   	 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12   	 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
13   	 * License for more details.
14   	 */
15   	
16   	#include "pmapi.h"
17   	#include "impl.h"
18   	
19   	#define PM_CONTEXT_UNDEF	-1	/* current context is undefined */
20   	
21   	static __pmContext	*contexts;		/* array of contexts */
22   	static int		contexts_len;		/* number of contexts */
23   	static int		curcontext = PM_CONTEXT_UNDEF;	/* current context */
24   	
25   	static int	n_backoff;
26   	static int	def_backoff[] = {5, 10, 20, 40, 80};
27   	static int	*backoff;
28   	
29   	static void
30   	waitawhile(__pmPMCDCtl *ctl)
31   	{
32   	    /*
33   	     * after failure, compute delay before trying again ...
34   	     */
35   	    if (n_backoff == 0) {
36   		char	*q;
37   		/* first time ... try for PMCD_RECONNECT_TIMEOUT from env */
38   		if ((q = getenv("PMCD_RECONNECT_TIMEOUT")) != NULL) {
39   		    char	*pend;
40   		    char	*p;
41   		    int		val;
42   	
43   		    for (p = q; *p != '\0'; ) {
44   			val = (int)strtol(p, &pend, 10);
45   			if (val <= 0 || (*pend != ',' && *pend != '\0')) {
46   			    __pmNotifyErr(LOG_WARNING,
47   					 "pmReconnectContext: ignored bad PMCD_RECONNECT_TIMEOUT = '%s'\n",
48   					 q);
49   			    n_backoff = 0;
50   			    if (backoff != NULL)
51   				free(backoff);
52   			    break;
53   			}
54   			if ((backoff = (int *)realloc(backoff, (n_backoff+1) * sizeof(backoff[0]))) == NULL) {
55   			    __pmNoMem("pmReconnectContext", (n_backoff+1) * sizeof(backoff[0]), PM_FATAL_ERR);
56   			}
57   			backoff[n_backoff++] = val;
58   			if (*pend == '\0')
59   			    break;
60   			p = &pend[1];
61   		    }
62   		}
63   		if (n_backoff == 0) {
64   		    /* use default */
65   		    n_backoff = 5;
66   		    backoff = def_backoff;
67   		}
68   	    }
69   	    if (ctl->pc_timeout == 0)
70   		ctl->pc_timeout = 1;
71   	    else if (ctl->pc_timeout < n_backoff)
72   		ctl->pc_timeout++;
73   	    ctl->pc_again = time(NULL) + backoff[ctl->pc_timeout-1];
74   	}
75   	
76   	__pmContext *
77   	__pmHandleToPtr(int handle)
78   	{
79   	    if (handle < 0 || handle >= contexts_len ||
80   		contexts[handle].c_type == PM_CONTEXT_FREE)
81   		return NULL;
82   	    else
83   		return &contexts[handle];
84   	}
85   	
86   	int
87   	__pmPtrToHandle(__pmContext *ctxp)
88   	{
89   	    return ctxp - contexts;
90   	}
91   	
92   	const char * 
93   	pmGetContextHostName (int ctxid)
94   	{
95   	    __pmContext * ctx;
96   	
97   	    if ( (ctx = __pmHandleToPtr(ctxid)) != NULL) {
98   		switch (ctx->c_type) {
99   		case PM_CONTEXT_HOST:
100  		    return (ctx->c_pmcd->pc_hosts[0].name);
101  	
102  		case PM_CONTEXT_ARCHIVE:
103  		    return (ctx->c_archctl->ac_log->l_label.ill_hostname);
104  		}
105  	    }
106  	
107  	    return ("");
108  	}
109  	
110  	int
111  	pmWhichContext(void)
112  	{
113  	    /*
114  	     * return curcontext, provided it is defined
115  	     */
116  	    int		sts;
117  	
118  	    if (curcontext > PM_CONTEXT_UNDEF)
119  		sts = curcontext;
120  	    else
121  		sts = PM_ERR_NOCONTEXT;
122  	
123  	#ifdef PCP_DEBUG
124  	    if (pmDebug & DBG_TRACE_CONTEXT)
125  		fprintf(stderr, "pmWhichContext() -> %d, cur=%d\n",
126  		    sts, curcontext);
127  	#endif
128  	    return sts;
129  	}
130  	
131  	static int
132  	__pmConvertTimeout (int timeo)
133  	{
134  	    int tout_msec;
135  	    const struct timeval *tv;
136  	
137  	    switch (timeo) {
138  	    case TIMEOUT_NEVER:
139  	        tout_msec = -1;
140  	        break;
141  	    case TIMEOUT_DEFAULT:
142  	        tv = __pmDefaultRequestTimeout();
143  	
144  	        tout_msec = tv->tv_sec *1000 + tv->tv_usec / 1000;
145  	        break;
146  	
147  	    default:
148  	        tout_msec = timeo  * 1000;
149  	        break;
150  	    }
151  	
152  	    return tout_msec;
153  	}
154  	
155  	int
156  	pmNewContext(int type, const char *name)
157  	{
158  	    __pmContext	*new = NULL;
159  	    __pmContext	*list;
160  	    int		i;
161  	    int		sts;
162  	    int		old_curcontext = curcontext;
163  	    int		old_contexts_len = contexts_len;
164  	
165  	    /* See if we can reuse a free context */
166  	    for (i = 0; i < contexts_len; i++) {
167  		if (contexts[i].c_type == PM_CONTEXT_FREE) {
168  		    curcontext = i;
169  		    new = &contexts[curcontext];
170  		    goto INIT_CONTEXT;
171  		}
172  	    }
173  	
174  	    /* Create a new one */
175  	    if (contexts == NULL)
176  		list = (__pmContext *)malloc(sizeof(__pmContext));
177  	    else
178  		list = (__pmContext *)realloc((void *)contexts, (1+contexts_len) * sizeof(__pmContext));
179  	
180  	    if (list == NULL) {
181  		/* fail : nothing changed */
182  		sts = -oserror();
183  		goto FAILED;
184  	    }
185  	
186  	    contexts = list;
187  	    curcontext = contexts_len++;
188  	    new = &contexts[curcontext];
189  	
190  	INIT_CONTEXT:
191  	    /*
192  	     * Set up the default state
193  	     */
194  	    memset(new, 0, sizeof(__pmContext));
195  	    new->c_type = (type & PM_CONTEXT_TYPEMASK);
196  	    if ((new->c_instprof = (__pmProfile *)malloc(sizeof(__pmProfile))) == NULL) {
197  		/*
198  		 * fail : nothing changed -- actually list is changed, but restoring
199  		 * contexts_len should make it ok next time through
200  		 */
201  		sts = -oserror();
202  		goto FAILED;
203  	    }
204  	    memset(new->c_instprof, 0, sizeof(__pmProfile));
205  	    new->c_instprof->state = PM_PROFILE_INCLUDE;	/* default global state */
206  	    new->c_sent = 0;	/* profile not sent */
207  	    new->c_origin.tv_sec = new->c_origin.tv_usec = 0;	/* default time */
208  	
209  	    if (new->c_type == PM_CONTEXT_HOST) {
210  		pmHostSpec	*hosts;
211  		int		nhosts;
212  		char		*errmsg;
213  	
214  		/* deconstruct a host[:port@proxy:port] specification */
Event alloc_arg: Calling allocation function "__pmParseHostSpec" on "errmsg". [details]
Also see events: [noescape][leaked_storage]
215  		sts = __pmParseHostSpec(name, &hosts, &nhosts, &errmsg);
At conditional (1): "sts < 0": Taking true branch.
216  		if (sts < 0) {
Event noescape: Variable "errmsg" is not freed or pointed-to in function "pmprintf".
Also see events: [alloc_arg][leaked_storage]
217  		    pmprintf("pmNewContext: bad host specification\n%s", errmsg);
218  		    pmflush();
Event leaked_storage: Variable "errmsg" going out of scope leaks the storage it points to.
Also see events: [alloc_arg][noescape]
219  		    goto FAILED;
220  		}
221  	
222  		if ((type & PM_CTXFLAG_EXCLUSIVE) == 0 && nhosts == 1) {
223  		    for (i = 0; i < contexts_len; i++) {
224  			if (i == curcontext)
225  			    continue;
226  			if (contexts[i].c_type == PM_CONTEXT_HOST &&
227  			    (contexts[i].c_pmcd->pc_curpdu == 0) &&
228  			    strcmp(contexts[i].c_pmcd->pc_hosts[0].name,
229  				    hosts[0].name) == 0) {
230  			    new->c_pmcd = contexts[i].c_pmcd;
231  			    /*new->c_pduinfo = contexts[i].c_pduinfo;*/
232  			}
233  		    }
234  		}
235  		if (new->c_pmcd == NULL) {
236  		    pmcd_ctl_state_t inistate;
237  		    /*
238  		     * Try to establish the connection.
239  		     * If this fails, restore the original current context
240  		     * and return an error.
241  		     */
242  		    if (type & PM_CTXFLAG_SHALLOW) {
243  			sts = __pmCreateSocket();
244  			inistate = PC_FETAL;
245  		    } else {
246  			sts = __pmConnectPMCD(hosts, nhosts);
247  			inistate = PC_READY;
248  		    }
249  	
250  		    if (sts < 0) {
251  			__pmFreeHostSpec(hosts, nhosts);
252  			goto FAILED;
253  		    }
254  	
255  		    new->c_pmcd = (__pmPMCDCtl *)calloc(1,sizeof(__pmPMCDCtl));
256  		    if (new->c_pmcd == NULL) {
257  			sts = -oserror();
258  			__pmCloseSocket(sts);
259  			__pmFreeHostSpec(hosts, nhosts);
260  			goto FAILED;
261  		    }
262  		    new->c_pmcd->pc_fd = sts;
263  		    new->c_pmcd->pc_state = inistate;
264  		    new->c_pmcd->pc_hosts = hosts;
265  		    new->c_pmcd->pc_nhosts = nhosts;
266  		    new->c_pmcd->pc_tout_sec = __pmConvertTimeout(TIMEOUT_DEFAULT) / 1000;
267  		}
268  		else {
269  		    /* duplicate of an existing context, don't need the __pmHostSpec */
270  		    __pmFreeHostSpec(hosts, nhosts);
271  		}
272  		new->c_pmcd->pc_refcnt++;
273  	    }
274  	    else if (new->c_type == PM_CONTEXT_LOCAL) {
275  		if (!(type & PM_CTXFLAG_SHALLOW) && (sts = __pmConnectLocal()) != 0)
276  		    goto FAILED;
277  	    }
278  	    else if (new->c_type == PM_CONTEXT_ARCHIVE) {
279  		if ((new->c_archctl = (__pmArchCtl *)malloc(sizeof(__pmArchCtl))) == NULL) {
280  		    sts = -oserror();
281  		    goto FAILED;
282  		}
283  		new->c_archctl->ac_log = NULL;
284  		for (i = 0; i < contexts_len; i++) {
285  		    if (i == curcontext)
286  			continue;
287  		    if (contexts[i].c_type == PM_CONTEXT_ARCHIVE &&
288  			strcmp(name, contexts[i].c_archctl->ac_log->l_name) == 0) {
289  			new->c_archctl->ac_log = contexts[i].c_archctl->ac_log;
290  		    }
291  		}
292  		if (new->c_archctl->ac_log == NULL) {
293  		    if ((new->c_archctl->ac_log = (__pmLogCtl *)malloc(sizeof(__pmLogCtl))) == NULL) {
294  			free(new->c_archctl);
295  			sts = -oserror();
296  			goto FAILED;
297  		    }
298  		    if ((sts = __pmLogOpen(name, new)) < 0) {
299  			free(new->c_archctl->ac_log);
300  			free(new->c_archctl);
301  			goto FAILED;
302  		    }
303  	        }
304  		else {
305  		    /* archive already open, set default starting state as per __pmLogOpen */
306  		    new->c_origin.tv_sec = (__int32_t)new->c_archctl->ac_log->l_label.ill_start.tv_sec;
307  		    new->c_origin.tv_usec = (__int32_t)new->c_archctl->ac_log->l_label.ill_start.tv_usec;
308  		    new->c_mode = (new->c_mode & 0xffff0000) | PM_MODE_FORW;
309  		}
310  	
311  		/* start after header + label record + trailer */
312  		new->c_archctl->ac_offset = sizeof(__pmLogLabel) + 2*sizeof(int);
313  		new->c_archctl->ac_vol = new->c_archctl->ac_log->l_curvol;
314  		new->c_archctl->ac_serial = 0;		/* not serial access, yet */
315  		new->c_archctl->ac_pmid_hc.nodes = 0;	/* empty hash list */
316  		new->c_archctl->ac_pmid_hc.hsize = 0;
317  	
318  		new->c_archctl->ac_log->l_refcnt++;
319  	    }
320  	    else {
321  		/* bad type */
322  	#ifdef PCP_DEBUG
323  		if (pmDebug & DBG_TRACE_CONTEXT) {
324  		    fprintf(stderr, "pmNewContext(%d, %s): illegal type\n",
325  			    type, name);
326  		}
327  	#endif
328  		return PM_ERR_NOCONTEXT;
329  	    }
330  	
331  	    /* bind defined metrics if any ... */
332  	    __dmopencontext(new);
333  	
334  	    /* return the handle to the new (current) context */
335  	#ifdef PCP_DEBUG
336  	    if (pmDebug & DBG_TRACE_CONTEXT) {
337  		fprintf(stderr, "pmNewContext(%d, %s) -> %d\n", type, name, curcontext);
338  		__pmDumpContext(stderr, curcontext, PM_INDOM_NULL);
339  	    }
340  	#endif
341  	    return curcontext;
342  	
343  	FAILED:
344  	    if (new != NULL) {
345  		new->c_type = PM_CONTEXT_FREE;
346  		if (new->c_instprof != NULL)
347  		    free(new->c_instprof);
348  	    }
349  	    curcontext = old_curcontext;
350  	    contexts_len = old_contexts_len;
351  	#ifdef PCP_DEBUG
352  	    if (pmDebug & DBG_TRACE_CONTEXT)
353  		fprintf(stderr, "pmNewContext(%d, %s) -> %d, curcontext=%d\n",
354  		    type, name, sts, curcontext);
355  	#endif
356  	    return sts;
357  	}
358  	
359  	
360  	int
361  	pmReconnectContext(int handle)
362  	{
363  	    __pmContext	*ctxp;
364  	    __pmPMCDCtl	*ctl;
365  	    int		sts;
366  	
367  	    if (handle < 0 || handle >= contexts_len ||
368  		contexts[handle].c_type == PM_CONTEXT_FREE) {
369  	#ifdef PCP_DEBUG
370  		    if (pmDebug & DBG_TRACE_CONTEXT)
371  			fprintf(stderr, "pmReconnectContext(%d) -> %d\n", handle, PM_ERR_NOCONTEXT);
372  	#endif
373  		    return PM_ERR_NOCONTEXT;
374  	    }
375  	
376  	    ctxp = &contexts[handle];
377  	    ctl = ctxp->c_pmcd;
378  	    if (ctxp->c_type == PM_CONTEXT_HOST) {
379  		if (ctl->pc_timeout && time(NULL) < ctl->pc_again) {
380  		    /* too soon to try again */
381  	#ifdef PCP_DEBUG
382  		if (pmDebug & DBG_TRACE_CONTEXT)
383  		    fprintf(stderr, "pmReconnectContext(%d) -> %d, too soon (need wait another %d secs)\n",
384  			handle, (int)-ETIMEDOUT, (int)(ctl->pc_again - time(NULL)));
385  	#endif
386  		    return -ETIMEDOUT;
387  		}
388  	
389  		if (ctl->pc_fd >= 0) {
390  		    /* don't care if this fails */
391  		    __pmCloseSocket(ctl->pc_fd);
392  		    ctl->pc_fd = -1;
393  		}
394  	
395  		if ((sts = __pmConnectPMCD(ctl->pc_hosts, ctl->pc_nhosts)) >= 0) {
396  		    ctl->pc_fd = sts;
397  		    ctxp->c_sent = 0;
398  		}
399  	
400  		if (sts < 0) {
401  		    waitawhile(ctl);
402  	#ifdef PCP_DEBUG
403  		    if (pmDebug & DBG_TRACE_CONTEXT)
404  			fprintf(stderr, "pmReconnectContext(%d), failed (wait %d secs before next attempt)\n",
405  			    handle, (int)(ctl->pc_again - time(NULL)));
406  	#endif
407  		    return -ETIMEDOUT;
408  		}
409  		else {
410  		    int		i;
411  		    /*
412  		     * mark profile as not sent for all contexts sharing this
413  		     * socket
414  		     */
415  		    for (i = 0; i < contexts_len; i++) {
416  			if (contexts[i].c_type != PM_CONTEXT_FREE && contexts[i].c_pmcd == ctl) {
417  			    contexts[i].c_sent = 0;
418  			}
419  		    }
420  	#ifdef PCP_DEBUG
421  		    if (pmDebug & DBG_TRACE_CONTEXT)
422  			fprintf(stderr, "pmReconnectContext(%d), done\n", handle);
423  	#endif
424  		    ctl->pc_timeout = 0;
425  		}
426  	    }
427  	    else {
428  		/*
429  		 * assume PM_CONTEXT_ARCHIVE or PM_CONTEXT_LOCAL reconnect,
430  		 * this is a NOP in either case.
431  		 */
432  		;
433  	    }
434  	
435  	    /*
436  	     * clear any derived metrics and re-bind
437  	     */
438  	    __dmclosecontext(ctxp);
439  	    __dmopencontext(ctxp);
440  	
441  	#ifdef PCP_DEBUG
442  	    if (pmDebug & DBG_TRACE_CONTEXT)
443  		fprintf(stderr, "pmReconnectContext(%d) -> %d\n", handle, handle);
444  	#endif
445  	    return handle;
446  	}
447  	
448  	int
449  	pmDupContext(void)
450  	{
451  	    int			sts;
452  	    int			old, new = -1;
453  	    char		hostspec[4096], *h;
454  	    __pmContext		*newcon, *oldcon;
455  	    __pmInDomProfile	*q, *p, *p_end;
456  	    __pmProfile		*save;
457  	    void		*save_dm;
458  	
459  	    if ((old = pmWhichContext()) < 0) {
460  		sts = old;
461  		goto done;
462  	    }
463  	    oldcon = &contexts[old];
464  	    if (oldcon->c_type == PM_CONTEXT_HOST) {
465  		h = &hostspec[0];
466  		__pmUnparseHostSpec(oldcon->c_pmcd->pc_hosts,
467  				oldcon->c_pmcd->pc_nhosts, &h, sizeof(hostspec));
468  		new = pmNewContext(oldcon->c_type, hostspec);
469  	    }
470  	    else if (oldcon->c_type == PM_CONTEXT_LOCAL)
471  		new = pmNewContext(oldcon->c_type, NULL);
472  	    else
473  		/* assume PM_CONTEXT_ARCHIVE */
474  		new = pmNewContext(oldcon->c_type, oldcon->c_archctl->ac_log->l_name);
475  	    if (new < 0) {
476  		/* failed to connect or out of memory */
477  		sts = new;
478  		goto done;
479  	    }
480  	    oldcon = &contexts[old];	/* contexts[] may have been relocated */
481  	    newcon = &contexts[new];
482  	    save = newcon->c_instprof;	/* need this later */
483  	    save_dm = newcon->c_dm;	/* need this later */
484  	    if (newcon->c_archctl != NULL)
485  		free(newcon->c_archctl);	/* will allocate a new one below */
486  	    *newcon = *oldcon;		/* struct copy */
487  	    newcon->c_instprof = save;	/* restore saved instprof from pmNewContext */
488  	    newcon->c_dm = save_dm;	/* restore saved derived metrics control also */
489  	
490  	    /* clone the per-domain profiles (if any) */
491  	    if (oldcon->c_instprof->profile_len > 0) {
492  		newcon->c_instprof->profile = (__pmInDomProfile *)malloc(
493  		    oldcon->c_instprof->profile_len * sizeof(__pmInDomProfile));
494  		if (newcon->c_instprof->profile == NULL) {
495  		    sts = -oserror();
496  		    goto done;
497  		}
498  		memcpy(newcon->c_instprof->profile, oldcon->c_instprof->profile,
499  		    oldcon->c_instprof->profile_len * sizeof(__pmInDomProfile));
500  		p = oldcon->c_instprof->profile;
501  		p_end = p + oldcon->c_instprof->profile_len;
502  		q = newcon->c_instprof->profile;
503  		for (; p < p_end; p++, q++) {
504  		    if (p->instances) {
505  			q->instances = (int *)malloc(p->instances_len * sizeof(int));
506  			if (q->instances == NULL) {
507  			    sts = -oserror();
508  			    goto done;
509  			}
510  			memcpy(q->instances, p->instances,
511  			    p->instances_len * sizeof(int));
512  		    }
513  		}
514  	    }
515  	
516  	    /*
517  	     * The call to pmNewContext (above) should have connected to the pmcd.
518  	     * Make sure the new profile will be sent before the next fetch.
519  	     */
520  	    newcon->c_sent = 0;
521  	
522  	    /* clone the archive control struct, if any */
523  	    if (oldcon->c_archctl != NULL) {
524  		if ((newcon->c_archctl = (__pmArchCtl *)malloc(sizeof(__pmArchCtl))) == NULL) {
525  		    sts = -oserror();
526  		    goto done;
527  		}
528  		*newcon->c_archctl = *oldcon->c_archctl;	/* struct assignment */
529  	    }
530  	
531  	    sts = new;
532  	
533  	done:
534  	    /* return an error code, or the handle for the new context */
535  	    if (sts < 0 && new >= 0)
536  		contexts[new].c_type = PM_CONTEXT_FREE;
537  	#ifdef PCP_DEBUG
538  	    if (pmDebug & DBG_TRACE_CONTEXT) {
539  		fprintf(stderr, "pmDupContext() -> %d\n", sts);
540  		if (sts >= 0)
541  		    __pmDumpContext(stderr, sts, PM_INDOM_NULL);
542  	    }
543  	#endif
544  	    return sts;
545  	}
546  	
547  	int
548  	pmUseContext(int handle)
549  	{
550  	    if (handle < 0 || handle >= contexts_len ||
551  		contexts[handle].c_type == PM_CONTEXT_FREE) {
552  	#ifdef PCP_DEBUG
553  		    if (pmDebug & DBG_TRACE_CONTEXT)
554  			fprintf(stderr, "pmUseContext(%d) -> %d\n", handle, PM_ERR_NOCONTEXT);
555  	#endif
556  		    return PM_ERR_NOCONTEXT;
557  	    }
558  	
559  	#ifdef PCP_DEBUG
560  	    if (pmDebug & DBG_TRACE_CONTEXT)
561  		fprintf(stderr, "pmUseContext(%d) -> 0\n", handle);
562  	#endif
563  	    curcontext = handle;
564  	    return 0;
565  	}
566  	
567  	int
568  	pmDestroyContext(int handle)
569  	{
570  	    __pmContext		*ctxp;
571  	    struct linger       dolinger = {0, 1};
572  	
573  	    if (handle < 0 || handle >= contexts_len ||
574  		contexts[handle].c_type == PM_CONTEXT_FREE) {
575  	#ifdef PCP_DEBUG
576  		    if (pmDebug & DBG_TRACE_CONTEXT)
577  			fprintf(stderr, "pmDestroyContext(%d) -> %d\n", handle, PM_ERR_NOCONTEXT);
578  	#endif
579  		    return PM_ERR_NOCONTEXT;
580  	    }
581  	
582  	    ctxp = &contexts[handle];
583  	    if (ctxp->c_pmcd != NULL) {
584  		if (--ctxp->c_pmcd->pc_refcnt == 0) {
585  		    if (ctxp->c_pmcd->pc_fd >= 0) {
586  			/* before close, unsent data should be flushed */
587  			setsockopt(ctxp->c_pmcd->pc_fd, SOL_SOCKET,
588  			    SO_LINGER, (char *) &dolinger, (mysocklen_t)sizeof(dolinger));
589  			__pmCloseSocket(ctxp->c_pmcd->pc_fd);
590  		    }
591  		    __pmFreeHostSpec(ctxp->c_pmcd->pc_hosts, ctxp->c_pmcd->pc_nhosts);
592  		    free(ctxp->c_pmcd);
593  		}
594  	    }
595  	    if (ctxp->c_archctl != NULL) {
596  		if (--ctxp->c_archctl->ac_log->l_refcnt == 0) {
597  		    __pmLogClose(ctxp->c_archctl->ac_log);
598  		    free(ctxp->c_archctl->ac_log);
599  		}
600  		free(ctxp->c_archctl);
601  	    }
602  	    ctxp->c_type = PM_CONTEXT_FREE;
603  	
604  	    if (handle == curcontext)
605  		/* we have no choice */
606  		curcontext = PM_CONTEXT_UNDEF;
607  	
608  	    __pmFreeProfile(ctxp->c_instprof);
609  	    __dmclosecontext(ctxp);
610  	#ifdef PCP_DEBUG
611  	    if (pmDebug & DBG_TRACE_CONTEXT)
612  		fprintf(stderr, "pmDestroyContext(%d) -> 0, curcontext=%d\n",
613  			handle, curcontext);
614  	#endif
615  	
616  	    return 0;
617  	}
618  	
619  	static char *_mode[] = { "LIVE", "INTERP", "FORW", "BACK" };
620  	
621  	/*
622  	 * dump context(s); context == -1 for all contexts, indom == PM_INDOM_NULL
623  	 * for all instance domains.
624  	 */
625  	void
626  	__pmDumpContext(FILE *f, int context, pmInDom indom)
627  	{
628  	    int			i;
629  	    __pmContext		*con;
630  	
631  	    fprintf(f, "Dump Contexts: current context = %d\n", curcontext);
632  	    if (curcontext < 0)
633  		return;
634  	
635  	    if (indom != PM_INDOM_NULL) {
636  		fprintf(f, "Dump restricted to indom=%d [%s]\n", 
637  		        indom, pmInDomStr(indom));
638  	    }
639  	
640  	    for (con=contexts, i=0; i < contexts_len; i++, con++) {
641  		if (context == -1 || context == i) {
642  		    fprintf(f, "Context[%d]", i);
643  		    if (con->c_type == PM_CONTEXT_HOST) {
644  			fprintf(f, " host %s:", con->c_pmcd->pc_hosts[0].name);
645  			fprintf(f, " pmcd=%s profile=%s fd=%d refcnt=%d",
646  			    (con->c_pmcd->pc_fd < 0) ? "NOT CONNECTED" : "CONNECTED",
647  			    con->c_sent ? "SENT" : "NOT_SENT",
648  			    con->c_pmcd->pc_fd,
649  			    con->c_pmcd->pc_refcnt);
650  		    }
651  		    else if (con->c_type == PM_CONTEXT_LOCAL) {
652  			fprintf(f, " standalone:");
653  			fprintf(f, " profile=%s\n",
654  			    con->c_sent ? "SENT" : "NOT_SENT");
655  		    }
656  		    else {
657  			fprintf(f, " log %s:", con->c_archctl->ac_log->l_name);
658  			fprintf(f, " mode=%s", _mode[con->c_mode & __PM_MODE_MASK]);
659  			fprintf(f, " profile=%s tifd=%d mdfd=%d mfd=%d\nrefcnt=%d vol=%d",
660  			    con->c_sent ? "SENT" : "NOT_SENT",
661  			    con->c_archctl->ac_log->l_tifp == NULL ? -1 : fileno(con->c_archctl->ac_log->l_tifp),
662  			    fileno(con->c_archctl->ac_log->l_mdfp),
663  			    fileno(con->c_archctl->ac_log->l_mfp),
664  			    con->c_archctl->ac_log->l_refcnt,
665  			    con->c_archctl->ac_log->l_curvol);
666  			fprintf(f, " offset=%ld (vol=%d) serial=%d",
667  			    (long)con->c_archctl->ac_offset,
668  			    con->c_archctl->ac_vol,
669  			    con->c_archctl->ac_serial);
670  		    }
671  		    if (con->c_type != PM_CONTEXT_LOCAL) {
672  			fprintf(f, " origin=%d.%06d",
673  			    con->c_origin.tv_sec, con->c_origin.tv_usec);
674  			fprintf(f, " delta=%d\n", con->c_delta);
675  		    }
676  		    __pmDumpProfile(f, indom, con->c_instprof);
677  		}
678  	    }
679  	}
680  	
681  	int
682  	__pmGetHostContextByID (int ctxid, __pmContext **cp)
683  	{
684  	    __pmContext *ctxp = __pmHandleToPtr(ctxid);
685  	
686  	    if (ctxp == NULL) {
687  		return (PM_ERR_NOCONTEXT);
688  	    } else if (ctxp->c_type != PM_CONTEXT_HOST) {
689  		return (PM_ERR_NOTHOST);
690  	    } else if ((ctxp->c_pmcd->pc_fd < 0) ||
691  		       (ctxp->c_pmcd->pc_state != PC_READY)) {
692  		return (PM_ERR_NOTCONN);
693  	    }
694  	    
695  	    *cp = ctxp;
696  	
697  	    return (0);
698  	}
699  	
700  	#ifdef ASYNC_API
701  	int
702  	__pmGetBusyHostContextByID (int ctxid, __pmContext **cp, int pdu)
703  	{
704  	    int n;
705  	
706  	    if ((n = __pmGetHostContextByID (ctxid, cp)) >= 0) {
707  		if ((*cp)->c_pmcd->pc_curpdu != pdu) {
708  		    *cp = NULL;
709  		    n = PM_ERR_CTXBUSY;
710  		}
711  	    }
712  	    return (n);
713  	}
714  	
715  	int
716  	pmGetContextFD (int ctxid)
717  	{
718  	    __pmContext *ctxp = __pmHandleToPtr(ctxid);
719  	
720  	    if (ctxp == NULL) {
721  		return (PM_ERR_NOCONTEXT);
722  	    } else if (ctxp->c_type != PM_CONTEXT_HOST) {
723  		return (PM_ERR_NOTHOST);
724  	    } else if (ctxp->c_pmcd->pc_fd < 0) {
725  		return (PM_ERR_NOTCONN);
726  	    }
727  	    return (ctxp->c_pmcd->pc_fd);
728  	}
729  	
730  	int 
731  	pmGetContextTimeout (int ctxid, int *tout_msec)
732  	{
733  	    __pmContext *ctxp = __pmHandleToPtr(ctxid);
734  	
735  	    if (ctxp == NULL) {
736  		return (PM_ERR_NOCONTEXT);
737  	    } else if (ctxp->c_type != PM_CONTEXT_HOST) {
738  		return (PM_ERR_NOTHOST);
739  	    } else if (tout_msec == NULL) {
740  		return (-EINVAL);
741  	    }
742  	
743  	    *tout_msec = __pmConvertTimeout(ctxp->c_pmcd->pc_tout_sec);
744  	
745  	    return (0);
746  	}
747  	
748  	int
749  	pmContextConnectTo (int ctxid, const struct sockaddr *addr)
750  	{
751  	    int f;
752  	    pmHostSpec *pmcd;
753  	    __pmPMCDCtl *pc;
754  	    __pmContext *ctxp = __pmHandleToPtr(ctxid);
755  	
756  	    if (ctxp == NULL) {
757  		return (PM_ERR_NOCONTEXT);
758  	    } else if (ctxp->c_type != PM_CONTEXT_HOST) {
759  		return (PM_ERR_NOTHOST);
760  	    } else if (ctxp->c_pmcd->pc_fd < 0) {
761  		return (PM_ERR_NOTCONN);
762  	    } else if (ctxp->c_pmcd->pc_state != PC_FETAL) {
763  		return (PM_ERR_ISCONN);
764  	    }
765  	
766  	    pc = ctxp->c_pmcd;
767  	    pmcd = &pc->pc_hosts[0];
768  	    memcpy(&pc->pc_addr, addr, sizeof (pc->pc_addr));
769  	    if (pmcd->nports < 1)
770  		__pmConnectGetPorts(pmcd);
771  	
772  	    if ((f =__pmConnectTo(pc->pc_fd, addr, pmcd->ports[0])) >= 0) {
773  		const struct timeval *tv = __pmConnectTimeout();
774  	
775  		pc->pc_fdflags = f;
776  		pc->pc_state = PC_CONN_INPROGRESS;
777  		pc->pc_tout_sec = tv->tv_sec;
778  	        return (0);
779  	    }
780  	
781  	    __pmCloseSocket(pc->pc_fd);
782  	    pc->pc_fd = -1;
783  	    
784  	    return (f);
785  	}
786  	
787  	int
788  	pmContextConnectChangeState (int ctxid)
789  	{
790  	    int f;
791  	    __pmContext *ctxp = __pmHandleToPtr(ctxid);
792  	    __pmPMCDCtl *pc;
793  	
794  	    if (ctxp == NULL) {
795  		return (PM_ERR_NOCONTEXT);
796  	    } else if (ctxp->c_type != PM_CONTEXT_HOST) {
797  		return (PM_ERR_NOTHOST);
798  	    } else if (ctxp->c_pmcd->pc_fd < 0) {
799  		return (PM_ERR_NOTCONN);
800  	    }
801  	
802  	    /* The assumption is that if pc_fd is less then 0 then state does
803  	     * not matter */
804  	    pc = ctxp->c_pmcd;
805  	    switch (pc->pc_state) {
806  	    case PC_CONN_INPROGRESS:
807  		if (((f = __pmConnectCheckError (pc->pc_fd)) == 0) &&
808  		    ((f = __pmConnectRestoreFlags (pc->pc_fd,
809  						   pc->pc_fdflags)) == pc->pc_fd)) {
810  		    pc->pc_tout_sec = TIMEOUT_DEFAULT;
811  		    pc->pc_state = PC_WAIT_FOR_PMCD;
812  		    f = 0;
813  		} else if (pc->pc_hosts[0].nports > 1) {
814  		    int fd;
815  		    __pmCloseSocket(pc->pc_fd);
816  	
817  		    if ((fd = __pmCreateSocket()) >= 0) {
818  			if (fd != pc->pc_fd) {
819  			    if ((f = dup2(fd, pc->pc_fd)) == pc->pc_fd) {
820  				__pmSetVersionIPC(pc->pc_fd, __pmVersionIPC(fd));
821  				__pmSetSocketIPC(pc->pc_fd);
822  				__pmCloseSocket(fd);
823  			    } else {
824  				fd = -oserror();
825  			    }
826  			}
827  	
828  			if (fd > 0) {
829  			    __pmDropHostPort(pc->pc_hosts);
830  			    pc->pc_state = PC_FETAL;
831  	
832  			    if ((f = __pmConnectTo(pc->pc_fd, &pc->pc_addr,
833  					   pc->pc_hosts[0].ports[0])) >= 0) {
834  				pc->pc_fdflags = f;
835  				pc->pc_state = PC_CONN_INPROGRESS;
836  				f = 0;
837  			    }
838  			} else {
839  			    f = fd;
840  			}
841  		    } else {
842  			f = fd;
843  		    }
844  		} else if (f > 0) {
845  		    f = __pmMapErrno(-f);
846  		}
847  		break;
848  	
849  	    case PC_WAIT_FOR_PMCD:
850  		if ((f = __pmConnectHandshake(pc->pc_fd)) >= 0) {
851  		    pc->pc_state = PC_READY;
852  		    f = 0;
853  		}
854  		break;
855  	
856  	    case PC_READY:
857  		f = PM_ERR_ISCONN;
858  		break;
859  	
860  	    case PC_FETAL:
861  		f = PM_ERR_NOTCONN;
862  		break;
863  	
864  	    default:
865  		f = -EINVAL;
866  		break;
867  	    }
868  	
869  	    if (f) {
870  		__pmCloseSocket(pc->pc_fd);
871  		pc->pc_fd = -1;
872  	    } else if (pc->pc_state != PC_READY) {
873  		f = PM_ERR_AGAIN;
874  	    }
875  	
876  	    return (f);
877  	}
878  	
879  	
880  	void
881  	pmContextUndef(void)
882  	{
883  	    curcontext = PM_CONTEXT_UNDEF;
884  	}
885  	#endif /*ASYNC_API*/