1    	/*
2    	 * Copyright (c) 1995-2001 Silicon Graphics, Inc.  All Rights Reserved.
3    	 * 
4    	 * This program is free software; you can redistribute it and/or modify it
5    	 * under the terms of the GNU General Public License as published by the
6    	 * Free Software Foundation; either version 2 of the License, or (at your
7    	 * option) any later version.
8    	 * 
9    	 * This program is distributed in the hope that it will be useful, but
10   	 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11   	 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12   	 * for more details.
13   	 */
14   	
15   	#include "logger.h"
16   	
17   	
18   	/* return one of these when a status request is made from a PCP 1.x pmlc */
19   	typedef struct {
20   	    __pmTimeval  ls_start;	/* start time for log */
21   	    __pmTimeval  ls_last;	/* last time log written */
22   	    __pmTimeval  ls_timenow;	/* current time */
23   	    int		ls_state;	/* state of log (from __pmLogCtl) */
24   	    int		ls_vol;		/* current volume number of log */
25   	    __int64_t	ls_size;	/* size of current volume */
26   	    char	ls_hostname[PM_LOG_MAXHOSTLEN];
27   					/* name of pmcd host */
28   	    char	ls_tz[40];      /* $TZ at collection host */
29   	    char	ls_tzlogger[40]; /* $TZ at pmlogger */
30   	} __pmLoggerStatus_v1;
31   	
32   	#ifdef PCP_DEBUG
33   	/* This crawls over the data structure looking for weirdness */
34   	void
35   	reality_check(void)
36   	{
37   	    __pmHashNode		*hp;
38   	    task_t		*tp;
39   	    task_t		*tp2;
40   	    fetchctl_t		*fp;
41   	    optreq_t		*rqp;
42   	    pmID		pmid;
43   	    int			i = 0, j, k;
44   	
45   	    /* check that all fetch_t's f_aux point back to their parent task */
46   	    for (tp = tasklist; tp != NULL; tp = tp->t_next, i++) {
47   		if (tp->t_fetch == NULL)
48   		    fprintf(stderr, "task[%d] @" PRINTF_P_PFX "%p has no fetch group\n", i, tp);
49   		j = 0;
50   		for (fp = tp->t_fetch; fp != NULL; fp = fp->f_next) {
51   		    if (fp->f_aux != (void *)tp)
52   			fprintf(stderr, "task[%d] fetch group[%d] has invalid task pointer\n",
53   				i, j);
54   		    j++;
55   		}
56   	
57   		/* check that all optreq_t's in hash list have valid r_fetch->f_aux
58   		 * pointing to a task in the task list.
59   		 */
60   		for (j = 0; j < tp->t_numpmid; j++) {
61   		    pmid = tp->t_pmidlist[j];
62   		    for (hp = __pmHashSearch(pmid, &pm_hash); hp != NULL; hp = hp->next) {
63   			if (pmid != (pmID)hp->key)
64   			continue;
65   			rqp = (optreq_t *)hp->data;
66   			for (tp2 = tasklist; tp2 != NULL; tp2 = tp2->t_next)
67   			    if (rqp->r_fetch->f_aux == (void *)tp2)
68   				break;
69   			if (tp2 == NULL) {
70   			    fprintf(stderr, "task[%d] pmid %s optreq " PRINTF_P_PFX "%p for [",
71   				    i, pmIDStr(pmid), rqp);
72   			    if (rqp->r_numinst == 0)
73   				fputs("`all instances' ", stderr);
74   			    else
75   				for (k = 0; k < rqp->r_numinst; k++)
76   				    fprintf(stderr, "%d ", rqp->r_instlist[k]);
77   			    fputs("] bad task pointer\n", stderr);
78   			}
79   		    }
80   		}
81   	    }
82   	
83   	    
84   	}
85   	
86   	/* Call this in dbx to dump the task list (dbx doesn't know about stdout) */
87   	void
88   	dumpit(void)
89   	{
90   	    int		i;
91   	    task_t	*tp;
92   	
93   	    reality_check();
94   	    for (tp = tasklist, i = 0; tp != NULL; tp = tp->t_next, i++) {
95   		
96   		fprintf(stderr,
97   			"\ntask[%d] @" PRINTF_P_PFX "%p: %s %s \ndelta = %f\n", i, tp,
98   			PMLC_GET_MAND(tp->t_state) ? "mandatory " : "advisory ",
99   			PMLC_GET_ON(tp->t_state) ? "on " : "off ",
100  			tp->t_delta.tv_sec + (float)tp->t_delta.tv_usec / 1.0e6);
101  		__pmOptFetchDump(stderr, tp->t_fetch);
102  	    }
103  	}
104  	
105  	/*
106  	 * stolen from __pmDumpResult
107  	 */
108  	static void
109  	dumpcontrol(FILE *f, const pmResult *resp, int dovalue)
110  	{
111  	    int		i;
112  	    int		j;
113  	
114  	    fprintf(f,"LogControl dump from " PRINTF_P_PFX "%p", resp);
115  	    fprintf(f, " numpmid: %d\n", resp->numpmid);
116  	    for (i = 0; i < resp->numpmid; i++) {
117  		pmValueSet	*vsp = resp->vset[i];
118  		fprintf(f,"  %s :", pmIDStr(vsp->pmid));
119  		if (vsp->numval == 0) {
120  		    fprintf(f, " No values!\n");
121  		    continue;
122  		}
123  		else if (vsp->numval < 0) {
124  		    fprintf(f, " %s\n", pmErrStr(vsp->numval));
125  		    continue;
126  		}
127  		fprintf(f, " numval: %d", vsp->numval);
128  		fprintf(f, " valfmt: %d", vsp->valfmt);
129  		for (j = 0; j < vsp->numval; j++) {
130  		    pmValue	*vp = &vsp->vlist[j];
131  		    if (vsp->numval > 1 || vp->inst != PM_INDOM_NULL) {
132  			fprintf(f," inst [%d]", vp->inst);
133  		    }
134  		    else
135  			fprintf(f, " singular");
136  		    if (dovalue) {
137  			fprintf(f, " value ");
138  			pmPrintValue(f, vsp->valfmt, PM_TYPE_U32, vp, 1); 
139  		    }
140  		    fputc('\n', f);
141  		}
142  	    }
143  	}
144  	
145  	#endif
146  	
147  	/* Called when optFetch or _pmHash routines fail.  This is terminal. */
148  	void
149  	die(char *name, int sts)
150  	{
151  	    __pmNotifyErr(LOG_ERR, "%s error unrecoverable: %s\n", name, pmErrStr(sts));
152  	    exit(1);
153  	}
154  	
155  	static void
156  	linkback(task_t *tp)
157  	{
158  	    fetchctl_t	*fcp;
159  	
160  	    /* link the new fetch groups back to their task */
161  	    for (fcp = tp->t_fetch; fcp != NULL; fcp = fcp->f_next)
162  		fcp->f_aux = (void *)tp;
163  	}
164  	
165  	optreq_t *
166  	findoptreq(pmID pmid, int inst)
167  	{
168  	    __pmHashNode	*hp;
169  	    optreq_t	*rqp;
170  	    optreq_t	*all_rqp = NULL;
171  	    int		j;
172  	
173  	    /*
174  	     * Note:
175  	     * The logic here assumes that for each metric-inst pair, there is
176  	     * at most one optreq_t structure, corresponding to the logging
177  	     * state of ON (mandatory or advisory) else OFF (mandatory).  Other
178  	     * requests change the data structures, but do not leave optreq_t
179  	     * structures lying about, i.e. MAYBE (mandatory) is the default,
180  	     * and does not have to be explicitly stored, while OFF (advisory)
181  	     * reverts to MAYBE (mandatory).
182  	     * There is one execption to the above assumption, namely for
183  	     * cases where the initial specification includes "all" instances,
184  	     * then some later concurrent specification may refer to specific
185  	     * instances ... in this case, the specific optreq_t structure is
186  	     * the one that applies.
187  	     */
188  	    
189  	    for (hp = __pmHashSearch(pmid, &pm_hash); hp != NULL; hp = hp->next) {
190  		if (pmid != (pmID)hp->key)
191  		    continue;
192  		rqp = (optreq_t *)hp->data;
193  		if (rqp->r_numinst == 0) {
194  		    all_rqp = rqp;
195  		    continue;
196  		}
197  		for (j = 0; j < rqp->r_numinst; j++)
198  		    if (inst == rqp->r_instlist[j])
199  			return rqp;
200  	    }
201  	
202  	    if (all_rqp != NULL)
203  		return all_rqp;
204  	    else
205  		return NULL;
206  	}
207  	
208  	/* Determine whether a metric is currently known.  Returns
209  	 *	-1 if metric not known
210  	 *	inclusive OR of the flags below if it is known
211  	 */
212  	#define MF_HAS_INDOM	0x1		/* has an instance domain */
213  	#define MF_HAS_ALL	0x2		/* has an "all instances" */
214  	#define MF_HAS_INST	0x4		/* has specific instance(s) */
215  	#define MF_HAS_MAND	0x8		/* has at least one inst with mandatory */
216  						/* logging (or is mandatory if no indom) */
217  	static int
218  	find_metric(pmID pmid)
219  	{
220  	    __pmHashNode	*hp;
221  	    optreq_t	*rqp;
222  	    int		result = 0;
223  	    int		found = 0;
224  	
225  	    for (hp = __pmHashSearch(pmid, &pm_hash); hp != NULL; hp = hp->next) {
226  		if (pmid != (pmID)hp->key)
227  		    continue;
228  		rqp = (optreq_t *)hp->data;
229  		if (found++ == 0)
230  		    if (rqp->r_desc->indom != PM_INDOM_NULL) {
231  			result |= MF_HAS_INDOM;
232  			if (rqp->r_numinst == 0)
233  			    result |= MF_HAS_ALL;
234  			else
235  			    result |= MF_HAS_INST;
236  		    }
237  		if (PMLC_GET_MAND(((task_t *)(rqp->r_fetch->f_aux))->t_state))
238  		    result |= MF_HAS_MAND;
239  	    }
240  	    return found ? result : -1;
241  	}
242  	
243  	/* Find an optreq_t suitable for adding a new instance */
244  	
245  	/* Add a new metric (given a pmValueSet and a pmDesc) to the specified task.
246  	 * Allocate and return a new task_t if the specified task pointer is nil.
247  	 *
248  	 * Note that this should only be called for metrics not currently in the
249  	 * logging data structure.  All instances in the pmValueSet are added!
250  	 */
251  	static int
252  	add_metric(pmValueSet *vsp, task_t **result)
253  	{
254  	    pmID	pmid = vsp->pmid;
255  	    task_t	*tp = *result;
256  	    optreq_t	*rqp;
257  	    pmDesc	*dp;
258  	    char	*name;
259  	    int		sts, i, need = 0;
260  	
261  	    dp = (pmDesc *)malloc(sizeof(pmDesc));
262  	    if (dp == NULL) {
263  		__pmNoMem("add_metric: new pmDesc malloc", sizeof(pmDesc), PM_FATAL_ERR);
264  	    }
265  	    if ((sts = pmLookupDesc(pmid, dp)) < 0)
266  		die("add_metric: lookup desc", sts);
267  	    if ((sts = pmNameID(pmid, &name)) < 0)
268  		die("add_metric: lookup name", sts);
269  	
270  	    /* allocate a new task if null task pointer passed in */
271  	    if (tp == NULL) {
272  		tp = calloc(1, sizeof(task_t));
273  		if (tp == NULL) {
274  		    __pmNoMem("add_metric: new task calloc", sizeof(task_t), PM_FATAL_ERR);
275  		}
276  		*result = tp;
277  	    }
278  	
279  	    /* add metric (and any instances specified) to task */
280  	    i = tp->t_numpmid++;
281  	    need = tp->t_numpmid * sizeof(pmID);
282  	    if (!(tp->t_pmidlist = (pmID *)realloc(tp->t_pmidlist, need)))
283  		__pmNoMem("add_metric: new task pmidlist realloc", need, PM_FATAL_ERR);
284  	    need = tp->t_numpmid * sizeof(char *);
285  	    if (!(tp->t_namelist = (char **)realloc(tp->t_namelist, need)))
286  		__pmNoMem("add_metric: new task namelist realloc", need, PM_FATAL_ERR);
287  	    need = tp->t_numpmid * sizeof(pmDesc);
288  	    if (!(tp->t_desclist = (pmDesc *)realloc(tp->t_desclist, need)))
289  		__pmNoMem("add_metric: new task desclist realloc", need, PM_FATAL_ERR);
290  	    tp->t_pmidlist[i] = pmid;
291  	    tp->t_namelist[i] = name;
292  	    tp->t_desclist[i] = *dp;	/* struct assignment */
293  	
294  	    rqp = (optreq_t *)calloc(1, sizeof(optreq_t));
295  	    if (rqp == NULL) {
296  		__pmNoMem("add_metric: new task optreq calloc", need, PM_FATAL_ERR);
297  	    }
298  	    rqp->r_desc = dp;
299  	
300  	    /* Now copy instances if required.  Remember that metrics with singular
301  	     * values actually have one instance specified to distinguish them from the
302  	     * "all instances" case (which has no instances).  Use the pmDesc to check
303  	     * for this.
304  	     */
305  	    if (dp->indom != PM_INDOM_NULL)
306  		need = rqp->r_numinst = vsp->numval;
307  	    if (need) {
308  		need *= sizeof(rqp->r_instlist[0]);
309  		rqp->r_instlist = (int *)malloc(need);
310  		if (rqp->r_instlist == NULL) {
311  		    __pmNoMem("add_metric: new task optreq instlist malloc", need,
312  			     PM_FATAL_ERR);
313  		}
314  		for (i = 0; i < vsp->numval; i++)
315  		    rqp->r_instlist[i] = vsp->vlist[i].inst;
316  	    }
317  	
318  	    /* Add new metric to task's fetchgroup(s) and global hash table */
319  	    if ((sts = __pmOptFetchAdd(&tp->t_fetch, rqp)) < 0)
320  		die("add_metric: __pmOptFetchAdd", sts);
321  	    linkback(tp);
322  	    if ((sts = __pmHashAdd(pmid, (void *)rqp, &pm_hash)) < 0)
323  		die("add_metric: __pmHashAdd", sts);
324  	    return 0;
325  	}
326  	
327  	/* Return true if a request for a new logging state (newstate) will be honoured
328  	 * when current state is curstate.
329  	 */
330  	static int
331  	update_ok(int curstate, int newstate)
332  	{
333  	    /* If new state is advisory and current is mandatory, reject request.
334  	     * Any new mandatory state is accepted.  If the new state is advisory
335  	     * and the current state is advisory, it is accepted.
336  	     * Note that a new state of maybe (mandatory maybe) counts as mandatory
337  	     */
338  	    if (PMLC_GET_MAND(newstate) == 0 && PMLC_GET_MAYBE(newstate) == 0 &&
339  		PMLC_GET_MAND(curstate))
340  		return 0;
341  	    else
342  		return 1;
343  	}
344  	
345  	/* Given a task and a pmID, find an optreq_t associated with the task suitable
346  	 * for inserting a new instance into.
347  	 * The one with the smallest number of instances is chosen.  We could also
348  	 * have just used the first, but smallest-first gives a more even distribution.
349  	 */
350  	static optreq_t *
351  	find_instoptreq(task_t *tp, pmID pmid)
352  	{
353  	    optreq_t	*result = NULL;
354  	    optreq_t	*rqp;
355  	    int		ni = 0;
356  	    __pmHashNode	*hp;
357  	
358  	    for (hp = __pmHashSearch(pmid, &pm_hash); hp != NULL;
359  		 hp = hp->next) {
360  		if (pmid != (pmID)hp->key)
361  		    continue;
362  		rqp = (optreq_t *)hp->data;
363  		if ((task_t *)rqp->r_fetch->f_aux != tp)
364  		    continue;
365  		if (rqp->r_numinst == 0)
366  		    continue;			/* don't want "all instances" cases */
367  		if (ni == 0 || rqp->r_numinst < ni) {
368  		    result = rqp;
369  		    ni = rqp->r_numinst;
370  		}
371  	    }
372  	    return result;
373  	}
374  	
375  	/* Delete an optreq_t from its task, free it and remove it from the hash list.
376  	 */
377  	static void
378  	del_optreq(optreq_t *rqp)
379  	{
380  	    int		sts;
381  	    task_t	*tp = (task_t *)rqp->r_fetch->f_aux;
382  	
383  	    if ((sts = __pmOptFetchDel(&tp->t_fetch, rqp)) < 0)
384  		die("del_optreq: __pmOptFetchDel", sts);
385  	    if ((sts = __pmHashDel(rqp->r_desc->pmid, (void *)rqp, &pm_hash)) < 0)
386  		die("del_optreq: __pmHashDel", sts);
387  	    free(rqp->r_desc);
388  	    if (rqp->r_numinst)
389  		free(rqp->r_instlist);
390  	    free(rqp);
391  	    /* TODO: remove pmid from task if that was the last optreq_t for it */
392  	    /* TODO: remove task if last pmid removed */
393  	}
394  	
395  	/* Delete every instance of a given metric from the data structure.
396  	 * The pmid is deleted from the pmidlist of every task containing an instance.
397  	 * Return a pointer to the first pmDesc found (the only thing salvaged from the
398  	 * smoking ruins), or nil if no instances were found.
399  	 */
400  	static pmDesc *
401  	del_insts(pmID pmid)
402  	{
403  	    optreq_t	*rqp;
404  	    __pmHashNode	*hp;
405  	    task_t	*tp;
406  	    pmDesc	*dp = NULL;
407  	    int		i, sts, keep;
408  	
409  	    for (hp = __pmHashSearch(pmid, &pm_hash); hp != NULL; ) {
410  		/* Do that BEFORE we nuke the node */
411  	    	__pmHashNode * nextnode = hp->next;
412  	
413  		if (pmid == (pmID)hp->key) {
414  		    rqp = (optreq_t *)hp->data;
415  		    tp = (task_t *)rqp->r_fetch->f_aux;
416  		    if ((sts = __pmOptFetchDel(&tp->t_fetch, rqp)) < 0)
417  			die("del_insts: __pmOptFetchDel", sts);
418  		    if ((sts = __pmHashDel(pmid, (void *)rqp, &pm_hash)) < 0)
419  			die("del_insts: __pmHashDel", sts);
420  	
421  		    /* save the first pmDesc pointer for return and subsequent
422  		     *  re-use, but free all the others
423  		     */
424  		    if (dp != NULL)
425  			free(rqp->r_desc);
426  		    else
427  			dp = rqp->r_desc;
428  	
429  		    if (rqp->r_numinst)
430  			free(rqp->r_instlist);
431  		    free(rqp);
432  	
433  		    /* remove pmid from the task's pmid list */
434  		    for (i = 0; i < tp->t_numpmid; i++)
435  			if (tp->t_pmidlist[i] == pmid)
436  			    break;
437  		    keep = (tp->t_numpmid - 1 - i) * sizeof(tp->t_pmidlist[0]);
438  		    if (keep) {
439  			memmove(&tp->t_pmidlist[i], &tp->t_pmidlist[i+1], keep);
440  			memmove(&tp->t_desclist[i], &tp->t_desclist[i+1], keep);
441  			memmove(&tp->t_namelist[i], &tp->t_namelist[i+1], keep);
442  		    }
443  	
444  		    /* don't bother shrinking the pmidlist */
445  		    tp->t_numpmid--;
446  		    if (tp->t_numpmid == 0) {
447  			/* TODO: nuke the task if that was the last pmID */
448  		    }
449  		}
450  		hp = nextnode;
451  	    }
452  	
453  	    return dp;
454  	}
455  	
456  	/* Update an existing metric (given a pmValueSet) adding it to the specified
457  	 * task. Allocate and return a new task_t if the specified task pointer is nil.
458  	 */
459  	static int
460  	update_metric(pmValueSet *vsp, int reqstate, int mflags, task_t **result)
461  	{
462  	    pmID	pmid = vsp->pmid;
463  	    task_t	*ntp = *result;		/* pointer to new task */
464  	    task_t	*ctp;			/* pointer to current task */
465  	    optreq_t	*rqp;
466  	    pmDesc	*dp;
467  	    int		i, j, inst;
468  	    int		sts, need = 0;
469  	    int		addpmid = 0;
470  	    int		freedp;
471  	
472  	    /* allocate a new task if null task pointer passed in */
473  	    if (ntp == NULL) {
474  		ntp = calloc(1, sizeof(task_t));
475  		if (ntp == NULL) {
476  		    __pmNoMem("update_metric: new task calloc", sizeof(task_t),
477  			     PM_FATAL_ERR);
478  		}
479  		*result = ntp;
480  	    }
481  	
482  	    if ((mflags & MF_HAS_INDOM) == 0) {
483  		rqp = findoptreq(pmid, 0);
484  		ctp = (task_t *)(rqp->r_fetch->f_aux);
485  		if (!update_ok(ctp->t_state, reqstate))
486  		    return 1;
487  	
488  		/* if the new state is advisory off, just remove the metric */
489  		if ((PMLC_GET_MAYBE(reqstate)) ||
490  		    (PMLC_GET_MAND(reqstate) == 0 && PMLC_GET_ON(reqstate) == 0))
491  		    del_optreq(rqp);
492  		else {
493  		    /* update the optreq.  For single valued metrics there are no
494  		     * instances involved so the sole optreq can just be re-used.
495  		     */
496  		    if ((sts = __pmOptFetchDel(&ctp->t_fetch, rqp)) < 0)
497  			die("update_metric: 1 metric __pmOptFetchDel", sts);
498  		    if ((sts = __pmOptFetchAdd(&ntp->t_fetch, rqp)) < 0)
499  			die("update_metric: 1 metric __pmOptFetchAdd", sts);
500  		    linkback(ntp);
501  		    addpmid = 1;
502  		}
503  	    }
504  	    else {
505  		/* metric has an instance domain */
506  		if (vsp->numval > 0) {
507  		    /* tricky: since optFetch can't handle instance profiles of the
508  		     * form "all except these specific instances", and managing it
509  		     * manually is just too hard, reject requests for specific
510  		     * metric instances if "all instances" of the metric are already
511  		     * being logged.
512  		     * Note: advisory off "all instances" is excepted since ANY request
513  		     * overrides and advisory off.  E.g. "advisory off all" followed by
514  		     * "advisory on someinsts" turns on advisory logging for
515  		     * "someinsts".  mflags will be zero for "advisory off" metrics.
516  		     */
517  		    if (mflags & MF_HAS_ALL)
518  			return 1;		/* can't turn "all" into specific insts */
519  	
520  		    for (i = 0; i < vsp->numval; i++) {
521  			dp = NULL;
522  			freedp = 0;
523  			inst = vsp->vlist[i].inst;
524  			rqp = findoptreq(pmid, inst);
525  			if (rqp != NULL) {
526  			    dp = rqp->r_desc;
527  			    ctp = (task_t *)(rqp->r_fetch->f_aux);
528  			    /* no work required if new task and current are the same */
529  			    if (ntp == ctp)
530  				continue;
531  			    if (!update_ok(ctp->t_state, reqstate))
532  				continue;
533  	
534  			    /* remove inst's group from current task */
535  			    if ((sts = __pmOptFetchDel(&ctp->t_fetch, rqp)) < 0)
536  				die("update_metric: instance add __pmOptFetchDel", sts);
537  	
538  			    /* put group back if there are any instances left */
539  			    if (rqp->r_numinst > 1) {
540  				/* remove inst from group */
541  				for (j = 0; j < rqp->r_numinst; j++)
542  				    if (inst == rqp->r_instlist[j])
543  					break;
544  				/* don't call memmove to move zero bytes */
545  				if (j < rqp->r_numinst - 1)
546  				    memmove(&rqp->r_instlist[j], &rqp->r_instlist[j+1],
547  					    (rqp->r_numinst - 1 - j) *
548  					    sizeof(rqp->r_instlist[0]));
549  				rqp->r_numinst--;
550  				/* (don't bother realloc-ing the instlist to a smaller size) */
551  	
552  				if ((sts = __pmOptFetchAdd(&ctp->t_fetch, rqp)) < 0)
553  				    die("update_metric: instance del __pmOptFetchAdd", sts);
554  				linkback(ctp);
555  				/* no need to update hash list, rqp already there */
556  			    }
557  			    /* if that was the last instance, free the group */
558  			    else {
559  				freedp = 1;
560  				free(rqp->r_instlist);
Event freed_arg: "free" frees "rqp".
Also see events: [pass_freed_arg]
561  				free(rqp);
Event pass_freed_arg: Passing freed pointer "rqp" as an argument to function "__pmHashDel".
Also see events: [freed_arg]
562  				if (( sts = __pmHashDel(pmid, (void *)rqp, &pm_hash)) < 0)
563  				    die("update_metric: instance __pmHashDel", sts);
564  			    }
565  			}
566  	
567  			/* advisory off (mandatory maybe) metrics don't get put into
568  			 * the data structure
569  			 */
570  			if (PMLC_GET_MAYBE(reqstate) ||
571  			    (PMLC_GET_MAND(reqstate) == 0 && PMLC_GET_ON(reqstate) == 0)) {
572  			    if (freedp)
573  				free(dp);
574  			    continue;
575  			}
576  			addpmid = 1;
577  	
578  			/* try to find an existing optreq_t for the instance */
579  			rqp = find_instoptreq(ntp, pmid);
580  			if (rqp != NULL) {
581  			    if ((sts = __pmOptFetchDel(&ntp->t_fetch, rqp)) < 0)
582  				die("update_metric: instance add __pmOptFetchDel", sts);
583  			}
584  			/* no existing optreq_t found, allocate & populate a new one */
585  			else {
586  			    rqp = (optreq_t *)calloc(1, sizeof(optreq_t));
587  			    if (rqp == NULL) {
588  				__pmNoMem("update_metric: optreq calloc",
589  					 sizeof(optreq_t), PM_FATAL_ERR);
590  			    }
591  			    /* if the metric existed but the instance didn't, we don't
592  			     * have a valid pmDesc (dp), so find one.
593  			     */
594  			    if (dp == NULL)  {
595  				/* find metric and associated pmDesc */
596  				__pmHashNode	*hp;
597  	
598  				for (hp = __pmHashSearch(pmid, &pm_hash);
599  				     hp != NULL; hp = hp->next)
600  				    if (pmid == (pmID)hp->key)
601  					break;
602  				dp = ((optreq_t *)hp->data)->r_desc;
603  			    }
604  			    /* recycle pmDesc from the old group, if possible */
605  			    if (freedp) {
606  				rqp->r_desc = dp;
607  				freedp = 0;
608  			    }
609  			    /* otherwise allocate & copy a new pmDesc via dp */
610  			    else {
611  				need = sizeof(pmDesc);
612  				rqp->r_desc = (pmDesc *)malloc(need);
613  				if (rqp->r_desc == NULL) {
614  				    __pmNoMem("update_metric: new inst pmDesc malloc",
615  					     need, PM_FATAL_ERR);
616  				}
617  				memcpy(rqp->r_desc, dp, need);
618  			    }
619  			    if ((sts = __pmHashAdd(pmid, (void *)rqp, &pm_hash)) < 0)
620  				die("update_metric: __pmHashAdd", sts);
621  			}
622  			    
623  			need = (rqp->r_numinst + 1) * sizeof(rqp->r_instlist[0]);
624  			rqp->r_instlist = (int *)realloc(rqp->r_instlist, need);
625  			if (rqp->r_instlist == NULL) {
626  			    __pmNoMem("update_metric: inst list resize", need,
627  				     PM_FATAL_ERR);
628  			}
629  			rqp->r_instlist[rqp->r_numinst++] = inst;
630  			if ((sts = __pmOptFetchAdd(&ntp->t_fetch, rqp)) < 0)
631  			    die("update_metric: instance add __pmOptFetchAdd", sts);
632  			linkback(ntp);
633  			if (freedp)
634  			    free(dp);
635  		    }
636  		}
637  		/* the vset has numval == 0, a request for "all instances" */
638  		else {
639  		    /* if the metric is a singular instance that has mandatory logging
640  		     * or has at least one instance with mandatory logging on, a
641  		     * request for advisory logging cannot be honoured
642  		     */
643  		    if ((mflags & MF_HAS_MAND) &&
644  			PMLC_GET_MAND(reqstate) == 0 && PMLC_GET_MAYBE(reqstate) == 0)
645  			return 1;
646  	
647  		    if (mflags & MF_HAS_ALL) {
648  			/* if there is an "all instances" for the metric, it will be
649  			 * the only optreq_t for the metric
650  			 */
651  			rqp = findoptreq(pmid, 0);
652  			ctp = (task_t *)rqp->r_fetch->f_aux;
653  	
654  			/* if the metric is "advisory on, all instances"  and the
655  			 * request is for "mandatory maybe, all instances" the current
656  			 * advisory logging state of the metric is retained
657  			 */
658  			if (PMLC_GET_MAND(ctp->t_state) == 0 && PMLC_GET_MAYBE(reqstate))
659  			    return 0;
660  	
661  			/* advisory off & mandatory maybe metrics don't get put into
662  			 * the data structure
663  			 */
664  			if (PMLC_GET_MAYBE(reqstate) ||
665  			    (PMLC_GET_MAND(reqstate) == 0 && PMLC_GET_ON(reqstate) == 0)) {
666  			    del_optreq(rqp);
667  			    return 0;
668  			}
669  	
670  			addpmid = 1;
671  			if ((sts = __pmOptFetchDel(&ctp->t_fetch, rqp)) < 0)
672  			    die("update_metric: all inst __pmOptFetchDel", sts);
673  			/* don't delete from hash list, rqp re-used */
674  			if ((sts = __pmOptFetchAdd(&ntp->t_fetch, rqp)) < 0)
675  			    die("update_metric: all inst __pmOptFetchAdd", sts);
676  			linkback(ntp);
677  		    }
678  		    else {
679  			/* there are one or more specific instances for the metric.
680  			 * The metric cannot have an "all instances" at the same time.
681  			 *
682  			 * if the request is for "mandatory maybe, all instances" and
683  			 * the only instances of the metric all have advisory logging
684  			 * on, retain the current advisory semantics.
685  			 */
686  			if (PMLC_GET_MAYBE(reqstate) &&
687  			    (mflags & MF_HAS_INST) && !(mflags & MF_HAS_MAND))
688  			    return 0;
689  	
690  			dp = del_insts(pmid);
691  	
692  			/* advisory off (mandatory maybe) metrics don't get put into
693  			 * the data structure
694  			 */
695  			if (PMLC_GET_MAYBE(reqstate) ||
696  			    (PMLC_GET_MAND(reqstate) == 0 && PMLC_GET_ON(reqstate) == 0)) {
697  			    free(dp);
698  			    return 0;
699  			}
700  	
701  			addpmid = 1;
702  			rqp = (optreq_t *)calloc(1, sizeof(optreq_t));
703  			if (rqp == NULL) {
704  			    __pmNoMem("update_metric: all inst calloc",
705  				     sizeof(optreq_t), PM_FATAL_ERR);
706  			}
707  			rqp->r_desc = dp;
708  			if ((sts = __pmOptFetchAdd(&ntp->t_fetch, rqp)) < 0)
709  			    die("update_metric: all inst __pmOptFetchAdd", sts);
710  			linkback(ntp);
711  			if ((sts = __pmHashAdd(pmid, (void *)rqp, &pm_hash)) < 0)
712  			    die("update_metric: all inst __pmHashAdd", sts);
713  		    }
714  		}
715  	    }
716  	
717  	    if (!addpmid)
718  		return 0;
719  	
720  	    /* add pmid to new task if not already there */
721  	    for (i = 0; i < ntp->t_numpmid; i++)
722  		if (pmid == ntp->t_pmidlist[i])
723  		    break;
724  	    if (i >= ntp->t_numpmid) {
725  		pmDesc	desc;
726  		char	*name;
727  		int	need;
728  	
729  		if ((sts = pmLookupDesc(pmid, &desc)) < 0)
730  		    die("update_metric: cannot lookup desc", sts);
731  		if ((sts = pmNameID(pmid, &name)) < 0)
732  		    die("update_metric: cannot lookup name", sts);
733  	
734  		need = (ntp->t_numpmid + 1) * sizeof(pmID);
735  		if (!(ntp->t_pmidlist = (pmID *)realloc(ntp->t_pmidlist, need)))
736  		    __pmNoMem("update_metric: grow task pmidlist", need, PM_FATAL_ERR);
737  		need = (ntp->t_numpmid + 1) * sizeof(char *);
738  		if (!(ntp->t_namelist = (char **)realloc(ntp->t_namelist, need)))
739  		    __pmNoMem("update_metric: grow task namelist", need, PM_FATAL_ERR);
740  		need = (ntp->t_numpmid + 1) * sizeof(pmDesc);
741  		if (!(ntp->t_desclist = (pmDesc *)realloc(ntp->t_desclist, need)))
742  		    __pmNoMem("update_metric: grow task desclist", need, PM_FATAL_ERR);
743  		i = ntp->t_numpmid;
744  		ntp->t_pmidlist[i] = pmid;
745  		ntp->t_namelist[i] = name;
746  		ntp->t_desclist[i] = desc;
747  		ntp->t_numpmid++;
748  	    }
749  	    return 0;
750  	}
751  	
752  	/* Given a state and a delta, return in result the first matching task.
753  	 * Return true if a matching task was found
754  	 */
755  	static int
756  	find_task(int state, int delta, task_t **result)
757  	{
758  	    task_t	*tp;
759  	    int		tdelta;
760  	
761  	    /* Never return a "once only" task, it may have gone off already and just
762  	     * be hanging around
763  	     * TODO: cleanup once only tasks after callback invoked
764  	     */
765  	    if (delta == 0)
766  		return 0;
767  	
768  	    for (tp = tasklist; tp != NULL; tp = tp->t_next) {
769  		tdelta = tp->t_delta.tv_sec * 1000 + (tp->t_delta.tv_usec / 1000);
770  		if (state == (tp->t_state & 0x3) && delta == tdelta)
771  		    break;
772  	    }
773  	    *result = tp;
774  	    return tp != NULL;
775  	}
776  	
777  	/* Return a mask containing the history flags for a given metric/instance.
778  	 * the history flags indicate whether the metric/instance is in the log at all
779  	 * and whether the last fetch of the metric/instance was successful.
780  	 *
781  	 * The result is suitable for ORing into the result returned by a control log
782  	 * request.
783  	 */
784  	static int
785  	gethistflags(pmID pmid, int inst)
786  	{
787  	    __pmHashNode		*hp;
788  	    pmidhist_t		*php;
789  	    insthist_t		*ihp;
790  	    int			i, found;
791  	    int			val;
792  	
793  	    for (hp = __pmHashSearch(pmid, &hist_hash); hp != NULL; hp = hp->next)
794  		if ((pmID)hp->key == pmid)
795  		    break;
796  	    if (hp == NULL)
797  		return 0;
798  	    php = (pmidhist_t *)hp->data;
799  	    ihp = &php->ph_instlist[0];
800  	    val = 0;
801  	    if (php->ph_indom != PM_INDOM_NULL) {
802  		for (i = 0; i < php->ph_numinst; i++, ihp++)
803  		    if (ihp->ih_inst == inst)
804  			break;
805  		found = i < php->ph_numinst;
806  	    }
807  	    else
808  		found = php->ph_numinst > 0;
809  	    if (found) {
810  		PMLC_SET_INLOG(val, 1);
811  		val |= ihp->ih_flags;		/* only "available flag" is ever set */
812  	    }
813  	    return val;
814  	}
815  	
816  	/* take a pmResult (from a control log request) and half-clone it: return a
817  	 * pointer to a new pmResult struct which shares the pmValueSets in the
818  	 * original that have numval > 0, and has null pointers for the pmValueSets
819  	 * in the original with numval <= 0
820  	 */
821  	static pmResult *
822  	siamise_request(pmResult *request)
823  	{
824  	    int		i, need;
825  	    pmValueSet	*vsp;
826  	    pmResult	*result;
827  	
828  	    need = sizeof(pmResult) + (request->numpmid - 1) * sizeof(pmValueSet *);
829  	    result = (pmResult *)malloc(need);
830  	    if (result == NULL) {
831  		__pmNoMem("siamise_request: malloc pmResult", need, PM_FATAL_ERR);
832  	    }
833  	    for (i = 0; i < request->numpmid; i++) {
834  		vsp = request->vset[i];
835  		if (vsp->numval > 0)
836  		    result->vset[i] = request->vset[i];
837  		else
838  		    result->vset[i] = NULL;
839  	    }
840  	    result->timestamp = request->timestamp; /* structure assignment */
841  	    result->numpmid = request->numpmid;
842  	
843  	    return result;
844  	}
845  	
846  	/* Temporarily borrow a bit in the metric/instance history to indicate that
847  	 * the instance currently exists in the instance domain.  The macros below
848  	 * set and get the bit, which is cleared after we are finished with it here.
849  	 */
850  	
851  	#define PMLC_SET_USEINDOM(val, flag) (val = (val & ~0x1000) | (flag << 12 ))
852  	#define PMLC_GET_USEINDOM(val) ((val & 0x1000) >> 12)
853  	
854  	/* create a pmValueSet large enough to contain the union of the current
855  	 * instance domain of the specified metric and any previous instances from
856  	 * the history list.
857  	 */
858  	static pmValueSet *
859  	build_vset(pmID pmid, int usehist)
860  	{
861  	    __pmHashNode		*hp;
862  	    pmidhist_t		*php = NULL;
863  	    insthist_t		*ihp;
864  	    int			need = 0;
865  	    int			i, numindom = 0;
866  	    pmDesc		desc;
867  	    int			have_desc;
868  	    int			*instlist;
869  	    char		**namelist;
870  	    pmValueSet		*vsp;
871  	
872  	   if (usehist) {
873  		/* find the number of instances of the metric in the history (1 if
874  		 * single-valued metric)
875  		 */
876  		for (hp = __pmHashSearch(pmid, &hist_hash); hp != NULL; hp = hp->next)
877  		    if ((pmID)hp->key == pmid)
878  			break;
879  		if (hp != NULL) {
880  		    php = (pmidhist_t *)hp->data;
881  		    need = php->ph_numinst;
882  		}
883  	    }
884  	    /*
885  	     * get the current instance domain, so that if the metric hasn't been
886  	     * logged yet a sensible result is returned.
887  	     */
888  	    if ((have_desc = pmLookupDesc(pmid, &desc)) < 0)
889  		goto no_info;
890  	    if (desc.indom == PM_INDOM_NULL)
891  		need = 1;			/* will be same in history */
892  	    else {
893  		int	j;
894  		    
895  		if ((numindom = pmGetInDom(desc.indom, &instlist, &namelist)) < 0) {
896  		    have_desc = numindom;
897  		    goto no_info;
898  		}
899  		/* php will be null if usehist is false or there is no history yet */
900  		if (php == NULL)
901  		    need = numindom;		/* no history => use indom */
902  		else
903  		    for (i = 0; i < numindom; i++) {
904  			int	inst = instlist[i];
905  			
906  			for (j = 0; j < php->ph_numinst; j++)
907  			    if (inst == php->ph_instlist[j].ih_inst)
908  				break;
909  			/*
910  			 * if instance is in history but not instance domain, leave
911  			 * extra space for it in vset, otherwise use the USEINDOM
912  			 * flag to avoid another NxM comparison when building the vset
913  			 * instances later.
914  			 */
915  			if (j >= php->ph_numinst)
916  			    need++;
917  			else
918  			    PMLC_SET_USEINDOM(php->ph_instlist[j].ih_flags, 1);
919  		    }
920  	    }
921  	
922  	no_info:
923  	
924  	    need = sizeof(pmValueSet) + (need - 1) * sizeof(pmValue);
925  	    vsp = (pmValueSet *)malloc(need);
926  	    if (vsp == NULL) {
927  		__pmNoMem("build_vset for control/enquire", need, PM_FATAL_ERR);
928  	    }
929  	    vsp->pmid = pmid;
930  	    if (have_desc < 0) {
931  		vsp->numval = have_desc;
932  	    }
933  	    else if (desc.indom == PM_INDOM_NULL) {
934  		vsp->vlist[0].inst = PM_IN_NULL;
935  		vsp->numval = 1;
936  	    }
937  	    else {
938  		int	j;
939  		
940  		i = 0;
941  		/* get instances out of instance domain first */
942  		if (numindom > 0)
943  		    for (j = 0; j < numindom; j++)
944  			vsp->vlist[i++].inst = instlist[j];
945  	
946  		/* then any not in instance domain from history */
947  		if (php != NULL) {
948  		    ihp = &php->ph_instlist[0];
949  		    for (j = 0; j < php->ph_numinst; j++, ihp++)
950  			if (PMLC_GET_USEINDOM(ihp->ih_flags))
951  			    /* it's already in the indom */
952  			    PMLC_SET_USEINDOM(ihp->ih_flags, 0);
953  			else
954  			    vsp->vlist[i++].inst = ihp->ih_inst;
955  		}
956  		vsp->numval = i;
957  		free(instlist);
958  		free(namelist);
959  	    }
960  	    
961  	    return vsp;
962  	}
963  	
964  	static int
965  	do_control(__pmPDU *pb)
966  	{
967  	    int			sts;
968  	    int			control;
969  	    int			state;
970  	    int			delta;
971  	    pmResult		*request;
972  	    pmResult		*result;
973  	    int			siamised = 0;	/* the verb from siamese (as in twins) */
974  	    int			i;
975  	    int			j;
976  	    int			val;
977  	    pmValueSet		*vsp;
978  	    optreq_t		*rqp;
979  	    task_t		*tp;
980  	    time_t		now;
981  	    int			reqstate = 0;
982  	
983  	    /*
984  	     * TODO	- encoding for logging interval in requests and results?
985  	     */
986  	    if ((sts = __pmDecodeLogControl(pb, &request, &control, &state, &delta)) < 0)
987  		return sts;
988  	
989  	#ifdef PCP_DEBUG
990  	    if (pmDebug & DBG_TRACE_LOG) {
991  		fprintf(stderr, "do_control: control=%d state=%d delta=%d request ...\n",
992  			control, state, delta);
993  		dumpcontrol(stderr, request, 0);
994  	    }
995  	#endif
996  	
997  	    if (control == PM_LOG_MANDATORY || control == PM_LOG_ADVISORY) {
998  		time(&now);
999  		fprintf(stderr, "\n%s", ctime(&now));
1000 		fprintf(stderr, "pmlc request from %s: %s",
1001 		    pmlc_host, control == PM_LOG_MANDATORY ? "mandatory" : "advisory");
1002 		if (state == PM_LOG_ON) {
1003 		    if (delta == 0)
1004 			fprintf(stderr, " on once\n");
1005 		    else
1006 			fprintf(stderr, " on %.1f sec\n", (float)delta/1000);
1007 		}
1008 		else if (state == PM_LOG_OFF)
1009 		    fprintf(stderr, " off\n");
1010 		else
1011 		    fprintf(stderr, " maybe\n");
1012 	    }
1013 	
1014 	    /*
1015 	     * access control checks
1016 	     */
1017 	    sts = 0;
1018 	    switch (control) {
1019 		case PM_LOG_MANDATORY:
1020 		    if (clientops & PM_OP_LOG_MAND)
1021 			sts = PM_ERR_PERMISSION;
1022 		    break;
1023 	
1024 		case PM_LOG_ADVISORY:
1025 		    if (clientops & PM_OP_LOG_ADV)
1026 			sts = PM_ERR_PERMISSION;
1027 		    break;
1028 	
1029 		case PM_LOG_ENQUIRE:
1030 		    if (clientops & PM_OP_LOG_ENQ)
1031 			sts = PM_ERR_PERMISSION;
1032 		    break;
1033 	
1034 		default:
1035 		    fprintf(stderr, "Bad control PDU type %d\n", control);
1036 		    sts = PM_ERR_IPC;
1037 		    break;
1038 	    }
1039 	    if (sts < 0) {
1040 		if (control == PM_LOG_MANDATORY || control == PM_LOG_ADVISORY)
1041 		    fprintf(stderr, "Error: %s\n", pmErrStr(sts));
1042 		if ((sts = __pmSendError(clientfd, FROM_ANON, sts)) < 0)
1043 		    __pmNotifyErr(LOG_ERR,
1044 				 "do_control: error sending Error PDU to client: %s\n",
1045 				 pmErrStr(sts));
1046 		pmFreeResult(request);
1047 		return sts;
1048 	    }
1049 	
1050 	    /* handle everything except PM_LOG_ENQUIRE */
1051 	    if (control == PM_LOG_MANDATORY || control == PM_LOG_ADVISORY) {
1052 		/* update the logging status of metrics */
1053 	
1054 		task_t		*newtp = NULL; /* task for metrics/insts in request */
1055 		int		newtask;
1056 		int		mflags;
1057 	
1058 		/* convert state and control to the bitmask used in pmlogger and values
1059 		 * returned in results.  Remember that reqstate starts with nothing on.
1060 		 */
1061 		if (state == PM_LOG_ON)
1062 		    PMLC_SET_ON(reqstate, 1);
1063 		else
1064 		    PMLC_SET_ON(reqstate, 0);
1065 		if (control == PM_LOG_MANDATORY) {
1066 		    if (state == PM_LOG_MAYBE)
1067 			/* mandatory+maybe => maybe+advisory+off  */
1068 			PMLC_SET_MAYBE(reqstate, 1);
1069 		    else
1070 			PMLC_SET_MAND(reqstate, 1);
1071 		}
1072 	
1073 		/* try to find an existing task for the request */
1074 		newtask = !find_task(reqstate, delta, &newtp);
1075 	
1076 		for (i = 0; i < request->numpmid; i++) {
1077 		    vsp = request->vset[i];
1078 		    if (vsp->numval < 0)
1079 			/*
1080 			 * request is malformed, as we cannot control logging
1081 			 * for an undefined instance ... there is no way to
1082 			 * return an error from here, so simply ignore this
1083 			 * metric
1084 			 */
1085 			continue;
1086 		    mflags = find_metric(vsp->pmid);
1087 		    if (mflags < 0) {
1088 			/* only add new metrics if they are ON or MANDATORY OFF
1089 			 * Careful: mandatory+maybe is mandatory+maybe+off
1090 			 */
1091 			if (PMLC_GET_ON(reqstate) ||
1092 			    (PMLC_GET_MAND(reqstate) && !PMLC_GET_MAYBE(reqstate)))
1093 			    add_metric(vsp, &newtp);
1094 		    }
1095 		    else
1096 			/* already a specification for this metric */
1097 			update_metric(vsp, reqstate, mflags, &newtp);
1098 		}
1099 	
1100 		/* schedule new logging task if new metric(s) specified */
1101 		if (newtask && newtp != NULL) {
1102 		    if (newtp->t_fetch == NULL) {
1103 			/* the new task ended up with no fetch groups, throw it away */
1104 			if (newtp->t_pmidlist != NULL)
1105 			    free(newtp->t_pmidlist);
1106 			free(newtp);
1107 		    }
1108 		    else {
1109 			/* link new task into tasklist */
1110 			newtp->t_next = tasklist;
1111 			tasklist = newtp;
1112 	
1113 			/* use only the MAND/ADV and ON/OFF bits of reqstate */
1114 			newtp->t_state = reqstate & 0x3;
1115 			if (PMLC_GET_ON(reqstate)) {
1116 			    newtp->t_delta.tv_sec = delta / 1000;
1117 			    newtp->t_delta.tv_usec = (delta % 1000) * 1000;
1118 			    newtp->t_afid = __pmAFregister(&newtp->t_delta, (void *)newtp,
1119 						       log_callback);
1120 			}
1121 			else
1122 			    newtp->t_delta.tv_sec = newtp->t_delta.tv_usec = 0;
1123 			linkback(newtp);	/* TODO: really needed? (no) */
1124 		    }
1125 		}
1126 	    }
1127 	
1128 	#ifdef PCP_DEBUG
1129 	    if (pmDebug & DBG_TRACE_APPL0)
1130 		dumpit();
1131 	#endif
1132 	
1133 	    /* just ignore advisory+maybe---the returned pmResult will have the metrics
1134 	     * in their original state indicating that the request could not be
1135 	     * satisfied.
1136 	     */
1137 	
1138 	    result = request;
1139 	    result->timestamp.tv_sec = result->timestamp.tv_usec = 0;	/* for purify */
1140 	    /* write the current state of affairs into the result _pmResult */
1141 	    for (i = 0; i < request->numpmid; i++) {
1142 	
1143 		if (control == PM_LOG_MANDATORY || control == PM_LOG_ADVISORY) {
1144 		    char	*p;
1145 	
1146 		    sts = pmNameID(request->vset[i]->pmid, &p);
1147 		    if (sts < 0)
1148 			fprintf(stderr, "  metric: %s", pmIDStr(request->vset[i]->pmid));
1149 		    else {
1150 			fprintf(stderr, "  metric: %s", p);
1151 			free(p);
1152 		    }
1153 		}
1154 	
1155 		if (request->vset[i]->numval <= 0 && !siamised) {
1156 		    result = siamise_request(request);
1157 		    siamised = 1;
1158 		}
1159 		/*
1160 		 * pmids with numval <= 0 in the request have a null vset ptr in the
1161 		 * in the corresponding place in the siamised result.
1162 		 */
1163 		if (result->vset[i] != NULL)
1164 		    vsp = result->vset[i];
1165 		else {
1166 		    /* the result should also contain the history for an all instances
1167 		     * enquire request.  Control requests just get the current indom
1168 		     * since the user of pmlc really wants to see what's being logged
1169 		     * now rather than in the past.
1170 		     */
1171 		    vsp = build_vset(request->vset[i]->pmid, control == PM_LOG_ENQUIRE);
1172 		    result->vset[i] = vsp;
1173 		}
1174 		vsp->valfmt = PM_VAL_INSITU;
1175 		for (j = 0; j < vsp->numval; j++) {
1176 		    rqp = findoptreq(vsp->pmid, vsp->vlist[j].inst);
1177 		    val = 0;
1178 		    if (rqp == NULL) {
1179 			PMLC_SET_STATE(val, 0);
1180 			PMLC_SET_DELTA(val, 0);
1181 		    }
1182 		    else {
1183 			tp = rqp->r_fetch->f_aux;
1184 			PMLC_SET_STATE(val, tp->t_state);
1185 			PMLC_SET_DELTA(val, (tp->t_delta.tv_sec*1000 + tp->t_delta.tv_usec/1000));
1186 		    }
1187 	
1188 		    val |= gethistflags(vsp->pmid, vsp->vlist[j].inst);
1189 		    vsp->vlist[j].value.lval = val;
1190 	
1191 		    if (control == PM_LOG_MANDATORY || control == PM_LOG_ADVISORY) {
1192 			int	expstate = 0;
1193 			int	statemask = 0;
1194 			int	expdelta;
1195 			if (rqp != NULL && rqp->r_desc->indom != PM_INDOM_NULL) {
1196 			    char	*p;
1197 			    if (j == 0)
1198 				fputc('\n', stderr);
1199 			    if (pmNameInDom(rqp->r_desc->indom, vsp->vlist[j].inst, &p) >= 0) {
1200 				fprintf(stderr, "    instance: %s", p);
1201 				free(p);
1202 			    }
1203 			    else
1204 				fprintf(stderr, "    instance: #%d", vsp->vlist[j].inst);
1205 			}
1206 			else {
1207 			    /* no pmDesc ... punt */
1208 			    if (vsp->numval > 1 || vsp->vlist[j].inst != PM_IN_NULL) {
1209 				if (j == 0)
1210 				    fputc('\n', stderr);
1211 				fprintf(stderr, "    instance: #%d", vsp->vlist[j].inst);
1212 			    }
1213 			}
1214 			if (state != PM_LOG_MAYBE) {
1215 			    if (control == PM_LOG_MANDATORY)
1216 				PMLC_SET_MAND(expstate, 1);
1217 			    else
1218 				PMLC_SET_MAND(expstate, 0);
1219 			    if (state == PM_LOG_ON)
1220 				PMLC_SET_ON(expstate, 1);
1221 			    else
1222 				PMLC_SET_ON(expstate, 0);
1223 			    PMLC_SET_MAND(statemask, 1);
1224 			    PMLC_SET_ON(statemask, 1);
1225 			}
1226 			else {
1227 			    PMLC_SET_MAND(expstate, 0);
1228 			    PMLC_SET_MAND(statemask, 1);
1229 			}
1230 			expdelta = PMLC_GET_ON(expstate) ? delta : 0;
1231 			if ((PMLC_GET_STATE(val) & statemask) != expstate ||
1232 			    PMLC_GET_DELTA(val) != expdelta)
1233 				fprintf(stderr, " [request failed]");
1234 			fputc('\n', stderr);
1235 		    }
1236 		}
1237 	    }
1238 	
1239 	#ifdef PCP_DEBUG
1240 	    if (pmDebug & DBG_TRACE_LOG) {
1241 		__pmDumpResult(stderr, result);
1242 	    }
1243 	#endif
1244 	
1245 	    if ((sts = __pmSendResult(clientfd, FROM_ANON, result)) < 0)
1246 			__pmNotifyErr(LOG_ERR,
1247 				     "do_control: error sending Error PDU to client: %s\n",
1248 				     pmErrStr(sts));
1249 	
1250 	    if (siamised) {
1251 		for (i = 0; i < request->numpmid; i++)
1252 		    if (request->vset[i]->numval <= 0)
1253 			free(result->vset[i]);
1254 		free(result);
1255 	    }
1256 	    pmFreeResult(request);
1257 	
1258 	    return 0;
1259 	}
1260 	
1261 	/*
1262 	 * sendstatus
1263 	 * (data_x send is kept for backwards compatability with PCP 1.x)
1264 	 */
1265 	static int
1266 	sendstatus(void)
1267 	{
1268 	    int				rv;
1269 	    int				end;
1270 	    int				version;
1271 	    static int			firsttime = 1;
1272 	    static char			*tzlogger;
1273 	    struct timeval		now;
1274 	    struct hostent		*hep = NULL;
1275 	
1276 	    if (firsttime) {
1277 	        tzlogger = __pmTimezone();
1278 		firsttime = 0;
1279 	    }
1280 	
1281 	    if ((version = __pmVersionIPC(clientfd)) < 0)
1282 		return version;
1283 	
1284 	    if (version >= LOG_PDU_VERSION2) {
1285 		__pmLoggerStatus		ls;
1286 	
1287 		if ((ls.ls_state = logctl.l_state) == PM_LOG_STATE_NEW)
1288 		    ls.ls_start.tv_sec = ls.ls_start.tv_usec = 0;
1289 		else
1290 		    memcpy(&ls.ls_start, &logctl.l_label.ill_start, sizeof(ls.ls_start));
1291 		memcpy(&ls.ls_last, &last_stamp, sizeof(ls.ls_last));
1292 		__pmtimevalNow(&now);
1293 		ls.ls_timenow.tv_sec = (__int32_t)now.tv_sec;
1294 		ls.ls_timenow.tv_usec = (__int32_t)now.tv_usec;
1295 		ls.ls_vol = logctl.l_curvol;
1296 		ls.ls_size = ftell(logctl.l_mfp);
1297 	
1298 		/* be careful of buffer size mismatches when copying strings */
1299 		end = sizeof(ls.ls_hostname) - 1;
1300 		strncpy(ls.ls_hostname, logctl.l_label.ill_hostname, end);
1301 		ls.ls_hostname[end] = '\0';
1302 		end = sizeof(ls.ls_fqdn) - 1;
1303 		hep = gethostbyname(logctl.l_label.ill_hostname);
1304 		if (hep != NULL)
1305 		    /* send the fully qualified domain name */
1306 		    strncpy(ls.ls_fqdn, hep->h_name, end);
1307 		else
1308 		    /* use the hostname from -h ... on command line */
1309 		    strncpy(ls.ls_fqdn, logctl.l_label.ill_hostname, end);
1310 		ls.ls_fqdn[end] = '\0';
1311 	
1312 		end = sizeof(ls.ls_tz) - 1;
1313 		strncpy(ls.ls_tz, logctl.l_label.ill_tz, end);
1314 		ls.ls_tz[end] = '\0';
1315 		end = sizeof(ls.ls_tzlogger) - 1;
1316 		if (tzlogger != NULL)
1317 		    strncpy(ls.ls_tzlogger, tzlogger, end);
1318 		else
1319 		    end = 0;
1320 		ls.ls_tzlogger[end] = '\0';
1321 	
1322 		rv = __pmSendLogStatus(clientfd, &ls);
1323 	    }
1324 	    else
1325 		rv = PM_ERR_IPC;
1326 	    return rv;
1327 	}
1328 	
1329 	static int
1330 	do_request(__pmPDU *pb)
1331 	{
1332 	    int		sts;
1333 	    int		type;
1334 	
1335 	    if ((sts = __pmDecodeLogRequest(pb, &type)) < 0) {
1336 		__pmNotifyErr(LOG_ERR, "do_request: error decoding PDU: %s\n", pmErrStr(sts));
1337 		return PM_ERR_IPC;
1338 	    }
1339 	
1340 	    switch (type) {
1341 		case LOG_REQUEST_STATUS:
1342 		    sts = sendstatus();
1343 		    break;
1344 	
1345 		case LOG_REQUEST_NEWVOLUME:
1346 		    sts = newvolume(VOL_SW_PMLC);
1347 		    if (sts >= 0)
1348 			sts = logctl.l_label.ill_vol;
1349 		    __pmSendError(clientfd, FROM_ANON, sts);
1350 		    break;
1351 	
1352 		case LOG_REQUEST_SYNC:
1353 		    sts = do_flush();
1354 		    __pmSendError(clientfd, FROM_ANON, sts);
1355 		    break;
1356 	
1357 		/*
1358 		 * QA support ... intended for error injection
1359 		 * If the request is > QA_OFF then this is a code to enable
1360 		 * a specific style of error behaviour.  If the request
1361 		 * is QA_OFF, this disables the error behaviour.
1362 		 *
1363 		 * Supported behaviours.
1364 		 * QA_SLEEPY
1365 		 *	After this exchange with pmlc, sleep for 5 seconds
1366 		 * 	after each incoming pmlc request ... allows testing
1367 		 * 	of timeout logic in pmlc
1368 		 */
1369 	
1370 		case QA_OFF:
1371 		    qa_case = 0;
1372 		    __pmSendError(clientfd, FROM_ANON, 0);
1373 		    break;
1374 	
1375 		case QA_SLEEPY:
1376 		    qa_case = type;
1377 		    __pmSendError(clientfd, FROM_ANON, 0);
1378 		    break;
1379 	
1380 		default:
1381 		    fprintf(stderr, "do_request: bad request type %d\n", type);
1382 		    sts = PM_ERR_IPC;
1383 		    break;
1384 	    }
1385 	    return sts;
1386 	}
1387 	
1388 	static int
1389 	do_creds(__pmPDU *pb)
1390 	{
1391 	    int		i;
1392 	    int		sts;
1393 	    int		version = UNKNOWN_VERSION;
1394 	    int		credcount;
1395 	    int		sender;
1396 	    __pmCred	*credlist = NULL;
1397 	
1398 	    if ((sts = __pmDecodeCreds(pb, &sender, &credcount, &credlist)) < 0) {
1399 		__pmNotifyErr(LOG_ERR, "do_creds: error decoding PDU: %s\n", pmErrStr(sts));
1400 			return PM_ERR_IPC;
1401 	    }
1402 	
1403 	    for (i = 0; i < credcount; i++) {
1404 		if (credlist[i].c_type == CVERSION) {
1405 		    version = credlist[i].c_vala;
1406 		    if ((sts = __pmSetVersionIPC(clientfd, version)) < 0) {
1407 			free(credlist);
1408 			return sts;
1409 		    }
1410 		}
1411 	    }
1412 	
1413 	    if (credlist)
1414 		free(credlist);
1415 	
1416 	#ifdef PCP_DEBUG
1417 	    if (pmDebug & DBG_TRACE_APPL1)
1418 		fprintf(stderr, "do_creds: pmlc version=%d\n", version);
1419 	#endif
1420 	
1421 	    return sts;
1422 	}
1423 	
1424 	/*
1425 	 * Service a request from the pmlogger client.
1426 	 * Return non-zero if the client has closed the connection.
1427 	 */
1428 	int
1429 	client_req(void)
1430 	{
1431 	    int		sts;
1432 	    __pmPDU	*pb;
1433 	    __pmPDUHdr	*php;
1434 	
1435 	    if ((sts = __pmGetPDU(clientfd, ANY_SIZE, TIMEOUT_DEFAULT, &pb)) <= 0) {
1436 		if (sts != 0)
1437 		    fprintf(stderr, "client_req: %s\n", pmErrStr(sts));
1438 		__pmResetIPC(clientfd);
1439 		close(clientfd);
1440 		clientfd = -1;
1441 		return 1;
1442 	    }
1443 	    if (qa_case == QA_SLEEPY) {
1444 		/* error injection - delay before processing and responding */
1445 		sleep(5);
1446 	    }
1447 	    php = (__pmPDUHdr *)pb;
1448 	    sts = 0;
1449 	
1450 	    switch (php->type) {
1451 		case PDU_CREDS:		/* version 2 PDU */
1452 		    sts = do_creds(pb);
1453 		    break;
1454 		case PDU_LOG_REQUEST:	/* version 2 PDU */
1455 		    sts = do_request(pb);
1456 		    break;
1457 		case PDU_LOG_CONTROL:	/* version 2 PDU */
1458 		    sts = do_control(pb);
1459 		    break;
1460 		default:		/*  unknown PDU  */
1461 		    fprintf(stderr, "client_req: bad PDU type 0x%x\n", php->type);
1462 		    sts = PM_ERR_IPC;
1463 		    break;
1464 	    }
1465 	    
1466 	    if (sts >= 0)
1467 		return 0;
1468 	    else {
1469 		/* the client isn't playing by the rules; disconnect it */
1470 		__pmSendError(clientfd, FROM_ANON, sts);
1471 		__pmCloseSocket(clientfd);
1472 		clientfd = -1;
1473 		return 1;
1474 	    }
1475 	}