1    	/*
2    	 * Copyright (c) 1995-2002 Silicon Graphics, Inc.  All Rights Reserved.
3    	 * 
4    	 * This library is free software; you can redistribute it and/or modify it
5    	 * under the terms of the GNU Lesser General Public License as published
6    	 * by the Free Software Foundation; either version 2.1 of the License, or
7    	 * (at your option) any later version.
8    	 * 
9    	 * This library 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 Lesser General Public
12   	 * License for more details.
13   	 */
14   	
15   	#include "pmapi.h"
16   	#include "impl.h"
17   	#include <stddef.h>
18   	
19   	/* bytes for a length field in a header/trailer, or a string length field */
20   	#define LENSIZE	4
21   	
22   	#ifdef PCP_DEBUG
23   	static char *
24   	StrTimeval(__pmTimeval *tp)
25   	{
26   	    if (tp == NULL) {
27   		static char *null_timeval = "<null timeval>";
28   		return null_timeval;
29   	    }
30   	    else {
31   		static char		sbuf[13];
32   		struct tm		tmp;
33   		time_t 			t = tp->tv_sec;
34   		pmLocaltime(&t, &tmp);
35   		sprintf(sbuf, "%02d:%02d:%02d.%03d",	/* safe */
36   		    tmp.tm_hour, tmp.tm_min, tmp.tm_sec, tp->tv_usec/1000);
37   		return sbuf;
38   	    }
39   	}
40   	#endif
41   	
42   	__pmHashNode *
43   	__pmHashSearch(unsigned int key, __pmHashCtl *hcp)
44   	{
45   	    __pmHashNode	*hp;
46   	
47   	    if (hcp->hsize == 0)
48   		return NULL;
49   	
50   	    for (hp = hcp->hash[key % hcp->hsize]; hp != NULL; hp = hp->next) {
51   		if (hp->key == key)
52   		    return hp;
53   	    }
54   	    return NULL;
55   	}
56   	
57   	int
58   	__pmHashAdd(unsigned int key, void *data, __pmHashCtl *hcp)
59   	{
60   	    __pmHashNode    *hp;
61   	    int		k;
62   	
63   	    hcp->nodes++;
64   	
65   	    if (hcp->hsize == 0) {
66   		hcp->hsize = 1;	/* arbitrary number */
67   		if ((hcp->hash = (__pmHashNode **)calloc(hcp->hsize, sizeof(__pmHashNode *))) == NULL) {
68   		    hcp->hsize = 0;
69   		    return -oserror();
70   		}
71   	    }
72   	    else if (hcp->nodes / 4 > hcp->hsize) {
73   		__pmHashNode	*tp;
74   		__pmHashNode	**old = hcp->hash;
75   		int		oldsize = hcp->hsize;
76   	
77   		hcp->hsize *= 2;
78   		if (hcp->hsize % 2) hcp->hsize++;
79   		if (hcp->hsize % 3) hcp->hsize += 2;
80   		if (hcp->hsize % 5) hcp->hsize += 2;
81   		if ((hcp->hash = (__pmHashNode **)calloc(hcp->hsize, sizeof(__pmHashNode *))) == NULL) {
82   		    hcp->hsize = oldsize;
83   		    hcp->hash = old;
84   		    return -oserror();
85   		}
86   		/*
87   		 * re-link chains
88   		 */
89   		while (oldsize) {
90   		    for (hp = old[--oldsize]; hp != NULL; ) {
91   			tp = hp;
92   			hp = hp->next;
93   			k = tp->key % hcp->hsize;
94   			tp->next = hcp->hash[k];
95   			hcp->hash[k] = tp;
96   		    }
97   		}
98   		free(old);
99   	    }
100  	
101  	    if ((hp = (__pmHashNode *)malloc(sizeof(__pmHashNode))) == NULL)
102  		return -oserror();
103  	
104  	    k = key % hcp->hsize;
105  	    hp->key = key;
106  	    hp->data = data;
107  	    hp->next = hcp->hash[k];
108  	    hcp->hash[k] = hp;
109  	
110  	    return 1;
111  	}
112  	
113  	int
114  	__pmHashDel(unsigned int key, void *data, __pmHashCtl *hcp)
115  	{
116  	    __pmHashNode    *hp;
117  	    __pmHashNode    *lhp = NULL;
118  	
119  	    if (hcp->hsize == 0)
120  		return 0;
121  	
122  	    for (hp = hcp->hash[key % hcp->hsize]; hp != NULL; hp = hp->next) {
123  		if (hp->key == key && hp->data == data) {
124  		    if (lhp == NULL)
125  			hcp->hash[key % hcp->hsize] = hp->next;
126  		    else
127  			lhp->next = hp->next;
128  		    free(hp);
129  		    return 1;
130  		}
131  		lhp = hp;
132  	    }
133  	
134  	    return 0;
135  	}
136  	
137  	static int
138  	addindom(__pmLogCtl *lcp, pmInDom indom, const __pmTimeval *tp, int numinst, 
139  	         int *instlist, char **namelist, int *indom_buf, int allinbuf)
140  	{
141  	    __pmLogInDom	*idp;
142  	    __pmHashNode	*hp;
143  	    int		sts;
144  	
145  	    if ((idp = (__pmLogInDom *)malloc(sizeof(__pmLogInDom))) == NULL)
146  		return -oserror();
147  	    idp->stamp = *tp;		/* struct assignment */
148  	    idp->numinst = numinst;
149  	    idp->instlist = instlist;
150  	    idp->namelist = namelist;
151  	    idp->buf = indom_buf;
152  	    idp->allinbuf = allinbuf;
153  	
154  	#ifdef PCP_DEBUG
155  	    if (pmDebug & DBG_TRACE_LOGMETA)
156  		fprintf(stderr, "addindom( ..., %s, %s, numinst=%d)\n",
157  		    pmInDomStr(indom), StrTimeval((__pmTimeval *)tp), numinst);
158  	#endif
159  	
160  	
161  	    if ((hp = __pmHashSearch((unsigned int)indom, &lcp->l_hashindom)) == NULL) {
162  		idp->next = NULL;
163  		sts = __pmHashAdd((unsigned int)indom, (void *)idp, &lcp->l_hashindom);
164  	    }
165  	    else {
166  		idp->next = (__pmLogInDom *)hp->data;
167  		hp->data = (void *)idp;
168  		sts = 0;
169  	    }
170  	    return sts;
171  	}
172  	
173  	/*
174  	 * load _all_ of the hashed pmDesc and __pmLogInDom structures from the metadata
175  	 * log file -- used at the initialization (NewContext) of an archive
176  	 * If version 2 then
177  	 * load all the names from the meta data and create l_pmns.
178  	 */
179  	int
180  	__pmLogLoadMeta(__pmLogCtl *lcp)
181  	{
182  	    int		rlen;
183  	    int		check;
184  	    pmDesc	*dp;
185  	    int		sts = 0;
186  	    __pmLogHdr	h;
187  	    FILE	*f = lcp->l_mdfp;
188  	    int         version2 = ((lcp->l_label.ill_magic & 0xff) == PM_LOG_VERS02);
189  	    int		numpmid = 0;
190  	    int		n;
191  	    
192  	    if (version2) {
193  	       if ((sts = __pmNewPMNS(&(lcp->l_pmns))) < 0) {
194  	          goto end;
195  	       }
196  	    }
197  	
198  	    fseek(f, (long)(sizeof(__pmLogLabel) + 2*sizeof(int)), SEEK_SET);
199  	    for ( ; ; ) {
200  		n = (int)fread(&h, 1, sizeof(__pmLogHdr), f);
201  	
202  		/* swab hdr */
203  		h.len = ntohl(h.len);
204  		h.type = ntohl(h.type);
205  	
206  		if (n != sizeof(__pmLogHdr) || h.len <= 0) {
207  	            if (feof(f)) {
208  			clearerr(f);
209  	                sts = 0;
210  			goto end;
211  	            }
212  	#ifdef PCP_DEBUG
213  		    if (pmDebug & DBG_TRACE_LOGMETA) {
214  			fprintf(stderr, "__pmLogLoadMeta: header read -> %d: expected: %d\n",
215  				n, (int)sizeof(__pmLogHdr));
216  		    }
217  	#endif
218  		    if (ferror(f)) {
219  			clearerr(f);
220  			sts = -oserror();
221  		    }
222  		    else
223  			sts = PM_ERR_LOGREC;
224  		    goto end;
225  		}
226  	#ifdef PCP_DEBUG
227  		if (pmDebug & DBG_TRACE_LOGMETA) {
228  		    fprintf(stderr, "__pmLogLoadMeta: record len=%d, type=%d @ offset=%d\n",
229  			h.len, h.type, (int)(ftell(f) - sizeof(__pmLogHdr)));
230  		}
231  	#endif
232  		rlen = h.len - (int)sizeof(__pmLogHdr) - (int)sizeof(int);
233  		if (h.type == TYPE_DESC) {
234  	            numpmid++;
235  		    if ((dp = (pmDesc *)malloc(sizeof(pmDesc))) == NULL) {
236  			sts = -oserror();
237  			goto end;
238  		    }
239  		    if ((n = (int)fread(dp, 1, sizeof(pmDesc), f)) != sizeof(pmDesc)) {
240  	#ifdef PCP_DEBUG
241  			if (pmDebug & DBG_TRACE_LOGMETA) {
242  			    fprintf(stderr, "__pmLogLoadMeta: pmDesc read -> %d: expected: %d\n",
243  				    n, (int)sizeof(pmDesc));
244  			}
245  	#endif
246  			if (ferror(f)) {
247  			    clearerr(f);
248  			    sts = -oserror();
249  			}
250  			else
251  			    sts = PM_ERR_LOGREC;
252  			goto end;
253  		    }
254  		    else {
255  			/* swab desc */
256  			dp->type = ntohl(dp->type);
257  			dp->sem = ntohl(dp->sem);
258  			dp->indom = __ntohpmInDom(dp->indom);
259  			dp->units = __ntohpmUnits(dp->units);
260  			dp->pmid = __ntohpmID(dp->pmid);
261  		    }
262  	
263  		    if ((sts = __pmHashAdd((int)dp->pmid, (void *)dp, &lcp->l_hashpmid)) < 0)
264  			goto end;
265  	
266  	            if (version2) {
267  	                char name[MAXPATHLEN];
268  	                int numnames;
269  			int i;
270  			int len;
271  	
272  	                /* read in the names & store in PMNS tree ... */
273  			if ((n = (int)fread(&numnames, 1, sizeof(numnames), f)) != 
274  	                     sizeof(numnames)) {
275  	#ifdef PCP_DEBUG
276  			    if (pmDebug & DBG_TRACE_LOGMETA) {
277  				fprintf(stderr, "__pmLogLoadMeta: numnames read -> %d: expected: %d\n",
278  					n, (int)sizeof(numnames));
279  			    }
280  	#endif
281  			    if (ferror(f)) {
282  				clearerr(f);
283  				sts = -oserror();
284  			    }
285  			    else
286  				sts = PM_ERR_LOGREC;
287  			    goto end;
288  			}
289  			else {
290  			    /* swab numnames */
291  			    numnames = ntohl(numnames);
292  			}
293  	
294  	 		for (i = 0; i < numnames; i++) {
295  			    if ((n = (int)fread(&len, 1, sizeof(len), f)) != 
296  				 sizeof(len)) {
297  	#ifdef PCP_DEBUG
298  				if (pmDebug & DBG_TRACE_LOGMETA) {
299  				    fprintf(stderr, "__pmLogLoadMeta: len name[%d] read -> %d: expected: %d\n",
300  					    i, n, (int)sizeof(len));
301  				}
302  	#endif
303  				if (ferror(f)) {
304  				    clearerr(f);
305  				    sts = -oserror();
306  				}
307  				else
308  				    sts = PM_ERR_LOGREC;
309  				goto end;
310  			    }
311  			    else {
312  				/* swab len */
313  				len = ntohl(len);
314  			    }
315  	
316  			    if ((n = (int)fread(name, 1, len, f)) != len) {
317  	#ifdef PCP_DEBUG
318  				if (pmDebug & DBG_TRACE_LOGMETA) {
319  				    fprintf(stderr, "__pmLogLoadMeta: name[%d] read -> %d: expected: %d\n",
320  					    i, n, len);
321  				}
322  	#endif
323  				if (ferror(f)) {
324  				    clearerr(f);
325  				    sts = -oserror();
326  				}
327  				else
328  				    sts = PM_ERR_LOGREC;
329  				goto end;
330  			    }
331  	                    name[len] = '\0';
332  	#ifdef PCP_DEBUG
333  			    if (pmDebug & DBG_TRACE_LOGMETA) {
334  				fprintf(stderr, "__pmLogLoadMeta: PMID: %s name: %s\n",
335  					pmIDStr(dp->pmid), name);
336  			    }
337  	#endif
338  	
339  	                    if ((sts = __pmAddPMNSNode(lcp->l_pmns, dp->pmid, name)) < 0) {
340  				/*
341  				 * If we see a duplicate PMID, its a recoverable error.
342  				 * We wont be able to see all of the data in the log, but
343  				 * its better to provide access to some rather than none,
344  				 * esp. when only one or two metric IDs may be corrupted
345  				 * in this way (which we may not be interested in anyway).
346  				 */
347  				if (sts != PM_ERR_PMID)
348  				    goto end;
349  				sts = 0;
350  			    } 
351  			}/*for*/
352  	            }/*version2*/
353  		}
354  		else if (h.type == TYPE_INDOM) {
355  		    int			*tbuf;
356  		    pmInDom		indom;
357  		    __pmTimeval		*when;
358  		    int			numinst;
359  		    int			*instlist;
360  		    char		**namelist;
361  		    char		*namebase;
362  		    int			*stridx;
363  		    int			i;
364  		    int			k;
365  		    int			allinbuf;
366  	
Event alloc_fn: Calling allocation function "malloc".
Event var_assign: Assigning: "tbuf" = storage returned from "malloc(rlen)".
Also see events: [noescape][leaked_storage]
At conditional (1): "(tbuf = (int *)malloc(rlen)) == NULL": Taking false branch.
367  		    if ((tbuf = (int *)malloc(rlen)) == NULL) {
368  			sts = -oserror();
369  			goto end;
370  		    }
Event noescape: Variable "tbuf" is not freed or pointed-to in function "fread".
Also see events: [alloc_fn][var_assign][leaked_storage]
At conditional (2): "(n = (int)fread(tbuf, 1UL, rlen, f)) != rlen": Taking true branch.
371  		    if ((n = (int)fread(tbuf, 1, rlen, f)) != rlen) {
372  	#ifdef PCP_DEBUG
At conditional (3): "pmDebug & 0x100": Taking true branch.
373  			if (pmDebug & DBG_TRACE_LOGMETA) {
374  			    fprintf(stderr, "__pmLogLoadMeta: indom read -> %d: expected: %d\n",
375  				    n, rlen);
376  			}
377  	#endif
At conditional (4): "ferror(f)": Taking true branch.
378  			if (ferror(f)) {
379  			    clearerr(f);
380  			    sts = -oserror();
381  			}
382  			else
383  			    sts = PM_ERR_LOGREC;
Event leaked_storage: Variable "tbuf" going out of scope leaks the storage it points to.
Also see events: [alloc_fn][var_assign][noescape]
384  			goto end;
385  		    }
386  	
387  		    k = 0;
388  		    when = (__pmTimeval *)&tbuf[k];
389  		    when->tv_sec = ntohl(when->tv_sec);
390  		    when->tv_usec = ntohl(when->tv_usec);
391  		    k += sizeof(*when)/sizeof(int);
392  		    indom = __ntohpmInDom((unsigned int)tbuf[k++]);
393  		    numinst = ntohl(tbuf[k++]);
394  		    if (numinst > 0) {
395  			instlist = &tbuf[k];
396  			k += numinst;
397  			stridx = &tbuf[k];
398  	#if defined(HAVE_32BIT_PTR)
399  			namelist = (char **)stridx;
400  			allinbuf = 1; /* allocation is all in tbuf */
401  	#else
402  			allinbuf = 0; /* allocation for namelist + tbuf */
403  			/* need to allocate to hold the pointers */
404  			namelist = (char **)malloc(numinst*sizeof(char*));
405  			if (namelist == NULL) {
406  			    sts = -oserror();
407  			    goto end;
408  			}
409  	#endif
410  			k += numinst;
411  			namebase = (char *)&tbuf[k];
412  		        for (i = 0; i < numinst; i++) {
413  			    instlist[i] = ntohl(instlist[i]);
414  		            namelist[i] = &namebase[ntohl(stridx[i])];
415  			}
416  		    }
417  		    else {
418  			/* no instances, or an error */
419  			instlist = NULL;
420  			namelist = NULL;
421  		    }
422  		    if ((sts = addindom(lcp, indom, when, numinst, instlist, namelist, tbuf, allinbuf)) < 0)
423  			goto end;
424  		}
425  		else
426  		    fseek(f, (long)rlen, SEEK_CUR);
427  		n = (int)fread(&check, 1, sizeof(check), f);
428  		check = ntohl(check);
429  		if (n != sizeof(check) || h.len != check) {
430  	#ifdef PCP_DEBUG
431  		    if (pmDebug & DBG_TRACE_LOGMETA) {
432  			fprintf(stderr, "__pmLogLoadMeta: trailer read -> %d or len=%d: expected %d @ offset=%d\n",
433  			    n, check, h.len, (int)(ftell(f) - sizeof(check)));
434  		    }
435  	#endif
436  		    if (ferror(f)) {
437  			clearerr(f);
438  			sts = -oserror();
439  		    }
440  		    else
441  			sts = PM_ERR_LOGREC;
442  		    goto end;
443  		}
444  	    }/*for*/
445  	end:
446  	    
447  	    fseek(f, (long)(sizeof(__pmLogLabel) + 2*sizeof(int)), SEEK_SET);
448  	
449  	    if (version2 && sts == 0) {
450  	        __pmFixPMNSHashTab(lcp->l_pmns, numpmid, 1);
451  	    }
452  	    return sts;
453  	}
454  	
455  	/*
456  	 * scan the hashed data structures to find a pmDesc, given a pmid
457  	 */
458  	int
459  	__pmLogLookupDesc(__pmLogCtl *lcp, pmID pmid, pmDesc *dp)
460  	{
461  	    __pmHashNode	*hp;
462  	    pmDesc	*tp;
463  	
464  	    if ((hp = __pmHashSearch((unsigned int)pmid, &lcp->l_hashpmid)) == NULL)
465  		return PM_ERR_PMID_LOG;
466  	
467  	    tp = (pmDesc *)hp->data;
468  	    *dp = *tp;			/* struct assignment */
469  	    return 0;
470  	}
471  	
472  	/*
473  	 * Add a new pmDesc into the metadata log, and to the hashed data structures
474  	 * If numnames is positive, then write out any associated PMNS names.
475  	 */
476  	int
477  	__pmLogPutDesc(__pmLogCtl *lcp, const pmDesc *dp, int numnames, char **names)
478  	{
479  	    __pmLogHdr	h;
480  	    FILE	*f = lcp->l_mdfp;
481  	    pmDesc	*tdp;
482  	    pmDesc	*odp;		/* pmDesc to write out */
483  	    int		onumnames;	/* numnames to write out */
484  	    int		olen;		/* length to write out */
485  	    int		i;
486  	    int		len;
487  	    pmDesc	tmp;
488  	
489  	    h.type = htonl(TYPE_DESC);
490  	    len = sizeof(__pmLogHdr) + sizeof(pmDesc) + LENSIZE;
491  	
492  	    tmp.type = htonl(dp->type);
493  	    tmp.sem = htonl(dp->sem);
494  	    tmp.indom = __htonpmInDom(dp->indom);
495  	    tmp.units = __htonpmUnits(dp->units);
496  	    tmp.pmid = __htonpmID(dp->pmid);
497  	    odp = &tmp;
498  	
499  	    if (numnames > 0) {
500  	        len += sizeof(numnames);
501  	        for (i = 0; i < numnames; i++)
502  	            len += LENSIZE + (int)strlen(names[i]);
503  	
504  		h.len = htonl(len);
505  		onumnames = htonl(numnames);
506  		if (fwrite(&h, 1, sizeof(__pmLogHdr), f) != sizeof(__pmLogHdr) ||
507  		    fwrite(odp, 1, sizeof(pmDesc), f) != sizeof(pmDesc) ||
508  	            fwrite(&onumnames, 1, sizeof(numnames), f) != sizeof(numnames))
509  			return -oserror();
510  	
511  	        /* write out the names */
512  	        for (i = 0; i < numnames; i++) {
513  		    int slen = (int)strlen(names[i]);
514  		    olen = htonl(slen);
515  	            if (fwrite(&olen, 1, LENSIZE, f) != LENSIZE)
516  	                return -oserror();
517  	            if (fwrite(names[i], 1, slen, f) != slen)
518  	                return -oserror();
519  	        }
520  	
521  		olen = htonl(len);
522  		if (fwrite(&olen, 1, LENSIZE, f) != LENSIZE)
523  		    return -oserror();
524  	    }
525  	    else {
526  		h.len = htonl(len);
527  		olen = htonl(len);
528  		if (fwrite(&h, 1, sizeof(__pmLogHdr), f) != sizeof(__pmLogHdr) ||
529  		    fwrite(odp, 1, sizeof(pmDesc), f) != sizeof(pmDesc) ||
530  		    fwrite(&olen, 1, LENSIZE, f) != LENSIZE)
531  			return -oserror();
532  	    }
533  	
534  	    /*
535  	     * need to make a copy of the pmDesc, and add this, since caller
536  	     * may re-use *dp
537  	     */
538  	    if ((tdp = (pmDesc *)malloc(sizeof(pmDesc))) == NULL)
539  		return -oserror();
540  	    *tdp = *dp;		/* struct assignment */
541  	    return __pmHashAdd((int)dp->pmid, (void *)tdp, &lcp->l_hashpmid);
542  	}
543  	
544  	static __pmLogInDom *
545  	searchindom(__pmLogCtl *lcp, pmInDom indom, __pmTimeval *tp)
546  	{
547  	    __pmHashNode	*hp;
548  	    __pmLogInDom	*idp;
549  	
550  	#ifdef PCP_DEBUG
551  	    if (pmDebug & DBG_TRACE_LOGMETA)
552  		fprintf(stderr, "searchindom( ..., %s, %s)\n",
553  		    pmInDomStr(indom), StrTimeval(tp));
554  	#endif
555  	
556  	    if ((hp = __pmHashSearch((unsigned int)indom, &lcp->l_hashindom)) == NULL)
557  		return NULL;
558  	
559  	    idp = (__pmLogInDom *)hp->data;
560  	    if (tp != NULL) {
561  		for ( ; idp != NULL; idp = idp->next) {
562  		    /*
563  		     * need first one at or earlier than the requested time
564  		     */
565  		    if (__pmTimevalSub(&idp->stamp, tp) <= 0)
566  			break;
567  	#ifdef PCP_DEBUG
568  		    if (pmDebug & DBG_TRACE_LOGMETA) {
569  			fprintf(stderr, "request @ %s is ", StrTimeval(tp));
570  			fprintf(stderr, "too early for indom @ %s\n",
571  			    StrTimeval(&idp->stamp));
572  		    }
573  	#endif
574  		}
575  		if (idp == NULL)
576  		    return NULL;
577  	    }
578  	
579  	#ifdef PCP_DEBUG
580  	    if (pmDebug & DBG_TRACE_LOGMETA)
581  		fprintf(stderr, "success for indom @ %s\n",
582  		    StrTimeval(&idp->stamp));
583  	#endif
584  	    return idp;
585  	}
586  	
587  	/*
588  	 * for the given indom retrieve the instance domain that is correct
589  	 * as of the latest time (tp == NULL) or at a designated
590  	 * time
591  	 */
592  	int
593  	__pmLogGetInDom(__pmLogCtl *lcp, pmInDom indom, __pmTimeval *tp, int **instlist, char ***namelist)
594  	{
595  	    __pmLogInDom	*idp = searchindom(lcp, indom, tp);
596  	
597  	    if (idp == NULL)
598  		return PM_ERR_INDOM_LOG;
599  	
600  	    *instlist = idp->instlist;
601  	    *namelist = idp->namelist;
602  	
603  	    return idp->numinst;
604  	}
605  	
606  	int
607  	__pmLogLookupInDom(__pmLogCtl *lcp, pmInDom indom, __pmTimeval *tp, 
608  			   const char *name)
609  	{
610  	    __pmLogInDom	*idp = searchindom(lcp, indom, tp);
611  	    int		i;
612  	
613  	    if (idp == NULL)
614  		return PM_ERR_INDOM_LOG;
615  	
616  	    if (idp->numinst < 0)
617  		return idp->numinst;
618  	
619  	    /* full match */
620  	    for (i = 0; i < idp->numinst; i++) {
621  		if (strcmp(name, idp->namelist[i]) == 0)
622  		    return idp->instlist[i];
623  	    }
624  	
625  	    /* half-baked match to first space */
626  	    for (i = 0; i < idp->numinst; i++) {
627  		char	*p = idp->namelist[i];
628  		while (*p && *p != ' ')
629  		    p++;
630  		if (*p == ' ') {
631  		    if (strncmp(name, idp->namelist[i], p - idp->namelist[i]) == 0)
632  			return idp->instlist[i];
633  		}
634  	    }
635  	
636  	    return PM_ERR_INST_LOG;
637  	}
638  	
639  	int
640  	__pmLogNameInDom(__pmLogCtl *lcp, pmInDom indom, __pmTimeval *tp, int inst, char **name)
641  	{
642  	    __pmLogInDom	*idp = searchindom(lcp, indom, tp);
643  	    int		i;
644  	
645  	    if (idp == NULL)
646  		return PM_ERR_INDOM_LOG;
647  	
648  	    if (idp->numinst < 0)
649  		return idp->numinst;
650  	
651  	    for (i = 0; i < idp->numinst; i++) {
652  		if (inst == idp->instlist[i]) {
653  		    *name = idp->namelist[i];
654  		    return 0;
655  		}
656  	    }
657  	
658  	    return PM_ERR_INST_LOG;
659  	}
660  	
661  	int
662  	__pmLogPutInDom(__pmLogCtl *lcp, pmInDom indom, const __pmTimeval *tp, 
663  			int numinst, int *instlist, char **namelist)
664  	{
665  	    int		sts = 0;
666  	    __pmLogHdr	h;
667  	    int		i;
668  	    int		wlen;
669  	    int		strsize;
670  	    int		*stridx;
671  	    int		real_numinst;
672  	    int		onuminst;
673  	    pmInDom	oindom;
674  	    __pmTimeval	otp;
675  	
676  	    /*
677  	     * Note: this routine depends upon the names pointed to by namelist[]
678  	     *       being packed, and starting at namelist[0] ... this is exactly
679  	     *       the format returned by pmGetInDom and __pmLogLoadMeta, so it
680  	     *       should be OK
681  	     */
682  	
683  	    real_numinst = numinst > 0 ? numinst : 0;
684  	    if ((stridx = (int *)malloc(real_numinst * sizeof(stridx[0]))) == NULL)
685  		return -oserror();
686  	
687  	    h.len = (int)sizeof(__pmLogHdr) + (int)sizeof(*tp) + (int)sizeof(indom) +
688  		    (int)sizeof(numinst) +
689  		    real_numinst * ((int)sizeof(instlist[0]) + (int)sizeof(stridx[0])) +
690  		    (int)sizeof(h.len);
691  	    strsize = 0;
692  	    for (i = 0; i < numinst; i++) {
693  		strsize += (int)strlen(namelist[i]) + 1;
694  		/* see Note */
695  		stridx[i] = (int)((ptrdiff_t)namelist[i] - (ptrdiff_t)namelist[0]);
696  		stridx[i] = htonl(stridx[i]); /* swab */
697  		instlist[i] = htonl(instlist[i]); /* swab: this is changed back after writing */
698  	    }
699  	    h.len += strsize;
700  	
701  	    /* swab all output buffers */
702  	    h.len = htonl(h.len);
703  	    h.type = htonl(TYPE_INDOM);
704  	    otp.tv_sec = htonl(tp->tv_sec);
705  	    otp.tv_usec = htonl(tp->tv_usec);
706  	    oindom = __htonpmInDom(indom);
707  	    onuminst = htonl(numinst);
708  	
709  	    wlen = (int)fwrite(&h, 1, sizeof(__pmLogHdr), lcp->l_mdfp);
710  	    wlen += fwrite(&otp, 1, sizeof(otp), lcp->l_mdfp);
711  	    wlen += fwrite(&oindom, 1, sizeof(oindom), lcp->l_mdfp);
712  	    wlen += fwrite(&onuminst, 1, sizeof(onuminst), lcp->l_mdfp);
713  	    if (numinst > 0) {
714  		wlen += fwrite(instlist, 1, real_numinst * sizeof(instlist[0]), lcp->l_mdfp);
715  		wlen += fwrite(stridx, 1, real_numinst * sizeof(stridx[0]), lcp->l_mdfp);
716  		/* see Note */
717  		wlen += fwrite(namelist[0], 1, strsize, lcp->l_mdfp);
718  	    }
719  	    wlen += fwrite(&h.len, 1, sizeof(h.len), lcp->l_mdfp);
720  	    free(stridx);
721  	
722  	    if (wlen != (int)ntohl(h.len)) {
723  		pmprintf("__pmLogPutInDom: wrote %d, expected %d: %s\n",
724  		    wlen, (int)ntohl(h.len), osstrerror());
725  		pmflush();
726  		return -oserror();
727  	    }
728  	
729  	    sts = addindom(lcp, indom, tp, numinst, instlist, namelist, NULL, 0);
730  	
731  	    /* swab instance list back again so as to not upset the caller */
732  	    for (i = 0; i < numinst; i++) {
733  		instlist[i] = ntohl(instlist[i]);
734  	    }
735  	
736  	    return sts;
737  	}
738  	
739  	int
740  	pmLookupInDomArchive(pmInDom indom, const char *name)
741  	{
742  	    int		n;
743  	    int		j;
744  	    __pmHashNode	*hp;
745  	    __pmLogInDom	*idp;
746  	    __pmContext	*ctxp;
747  	
748  	    if (indom == PM_INDOM_NULL)
749  		return PM_ERR_INDOM;
750  	
751  	    if ((n = pmWhichContext()) >= 0) {
752  		ctxp = __pmHandleToPtr(n);
753  		if (ctxp->c_type != PM_CONTEXT_ARCHIVE)
754  		    return PM_ERR_NOTARCHIVE;
755  	
756  		if ((hp = __pmHashSearch((unsigned int)indom, &ctxp->c_archctl->ac_log->l_hashindom)) == NULL)
757  		    return PM_ERR_INDOM_LOG;
758  	
759  		for (idp = (__pmLogInDom *)hp->data; idp != NULL; idp = idp->next) {
760  		    /* full match */
761  		    for (j = 0; j < idp->numinst; j++) {
762  			if (strcmp(name, idp->namelist[j]) == 0) {
763  			    return idp->instlist[j];
764  			}
765  		    }
766  		    /* half-baked match to first space */
767  		    for (j = 0; j < idp->numinst; j++) {
768  			char	*p = idp->namelist[j];
769  			while (*p && *p != ' ')
770  			    p++;
771  			if (*p == ' ') {
772  			    if (strncmp(name, idp->namelist[j], p - idp->namelist[j]) == 0)
773  				return idp->instlist[j];
774  			}
775  		    }
776  		}
777  		n = PM_ERR_INST_LOG;
778  	    }
779  	
780  	    return n;
781  	}
782  	
783  	int
784  	pmNameInDomArchive(pmInDom indom, int inst, char **name)
785  	{
786  	    int		n;
787  	    int		j;
788  	    __pmHashNode	*hp;
789  	    __pmLogInDom	*idp;
790  	    __pmContext	*ctxp;
791  	
792  	    if (indom == PM_INDOM_NULL)
793  		return PM_ERR_INDOM;
794  	
795  	    if ((n = pmWhichContext()) >= 0) {
796  		ctxp = __pmHandleToPtr(n);
797  		if (ctxp->c_type != PM_CONTEXT_ARCHIVE)
798  		    return PM_ERR_NOTARCHIVE;
799  	
800  		if ((hp = __pmHashSearch((unsigned int)indom, &ctxp->c_archctl->ac_log->l_hashindom)) == NULL)
801  		    return PM_ERR_INDOM_LOG;
802  	
803  		for (idp = (__pmLogInDom *)hp->data; idp != NULL; idp = idp->next) {
804  		    for (j = 0; j < idp->numinst; j++) {
805  			if (idp->instlist[j] == inst) {
806  			    if ((*name = strdup(idp->namelist[j])) == NULL)
807  				n = -oserror();
808  			    else
809  				n = 0;
810  			    return n;
811  			}
812  		    }
813  		}
814  		n = PM_ERR_INST_LOG;
815  	    }
816  	
817  	    return n;
818  	}
819  	
820  	int
821  	pmGetInDomArchive(pmInDom indom, int **instlist, char ***namelist)
822  	{
823  	    int			n;
824  	    int			i;
825  	    int			j;
826  	    char		*p;
827  	    __pmContext		*ctxp;
828  	    __pmHashNode		*hp;
829  	    __pmLogInDom		*idp;
830  	    int			numinst = 0;
831  	    int			strsize = 0;
832  	    int			*ilist = NULL;
833  	    char		**nlist = NULL;
834  	    char		**olist;
835  	
836  	    /* avoid ambiguity when no instances or errors */
837  	    *instlist = NULL;
838  	    *namelist = NULL;
839  	    if (indom == PM_INDOM_NULL)
840  		return PM_ERR_INDOM;
841  	
842  	    if ((n = pmWhichContext()) >= 0) {
843  		ctxp = __pmHandleToPtr(n);
844  		if (ctxp->c_type != PM_CONTEXT_ARCHIVE)
845  		    return PM_ERR_NOTARCHIVE;
846  	
847  		if ((hp = __pmHashSearch((unsigned int)indom, &ctxp->c_archctl->ac_log->l_hashindom)) == NULL)
848  		    return PM_ERR_INDOM_LOG;
849  	
850  		for (idp = (__pmLogInDom *)hp->data; idp != NULL; idp = idp->next) {
851  		    for (j = 0; j < idp->numinst; j++) {
852  			for (i = 0; i < numinst; i++) {
853  			    if (idp->instlist[j] == ilist[i])
854  				break;
855  			}
856  			if (i == numinst) {
857  			    numinst++;
858  			    if ((ilist = (int *)realloc(ilist, numinst*sizeof(ilist[0]))) == NULL) {
859  				__pmNoMem("pmGetInDomArchive: ilist", numinst*sizeof(ilist[0]), PM_FATAL_ERR);
860  			    }
861  			    if ((nlist = (char **)realloc(nlist, numinst*sizeof(nlist[0]))) == NULL) {
862  				__pmNoMem("pmGetInDomArchive: nlist", numinst*sizeof(nlist[0]), PM_FATAL_ERR);
863  			    }
864  			    ilist[numinst-1] = idp->instlist[j];
865  			    nlist[numinst-1] = idp->namelist[j];
866  			    strsize += strlen(idp->namelist[j])+1;
867  			}
868  		    }
869  		}
870  		if ((olist = (char **)malloc(numinst*sizeof(olist[0]) + strsize)) == NULL) {
871  		    __pmNoMem("pmGetInDomArchive: olist", numinst*sizeof(olist[0]) + strsize, PM_FATAL_ERR);
872  		}
873  		p = (char *)olist;
874  		p += numinst * sizeof(olist[0]);
875  		for (i = 0; i < numinst; i++) {
876  		    olist[i] = p;
877  		    strcpy(p, nlist[i]);
878  		    p += strlen(nlist[i]) + 1;
879  		}
880  		free(nlist);
881  		*instlist = ilist;
882  		*namelist = olist;
883  		n = numinst;
884  	    }
885  	
886  	    return n;
887  	}