1    	/*
2    	 * Copyright (c) 1995 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 <ctype.h>
16   	#include "pmapi.h"
17   	#include "impl.h"
18   	
19   	/*
20   	 * PDU for id list (PDU_PMNS_IDS)
21   	 */
22   	typedef struct {
23   	    __pmPDUHdr   hdr;
24   	    int		sts;      /* to encode status of pmns op */
25   	    int		numids;
26   	    pmID        idlist[1];
27   	} idlist_t;
28   	
29   	#ifdef PCP_DEBUG
30   	void
31   	__pmDumpIDList(FILE *f, int numids, const pmID idlist[])
32   	{
33   	    int i;
34   	
35   	    fprintf(f, "IDlist dump: numids = %d\n", numids);
36   	    for (i = 0; i < numids; i++)
37   		fprintf(f, "  PMID[%d]: 0x%08x %s\n", i, idlist[i], pmIDStr(idlist[i]));
38   	}
39   	#endif
40   	
41   	/*
42   	 * Send a PDU_PMNS_IDS across socket.
43   	 */
44   	int
45   	__pmSendIDList(int fd, int from, int numids, const pmID idlist[], int sts)
46   	{
47   	    idlist_t	*ip;
48   	    int		need;
49   	    int		j;
50   	
51   	#ifdef PCP_DEBUG
52   	    if (pmDebug & DBG_TRACE_PMNS) {
53   	        fprintf(stderr, "__pmSendIDList\n");
54   		__pmDumpIDList(stderr, numids, idlist);
55   	    }
56   	#endif
57   	
58   	    need = (int)(sizeof(idlist_t) + (numids-1) * sizeof(idlist[0]));
59   	
60   	    if ((ip = (idlist_t *)__pmFindPDUBuf(need)) == NULL)
61   		return -oserror();
62   	    ip->hdr.len = need;
63   	    ip->hdr.type = PDU_PMNS_IDS;
64   	    ip->hdr.from = from;
65   	    ip->sts = htonl(sts);
66   	    ip->numids = htonl(numids);
67   	    for (j = 0; j < numids; j++) {
68   		ip->idlist[j] = __htonpmID(idlist[j]);
69   	    }
70   	
71   	    return __pmXmitPDU(fd, (__pmPDU *)ip);
72   	}
73   	
74   	/*
75   	 * Decode a PDU_PMNS_IDS
76   	 * Assumes that we have preallocated idlist prior to this call
77   	 * (i.e. we know how many should definitely be coming over)
78   	 * Returns 0 on success.
79   	 */
80   	int
81   	__pmDecodeIDList(__pmPDU *pdubuf, int numids, pmID idlist[], int *sts)
82   	{
83   	    idlist_t	*idlist_pdu;
84   	    int		j;
85   	
86   	    idlist_pdu = (idlist_t *)pdubuf;
87   	
88   	    *sts = ntohl(idlist_pdu->sts);
89   	    for (j = 0; j < numids; j++) {
90   		idlist[j] = __ntohpmID(idlist_pdu->idlist[j]);
91   	    }
92   	
93   	#ifdef PCP_DEBUG
94   	    if (pmDebug & DBG_TRACE_PMNS) {
95   	        fprintf(stderr, "__pmDecodeIDList\n");
96   		__pmDumpIDList(stderr, numids, idlist);
97   	    }
98   	#endif
99   	
100  	    return 0;
101  	}
102  	
103  	/*********************************************************************/
104  	
105  	/*
106  	 * PDU for name list (PDU_PMNS_NAMES)
107  	 */
108  	
109  	typedef struct {
110  	    int namelen;
111  	    char name[sizeof(__pmPDU)]; /* variable length */
112  	} name_t;
113  	
114  	typedef struct {
115  	    int status;
116  	    int namelen;
117  	    char name[sizeof(__pmPDU)]; /* variable length */
118  	} name_status_t;
119  	
120  	typedef struct {
121  	    __pmPDUHdr	hdr;
122  	    int		nstrbytes; /* number of str bytes including null terminators */
123  	    int 	numstatus; /* = 0 if there is no status to be encoded */
124  	    int		numnames;
125  	    __pmPDU	names[1]; /* list of variable length name_t or name_status_t */
126  	} namelist_t;
127  	
128  	/*
129  	 * NOTES:
130  	 *
131  	 * 1.
132  	 * name_t's are padded to a __pmPDU boundary (if needed)
133  	 * so that direct accesses of a following record
134  	 * can be made on a word boundary.
135  	 * i.e. the following "namelen" field will be accessible.
136  	 *
137  	 * 2.
138  	 * Names are sent length prefixed as opposed to null terminated.
139  	 * This can make copying at the decoding end simpler 
140  	 * (and possibly more efficient using memcpy).
141  	 *
142  	 * 3.
143  	 * nstrbytes is used by the decoding function to know how many
144  	 * bytes to allocate.
145  	 *
146  	 * 4.
147  	 * name_status_t was added for pmGetChildrenStatus.
148  	 * It is a variant of the names pdu which encompasses status
149  	 * data as well.
150  	 */ 
151  	
152  	#ifdef PCP_DEBUG
153  	void
154  	__pmDumpNameList(FILE *f, int numnames, char *namelist[])
155  	{
156  	    int i;
157  	
158  	    fprintf(f, "namelist dump: numnames = %d\n", numnames);
159  	    for (i = 0; i < numnames; i++)
160  		fprintf(f, "  name[%d]: \"%s\"\n", i, namelist[i]);
161  	}
162  	
163  	void
164  	__pmDumpStatusList(FILE *f, int numstatus, const int statuslist[])
165  	{
166  	    int i;
167  	
168  	    fprintf(f, "statuslist dump: numstatus = %d\n", numstatus);
169  	    for (i = 0; i < numstatus; i++)
170  		fprintf(f, "  status[%d]: %d\n", i, statuslist[i]);
171  	}
172  	
173  	void
174  	__pmDumpNameAndStatusList(FILE *f, int numnames, char *namelist[], int statuslist[])
175  	{
176  	    int i;
177  	
178  	    fprintf(f, "namelist & statuslist dump: numnames = %d\n", numnames);
179  	    for (i = 0; i < numnames; i++)
180  		fprintf(f, "  name[%d]: \"%s\" (%s)\n", i, namelist[i],
181  			statuslist[i] == PMNS_LEAF_STATUS ? "leaf" : "non-leaf");
182  	}
183  	#endif
184  	
185  	/*
186  	 * Send a PDU_PMNS_NAMES across socket.
187  	 */
188  	int
189  	__pmSendNameList(int fd, int from, int numnames, char *namelist[],
190  			 const int statuslist[])
191  	{
192  	    namelist_t	*nlistp;
193  	    int		need;
194  	    int 	nstrbytes=0;
195  	    int 	i;
196  	    name_t	*nt; 
197  	    name_status_t *nst; 
198  	
199  	#ifdef PCP_DEBUG
200  	    if (pmDebug & DBG_TRACE_PMNS) {
201  	        fprintf(stderr, "__pmSendNameList\n");
202  		__pmDumpNameList(stderr, numnames, namelist);
203  	        if (statuslist != NULL)
204  		    __pmDumpStatusList(stderr, numnames, statuslist);
205  	    }
206  	#endif
207  	
208  	    /* namelist_t + names rounded up to a __pmPDU boundary */
209  	    need = sizeof(*nlistp) - sizeof(nlistp->names);
210  	    for (i = 0; i < numnames; i++) {
Event deref_ptr: Directly dereferencing pointer "namelist".
Also see events: [check_after_deref]
211  		int len = (int)strlen(namelist[i]);
212  	        nstrbytes += len+1;
Event check_after_deref: Dereferencing "namelist" before a null check.
Also see events: [deref_ptr]
213  	        if (namelist != NULL)
214  		    need += PM_PDU_SIZE_BYTES(len);
215  		if (statuslist == NULL) 
216  	            need += sizeof(*nt) - sizeof(nt->name);
217  		else 
218  	            need += sizeof(*nst) - sizeof(nst->name);
219  	    }
220  	
221  	    if ((nlistp = (namelist_t *)__pmFindPDUBuf(need)) == NULL)
222  		return -oserror();
223  	    nlistp->hdr.len = need;
224  	    nlistp->hdr.type = PDU_PMNS_NAMES;
225  	    nlistp->hdr.from = from;
226  	    nlistp->nstrbytes = htonl(nstrbytes);
227  	    nlistp->numnames = htonl(numnames);
228  	
229  	    if (statuslist == NULL) {
230  	        int i, j = 0, namelen;
231  	        nlistp->numstatus = htonl(0);
232  		for(i=0; i<numnames; i++) {
233  		    nt = (name_t*)&nlistp->names[j/sizeof(__pmPDU)];
234  		    namelen = (int)strlen(namelist[i]);
235  		    memcpy(nt->name, namelist[i], namelen);
236  	#ifdef PCP_DEBUG
237  		    if ((namelen % sizeof(__pmPDU)) != 0) {
238  			/* for Purify */
239  			int	pad;
240  			char	*padp = nt->name + namelen;
241  			for (pad = sizeof(__pmPDU) - 1; pad >= (namelen % sizeof(__pmPDU)); pad--)
242  			    *padp++ = '~';	/* buffer end */
243  		    }
244  	#endif
245  		    j += sizeof(namelen) + PM_PDU_SIZE_BYTES(namelen);
246  		    nt->namelen = htonl(namelen);
247  		}
248  	    }
249  	    else { /* include the status fields */
250  	        int i, j = 0, namelen;
251  	        nlistp->numstatus = htonl(numnames);
252  		for(i=0; i<numnames; i++) {
253  		    nst = (name_status_t*)&nlistp->names[j/sizeof(__pmPDU)];
254  		    nst->status = htonl(statuslist[i]);
255  		    namelen = (int)strlen(namelist[i]);
256  		    memcpy(nst->name, namelist[i], namelen);
257  	#ifdef PCP_DEBUG
258  		    if ((namelen % sizeof(__pmPDU)) != 0) {
259  			/* for Purify */
260  			int	pad;
261  			char	*padp = nst->name + namelen;
262  			for (pad = sizeof(__pmPDU) - 1; pad >= (namelen % sizeof(__pmPDU)); pad--)
263  			    *padp++ = '~';	/* buffer end */
264  		    }
265  	#endif
266  		    j += sizeof(nst->status) + sizeof(namelen) +
267  		         PM_PDU_SIZE_BYTES(namelen);
268  		    nst->namelen = htonl(namelen);
269  		}
270  	    }
271  	
272  	    return __pmXmitPDU(fd, (__pmPDU *)nlistp);
273  	}
274  	
275  	/*
276  	 * Decode a PDU_PMNS_NAMES
277  	 */
278  	int
279  	__pmDecodeNameList(__pmPDU *pdubuf, int *numnames, 
280  	                  char*** namelist, int** statuslist)
281  	{
282  	    namelist_t	*namelist_pdu;
283  	    char        **names;
284  	    int		*status = NULL;
285  	    int 	need;
286  	    int		numstatus;
287  	
288  	    namelist_pdu = (namelist_t *)pdubuf;
289  	
290  	    *namelist = NULL;
291  	    if (statuslist)
292  		*statuslist = NULL;
293  	
294  	    *numnames = ntohl(namelist_pdu->numnames);
295  	    numstatus = ntohl(namelist_pdu->numstatus);
296  	
297  	    if (*numnames == 0)
298  		return 0;
299  	
300  	    /* need space for name ptrs and the name characters */
301  	    need = *numnames * ((int)sizeof(char*)) + ntohl(namelist_pdu->nstrbytes);
302  	    if ((names = (char**)malloc(need)) == NULL)
303  		return -oserror();
304  	
305  	    /* need space for status values */
306  	    if (numstatus > 0) {
307  		need = numstatus * (int)sizeof(int);
308  		if ((status = (int*)malloc(need)) == NULL) {
309  		    free(names);
310  		    return -oserror();
311  		}
312  	    }
313  	
314  	    /* copy over ptrs and characters */
315  	    if (numstatus == 0) {
316  		int	i, j = 0;
317  		char	*dest = (char*)&names[*numnames];
318  		name_t	*np;
319  		int	namelen;
320  	
321  	        for (i = 0; i < *numnames; i++) {
322  		    np = (name_t*)&namelist_pdu->names[j/sizeof(__pmPDU)];
323  		    names[i] = dest;
324  		    namelen = ntohl(np->namelen);
325  	
326  		    memcpy(dest, np->name, namelen);
327  		    *(dest + namelen) = '\0';
328  		    dest += namelen + 1; 
329  	
330  		    j += sizeof(namelen) + PM_PDU_SIZE_BYTES(namelen);
331  		}
332  	    }
333  	    else { /* include the status fields */
334  		int		i, j = 0;
335  	        char		*dest = (char*)&names[*numnames];
336  		name_status_t	*np;
337  		int		namelen;
338  	
339  		for (i = 0; i < *numnames; i++) {
340  		    np = (name_status_t*)&namelist_pdu->names[j/sizeof(__pmPDU)];
341  		    names[i] = dest;
342  		    namelen = ntohl(np->namelen);
343  		    status[i] = ntohl(np->status);
344  	
345  		    memcpy(dest, np->name, namelen);
346  		    *(dest + namelen) = '\0';
347  		    dest += namelen + 1; 
348  	
349  		    j += sizeof(np->status) + sizeof(namelen) + PM_PDU_SIZE_BYTES(namelen);
350  		}
351  	    }
352  	
353  	#ifdef PCP_DEBUG
354  	    if (pmDebug & DBG_TRACE_PMNS) {
355  		fprintf(stderr, "__pmDecodeNameList\n");
356  		__pmDumpNameList(stderr, *numnames, names);
357  		if (status != NULL)
358  		    __pmDumpStatusList(stderr, numstatus, status);
359  	    }
360  	#endif
361  	
362  	    *namelist = names;
363  	    if (status != NULL)
364  		*statuslist = status;
365  	    return *numnames;
366  	}
367  	
368  	
369  	/*********************************************************************/
370  	
371  	/*
372  	 * name request 
373  	 */
374  	
375  	typedef struct {
376  	    __pmPDUHdr	hdr;
377  	    int		subtype; 
378  	    int		namelen;
379  	    char	name[sizeof(int)];
380  	} namereq_t;
381  	
382  	/*
383  	 * Send a PDU_PMNS_CHILD_REQ across socket.
384  	 */
385  	static int
386  	SendNameReq(int fd, int from, const char *name, int pdu_type, int subtype)
387  	{
388  	    namereq_t	*nreq;
389  	    int		need;
390  	    int		namelen;
391  	    int		alloc_len; /* length allocated for name */
392  	
393  	#ifdef PCP_DEBUG
394  	    if (pmDebug & DBG_TRACE_PMNS)
395  		fprintf(stderr, "SendNameReq: from=%d name=\"%s\" pdu=%s subtype=%d\n",
396  			from, name, __pmPDUTypeStr(pdu_type), subtype);
397  	#endif
398  	
399  	    namelen = (int)strlen(name);
400  	    alloc_len = (int)(sizeof(int)*((namelen-1 + sizeof(int))/sizeof(int)));
401  	    need = (int)(sizeof(*nreq) - sizeof(nreq->name) + alloc_len);
402  	
403  	    if ((nreq = (namereq_t *)__pmFindPDUBuf(need)) == NULL)
404  		return -oserror();
405  	    nreq->hdr.len = need;
406  	    nreq->hdr.type = pdu_type;
407  	    nreq->hdr.from = from;
408  	    nreq->subtype = htonl(subtype);
409  	    nreq->namelen = htonl(namelen);
410  	    memcpy(&nreq->name[0], name, namelen);
411  	
412  	    return __pmXmitPDU(fd, (__pmPDU *)nreq);
413  	}
414  	
415  	/*
416  	 * Decode a name request
417  	 */
418  	static int
419  	DecodeNameReq(__pmPDU *pdubuf, char **name_p, int *subtype)
420  	{
421  	    namereq_t	*namereq_pdu;
422  	    char	*name;
423  	    int		namelen;
424  	
425  	    namereq_pdu = (namereq_t *)pdubuf;
426  	
427  	    /* only set it if you want it */
428  	    if (subtype != NULL)
429  		*subtype = ntohl(namereq_pdu->subtype);
430  	
431  	    namelen = ntohl(namereq_pdu->namelen);
432  	    name = malloc(namelen+1);
433  	    if (name == NULL)
434  		return -oserror(); 
435  	    memcpy(name, namereq_pdu->name, namelen);
436  	    name[namelen] = '\0';
437  	
438  	#ifdef PCP_DEBUG
439  	    if (pmDebug & DBG_TRACE_PMNS)
440  		fprintf(stderr, "DecodeNameReq: name=\"%s\"\n", name);
441  	#endif
442  	
443  	    *name_p = name;
444  	    return 0;
445  	}
446  	
447  	/*********************************************************************/
448  	
449  	/*
450  	 * Send a PDU_PMNS_CHILD
451  	 */
452  	int
453  	__pmSendChildReq(int fd, int from, const char *name, int subtype)
454  	{
455  	    return SendNameReq(fd, from, name, PDU_PMNS_CHILD, subtype);
456  	}
457  	
458  	
459  	/*
460  	 * Decode a PDU_PMNS_CHILD
461  	 */
462  	int
463  	__pmDecodeChildReq(__pmPDU *pdubuf, char **name_p, int *subtype)
464  	{
465  	    return DecodeNameReq(pdubuf, name_p, subtype);
466  	}
467  	
468  	/*********************************************************************/
469  	
470  	/*
471  	 * Send a PDU_PMNS_TRAVERSE
472  	 */
473  	int
474  	__pmSendTraversePMNSReq(int fd, int from, const char *name)
475  	{
476  	    return SendNameReq(fd, from, name, PDU_PMNS_TRAVERSE, 0);
477  	}
478  	
479  	
480  	/*
481  	 * Decode a PDU_PMNS_TRAVERSE
482  	 */
483  	int
484  	__pmDecodeTraversePMNSReq(__pmPDU *pdubuf, char **name_p)
485  	{
486  	    return DecodeNameReq(pdubuf, name_p, 0);
487  	}
488  	
489  	/*********************************************************************/