1    	/*
2    	 * Copyright (c) 1995-2003 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 <ctype.h>
18   	
19   	static __pmLogPort *logport;
20   						/* array of all known pmlogger ports */
21   	static int	nlogports;		/* no. of elements used in logports array */
22   	static int	szlogport;		/* size of logport array */
23   	
24   	/* Make sure the logports array is large enough to hold newsize entries.  Free
25   	 * any currently allocated names and zero the first newsize entries.
26   	 */
27   	static int
28   	resize_logports(int newsize)
29   	{
30   	    int	i;
31   	    int	need;
32   	
33   	    if (nlogports) {
34   		for (i = 0; i < nlogports; i++) {
35   		    if (logport[i].pmcd_host != NULL)
36   			free(logport[i].pmcd_host);
37   		    if (logport[i].archive != NULL)
38   			free(logport[i].archive);
39   		    if (logport[i].name != NULL)
40   			free(logport[i].name);
41   		}
42   		memset(logport, 0, nlogports * sizeof(__pmLogPort));
43   	    }
44   	    nlogports = 0;
45   	    if (szlogport >= newsize)
46   		return 0;
47   	    free(logport);
48   	    need = newsize * (int)sizeof(__pmLogPort);
49   	    if ((logport = (__pmLogPort *)malloc(need)) == NULL) {
50   		szlogport = 0;
51   		return -1;
52   	    }
53   	    memset(logport, 0, need);
54   	    szlogport = newsize;
55   	    return 0;
56   	}
57   	
58   	/* Used by scandir to determine which files are pmlogger port files.  The valid
59   	 * files are numbers (pids) or PM_LOG_PRIMARY_LINK for the primary logger.
60   	 */
61   	static int
62   	is_portfile(const_dirent *dep)
63   	{
64   	    char	*endp;
65   	    pid_t	pid;
66   	
67   	    pid = (pid_t)strtol(dep->d_name, &endp, 10);
68   	    if (pid > (pid_t)1)
69   		return __pmProcessExists(pid);
70   	    return strcmp(dep->d_name, "primary") == 0;
71   	}
72   	
73   	/* The following function is used for selecting particular port files rather
74   	 * than all valid files.  snprintf the pid of the pmlogger process or the
75   	 * special constant PM_LOG_PRIMARY_LINK into the match array first.
76   	 */
77   	#define PROCFS_ENTRY_SIZE 40	/* encompass any size of entry for pid */
78   	static char match[PROCFS_ENTRY_SIZE];
79   	
80   	static int
81   	is_match(const_dirent *dep)
82   	{
83   	    return strcmp(match, dep->d_name) == 0;
84   	}
85   	
86   	/* Return (in result) a list of active pmlogger ports on the local machine.
87   	 * The return value of the function is the number of elements in the array.
88   	 * The caller must NOT free any part of the result stucture, it's storage is
89   	 * managed here.  Subsequent calls will overwrite the data so the caller should
90   	 * copy it if persistence is required.
91   	 */
92   	int
93   	__pmLogFindLocalPorts(int pid, __pmLogPort **result)
94   	{
95   	    static char		dir[MAXPATHLEN];
96   	    static int		lendir;
97   	    int			i, j, n;
98   	    int			nf;		/* number of port files found */
99   	    struct dirent	**files = NULL;	/* array of port file dirents */
100  	    char		*p;
101  	    int			len;
102  	    static char		*namebuf = NULL;
103  						/* for building file names */
104  	    static int		sznamebuf = 0;	/* current size of namebuf */
105  	    int			(*scanfn)(const_dirent *dep);
106  	    FILE		*pfile;
107  	    char		buf[MAXPATHLEN];
108  	
109  	    if (result == NULL)
110  		return -EINVAL;
111  	
112  	    if (lendir == 0)
113  		lendir = snprintf(dir, sizeof(dir), "%s%cpmlogger",
114  			pmGetConfig("PCP_TMP_DIR"), __pmPathSeparator());
115  	
116  	    /* Set up the appropriate function to select files from the control port
117  	     * directory.  Anticipate that this will usually be an exact match for
118  	     * the primary logger control port.
119  	     */
120  	    scanfn = is_match;
121  	    switch (pid) {
122  		case PM_LOG_PRIMARY_PID:	/* primary logger control (single) */
123  		    strcpy(match, "primary");
124  		    break;
125  	
126  		case PM_LOG_ALL_PIDS:		/* find all ports */
127  		    scanfn = is_portfile;
128  		    break;
129  	
130  		default:			/* a specific pid (single) */
131  		    if (!__pmProcessExists((pid_t)pid)) {
132  	#ifdef PCP_DEBUG
133  			if (pmDebug & DBG_TRACE_LOG) {
134  			    fprintf(stderr, "__pmLogFindLocalPorts() -> 0, "
135  					"pid(%d) doesn't exist\n", pid);
136  			}
137  	#endif
138  			*result = NULL;
139  			return 0;
140  		    }
141  		    snprintf(match, sizeof(match), "%d", pid);
142  		    break;
143  	    }
144  	
145  	    nf = scandir(dir, &files, scanfn, alphasort);
146  	    if (nf == -1 && oserror() == ENOENT)
147  		nf = 0;
148  	    else if (nf == -1) {
149  		pmprintf("__pmLogFindLocalPorts: scandir: %s\n", osstrerror());
150  		pmflush();
151  		return -oserror();
152  	    }
153  	    if (resize_logports(nf) < 0) {
154  		for (i=0; i < nf; i++)
155  		    free(files[i]);
156  		free(files);
157  		return -oserror();
158  	    }
159  	    if (nf == 0) {
160  	#ifdef PCP_DEBUG
161  		if (pmDebug & DBG_TRACE_LOG) {
162  		    fprintf(stderr, "__pmLogFindLocalPorts() -> 0, "
163  				"num files = 0\n");
164  		}
165  	#endif
166  		*result = NULL;
167  		free(files);
168  		return 0;
169  	    }
170  	
171  	    /* make a buffer for the longest complete pathname found */
172  	    len = (int)strlen(files[0]->d_name);
173  	    for (i = 1; i < nf; i++)
174  		if ((j = (int)strlen(files[i]->d_name)) > len)
175  		    len = j;
176  	    /* +1 for trailing path separator, +1 for null termination */
177  	    len += lendir + 2;
178  	    if (len > sznamebuf) {
179  		if (namebuf != NULL)
180  		    free(namebuf);
181  		if ((namebuf = (char *)malloc(len)) == NULL) {
182  		    __pmNoMem("__pmLogFindLocalPorts.namebuf", len, PM_RECOV_ERR);
183  		    for (i=0; i < nf; i++)
184  			free(files[i]);
185  		    free(files);
186  		    return -oserror();
187  		}
188  		sznamebuf = len;
189  	    }
190  	
191  	    /* namebuf is the complete pathname, p points to the trailing filename
192  	     * within namebuf.
193  	     */
194  	    strcpy(namebuf, dir);
195  	    p = namebuf + lendir;
196  	    *p++ = __pmPathSeparator();
197  	
198  	    /* open the file, try to read the port number and add the port to the
199  	     * logport array if successful.
200  	     */
201  	    for (i = 0; i < nf; i++) {
202  		char		*fname = files[i]->d_name;
203  		int		err = 0;
204  		__pmLogPort	*lpp = &logport[nlogports];
205  		
206  		strcpy(p, fname);
207  		if ((pfile = fopen(namebuf, "r")) == NULL) {
208  		    pmprintf("__pmLogFindLocalPorts: pmlogger port file %s: %s\n",
209  			    namebuf, osstrerror());
210  		    free(files[i]);
211  		    pmflush();
212  		    continue;
213  		}
214  		if (!err && fgets(buf, MAXPATHLEN, pfile) == NULL) {
215  		    if (feof(pfile)) {
216  			clearerr(pfile);
217  			pmprintf("__pmLogFindLocalPorts: pmlogger port file %s empty!\n",
218  				namebuf);
219  		    }
220  		    else
221  			pmprintf("__pmLogFindLocalPorts: pmlogger port file %s: %s\n",
222  				namebuf, osstrerror());
223  		    err = 1;
224  		}
225  		else {
226  		    char	*endp;
227  	
228  		    lpp->port = (int)strtol(buf, &endp, 10);
229  		    if (*endp != '\n') {
230  			pmprintf("__pmLogFindLocalPorts: pmlogger port file %s: no port number\n",
231  				namebuf);
232  			err = 1;
233  		    }
234  		    else {
235  			lpp->pid = (int)strtol(fname, &endp, 10);
236  			if (*endp != '\0') {
237  			    if (strcmp(fname, "primary") == 0)
238  				lpp->pid = PM_LOG_PRIMARY_PORT;
239  			    else {
240  				pmprintf("__pmLogFindLocalPorts: unrecognised pmlogger port file %s\n",
241  					namebuf);
242  				err = 1;
243  			    }
244  			}
245  		    }
246  		}
247  		if (err) {
248  		    pmflush();
249  		    fclose(pfile);
250  		}
251  		else {
252  		    if (fgets(buf, MAXPATHLEN, pfile) == NULL) {
253  			pmprintf("__pmLogFindLocalPorts: pmlogger port file %s: no PMCD host name\n",
254  				namebuf);
255  			pmflush();
256  		    }
257  		    else {
258  			char	*q = strchr(buf, '\n');
259  			if (q != NULL)
260  			    *q = '\0';
261  			lpp->pmcd_host = strdup(buf);
262  			if (fgets(buf, MAXPATHLEN, pfile) == NULL) {
263  			    pmprintf("__pmLogFindLocalPorts: pmlogger port file %s: no archive base pathname\n",
264  				    namebuf);
265  			    pmflush();
266  			}
267  			else {
268  			    char	*q = strchr(buf, '\n');
269  			    if (q != NULL)
270  				*q = '\0';
271  			    lpp->archive = strdup(buf);
272  			}
273  		    }
274  		    fclose(pfile);
275  		    if ((lpp->name = strdup(fname)) != NULL)
276  			nlogports++;
277  		    else {
278  			if (lpp->pmcd_host != NULL) {
279  			    free(lpp->pmcd_host);
280  			    lpp->pmcd_host = NULL;
281  			}
282  			if (lpp->archive != NULL) {
283  			    free(lpp->archive);
284  			    lpp->archive = NULL;
285  			}
286  			break;
287  		    }
288  		}
289  		free(files[i]);
290  	    }
291  	    
292  	    if (i == nf) {			/* all went well */
293  		n = nlogports;
294  		*result = logport;
295  	    }
296  	    else {				/* strdup error on fname, clean up */
297  		*result = NULL;
298  		for (j = i; j < nf; j++)
299  		    free(files[j]);
300  		n = -oserror();
301  	    }
302  	    free(files);
303  	    return n;
304  	}
305  	
306  	/*
307  	 * Return 1 if hostname corresponds to the current host, 0 if not and < 0 for
308  	 * an error.
309  	 */
310  	int
311  	__pmIsLocalhost(const char *hostname)
312  	{
313  	    int sts = 0;
314  	
315  	    if (strcasecmp(hostname, "localhost") == 0)
316  		return 1;
317  	    else {
318  		char lhost[MAXHOSTNAMELEN+1];
319  		struct hostent * he;
320  	
321  		if (gethostname(lhost, MAXHOSTNAMELEN) < 0)
322  		   return -oserror();
323  	
324  	        if ((he = gethostbyname(lhost)) != NULL ) {
325  		    int i;
326  		    unsigned int * laddrs;
327  		    for ( i=0; he->h_addr_list[i] != NULL; i++ ) ;
328  	
Event alloc_fn: Calling allocation function "calloc".
Event var_assign: Assigning: "laddrs" = storage returned from "calloc(i, sizeof (unsigned int) /*4*/)".
Also see events: [leaked_storage]
329  		    laddrs = (unsigned int *)calloc(i, sizeof (unsigned int));
At conditional (1): "laddrs != NULL": Taking true branch.
330  		    if ( laddrs != NULL ) {
331  			int k;
At conditional (2): "k < i": Taking true branch.
At conditional (3): "k < i": Taking true branch.
At conditional (4): "k < i": Taking false branch.
332  			for ( k=0; k < i; k++ ) {
333  			    laddrs[k] = ((struct in_addr *)he->h_addr_list[k])->s_addr;
334  			}
335  	
At conditional (5): "(he = gethostbyname(hostname)) == NULL": Taking true branch.
336  			if ((he = gethostbyname(hostname)) == NULL)
Event leaked_storage: Variable "laddrs" going out of scope leaks the storage it points to.
Also see events: [alloc_fn][var_assign]
337  			    return -EHOSTUNREACH;
338  	
339  			for ( i--; i >= 0; i-- ) {
340  			    for (k = 0; he->h_addr_list[k] != NULL; k++) {
341  				struct in_addr *s=(struct in_addr *)he->h_addr_list[k];
342  				if (s->s_addr == laddrs[i]) {
343  				    free (laddrs);
344  				    return (1);
345  				}
346  			    }
347  			}
348  	
349  			free (laddrs);
350  		    }
351  		}
352  	    }
353  	
354  	    return sts;
355  	}
356  	
357  	/* Return (in result) a list of active pmlogger ports on the specified machine.
358  	 * The return value of the function is the number of elements in the array.
359  	 * The caller must NOT free any part of the result stucture, it's storage is
360  	 * managed here.  Subsequent calls will overwrite the data so the caller should
361  	 * copy it if persistence is required.
362  	 */
363  	int
364  	__pmLogFindPort(const char *host, int pid, __pmLogPort **lpp)
365  	{
366  	    int			ctx, oldctx;
367  	    int			sts, numval;
368  	    int			i, j;
369  	    int			findone = pid != PM_LOG_ALL_PIDS;
370  	    int			localcon = 0;	/* > 0 for local connection */
371  	    pmDesc		desc;
372  	    pmResult		*res;
373  	    char		*namelist[] = {"pmcd.pmlogger.port"};
374  	    pmID		pmid;
375  	
376  	    *lpp = NULL;		/* pass null back in event of error */
377  	    localcon = __pmIsLocalhost(host);
378  	    if (localcon > 0)
379  		/* do the work here instead of making PMCD do it */
380  		return __pmLogFindLocalPorts(pid, lpp);
381  	    else if (localcon < 0)
382  		return localcon;
383  	
384  	    /* note: there may not be a current context */
385  	    oldctx = pmWhichContext();
386  	
387  	    if ((ctx = pmNewContext(PM_CONTEXT_HOST, host)) < 0)
388  		return ctx;
389  	    if ((sts = pmLookupName(1, namelist, &pmid)) < 0)
390  		goto ctxErr;
391  	
392  	    if ((sts = pmLookupDesc(pmid, &desc)) < 0)
393  		goto ctxErr;
394  	    if ((sts = pmFetch(1, &pmid, &res) < 0))
395  		goto ctxErr;
396  	    if ((sts = numval = res->vset[0]->numval) < 0)
397  		goto resErr;
398  	    j = 0;
399  	    if (numval) {
400  		if (resize_logports(findone ? 1 : numval) < 0) {
401  		    sts = -oserror();
402  		    goto resErr;
403  		}
404  		/* scan the pmResult, copying matching pid(s) to logport */
405  		for (i = j = 0; i < numval; i++) {
406  		    __pmLogPort	*p = &logport[j];
407  		    pmValue	*vp = &res->vset[0]->vlist[i];
408  	
409  		    if (vp->inst == 1)	/* old vcr instance (pseudo-init) */
410  			continue;
411  		    if (findone && vp->inst != pid)
412  			continue;
413  		    p->pid = vp->inst;
414  		    p->port = vp->value.lval;
415  		    sts = pmNameInDom(desc.indom, p->pid, &p->name);
416  		    if (sts < 0) {
417  			p->name = NULL;
418  			goto resErr;
419  		    }
420  		    j++;
421  		    if (findone)		/* found one, stop searching */
422  			break;
423  		}
424  		*lpp = logport;
425  	    }
426  	    sts = j;			/* the number actually added */
427  	
428  	resErr:
429  	    pmFreeResult(res);
430  	ctxErr:
431  	    if (oldctx >= 0)
432  		pmUseContext(oldctx);
433  	    pmDestroyContext(ctx);
434  	    return sts;
435  	}