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 }