1 /*
2 * Copyright (c) 1995-2002 Silicon Graphics, Inc. All Rights Reserved.
3 * Copyright (c) 2007 Aconex. All Rights Reserved.
4 *
5 * This library is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published
7 * by the Free Software Foundation; either version 2.1 of the License, or
8 * (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
13 * License for more details.
14 */
15
16 /*
17 * Parse uniform metric and host specification syntax
18 */
19
20 #include <ctype.h>
21 #include "pmapi.h"
22 #include "impl.h"
23 #ifdef HAVE_STRINGS_H
24 #include <strings.h>
25 #endif
26
27 static void *
28 parseAlloc(const char *func, size_t need)
29 {
30 void *tmp;
31
32 if ((tmp = malloc(need)) == NULL)
33 __pmNoMem(func, need, PM_FATAL_ERR);
34 return tmp;
35 }
36
37 static void
38 parseError(const char *func, const char *spec, const char *point, char *msg, char **rslt)
39 {
40 int need;
41 const char *p;
42 char *q;
43
44 if (rslt == NULL)
45 return;
46
47 need = 2 * (int)strlen(spec) + 1 + 6 + (int)strlen(msg) + 2;
48 *rslt = q = (char *)parseAlloc(func, need);
49 for (p = spec; *p != '\0'; p++)
50 *q++ = *p;
51 *q++ = '\n';
52 for (p = spec; p != point; p++)
53 *q++ = isgraph((int)*p) ? ' ' : *p;
54 sprintf(q, "^ -- %s\n", msg); /* safe */
55 }
56
57 static void *
58 metricAlloc(size_t need)
59 {
60 return parseAlloc("pmParseMetricSpec", need);
61 }
62
63 static void
64 metricError(const char *spec, const char *point, char *msg, char **rslt)
65 {
66 parseError("pmParseMetricSpec", spec, point, msg, rslt);
67 }
68
69 int /* 0 -> ok, PM_ERR_GENERIC -> error */
70 pmParseMetricSpec(
71 const char *spec, /* parse this string */
72 int isarch, /* default source: 0 -> host, 1 -> archive */
73 char *source, /* name of default host or archive */
74 pmMetricSpec **rslt, /* result allocated and returned here */
75 char **errmsg) /* error message */
76 {
77 pmMetricSpec *msp = NULL;
78 const char *scan;
79 const char *mark;
80 const char *h_start = NULL; /* host name */
81 const char *h_end = NULL;
82 const char *a_start = NULL; /* archive name */
83 const char *a_end = NULL;
84 const char *m_start = NULL; /* metric name */
85 const char *m_end = NULL;
86 const char *i_start = NULL; /* instance names */
87 const char *i_end = NULL;
88 char *i_str = NULL; /* temporary instance names */
89 char *i_scan;
90 int ninst; /* number of instance names */
91 char *push;
92 const char *pull;
93 int length;
94 int i;
95 int inquote = 0; /* true if within quotes */
96
97 scan = spec;
98 while (isspace((int)*scan))
99 scan++;
100
101 /*
102 * Options here are ...
103 * [host:]metric[[instance list]]
104 * special case for PM_CONTEXT_LOCAL [@:]metric[[instance list]]
105 * [archive/]metric[[instance list]]
106 *
107 * Find end of metric name first ([ or end of string) then scan
108 * backwards for first ':' or '/'
109 */
110 mark = index(scan, (int)'[');
111 if (mark == NULL) mark = &scan[strlen(scan)-1];
112 while (mark >= scan) {
113 if (*mark == ':') {
114 h_start = scan;
115 h_end = mark-1;
116 while (h_end >= scan && isspace((int)*h_end)) h_end--;
117 if (h_end < h_start) {
118 metricError(spec, h_start, "host name expected", errmsg);
119 return PM_ERR_GENERIC;
120 }
121 h_end++;
122 scan = mark+1;
123 break;
124 }
125 else if (*mark == '/') {
126 a_start = scan;
127 a_end = mark-1;
128 while (a_end >= scan && isspace((int)*a_end)) a_end--;
129 if (a_end < a_start) {
130 metricError(spec, a_start, "archive name expected", errmsg);
131 return PM_ERR_GENERIC;
132 }
133 a_end++;
134 scan = mark+1;
135 break;
136 }
137 mark--;
138 }
139
140 while (isspace((int)*scan))
141 scan++;
142 mark = scan;
143
144 /* delimit metric name */
145 m_start = scan;
146 while (! isspace((int)*scan) && *scan != '\0' && *scan != '[') {
147 if (*scan == ']' || *scan == ',') {
148 metricError(spec, scan, "unexpected character in metric name", errmsg);
149 return PM_ERR_GENERIC;
150 }
151 if (*scan == '\\' && *(scan+1) != '\0')
152 scan++;
153 scan++;
154 }
155 m_end = scan;
156 if (m_start == m_end) {
157 metricError(spec, m_start, "performance metric name expected", errmsg);
158 return PM_ERR_GENERIC;
159 }
160
161 while (isspace((int)*scan))
162 scan++;
163
164 /* delimit instance names */
165 if (*scan == '[') {
166 scan++;
167 while (isspace((int)*scan))
168 scan++;
169 i_start = scan;
170 for ( ; ; ) {
171 if (*scan == '\0') {
172 if (inquote)
173 metricError(spec, scan, "closing \" and ] expected", errmsg);
174 else
175 metricError(spec, scan, "closing ] expected", errmsg);
176 return PM_ERR_GENERIC;
177 }
178 if (*scan == '\\' && *(scan+1) != '\0')
179 scan++;
180 else if (*scan == '"')
181 inquote = 1 - inquote;
182 else if (!inquote && *scan == ']')
183 break;
184 scan++;
185 }
186 i_end = scan;
187 scan++;
188 }
189
190 /* check for rubbish at end of string */
191 while (isspace((int)*scan))
192 scan++;
193 if (*scan != '\0') {
194 metricError(spec, scan, "unexpected extra characters", errmsg);
195 return PM_ERR_GENERIC;
196 }
197
198 /* count instance names and make temporary copy */
199 ninst = 0;
200 if (i_start != NULL) {
201 i_str = (char *) metricAlloc(i_end - i_start + 1);
202
203 /* count and copy instance names */
204 scan = i_start;
205 i_scan = i_str;
206 while (scan < i_end) {
207
208 /* copy single instance name */
209 ninst++;
210 if (*scan == '"') {
211 scan++;
212 for (;;) {
213 if (scan >= i_end) {
214 metricError(spec, scan, "closing \" expected (pmParseMetricSpec botch?)", errmsg);
215 if (msp)
216 pmFreeMetricSpec(msp);
217 if (i_str)
218 free(i_str);
219 return PM_ERR_GENERIC;
220 }
221 if (*scan == '\\')
222 scan++;
223 else if (*scan == '"')
224 break;
225 *i_scan++ = *scan++;
226 }
227 scan++;
228 }
229 else {
230 while (! isspace((int)*scan) && *scan != ',' && scan < i_end) {
231 if (*scan == '\\')
232 scan++;
233 *i_scan++ = *scan++;
234 }
235 }
236 *i_scan++ = '\0';
237
238 /* skip delimiters */
239 while ((isspace((int)*scan) || *scan == ',') && scan < i_end)
240 scan++;
241 }
242 i_start = i_str;
243 i_end = i_scan;
244 }
245
246 /* single memory allocation for result structure */
247 length = (int)(sizeof(pmMetricSpec) +
248 ((ninst > 1) ? (ninst - 1) * sizeof(char *) : 0) +
249 ((h_start) ? h_end - h_start + 1 : 0) +
250 ((a_start) ? a_end - a_start + 1 : 0) +
251 ((m_start) ? m_end - m_start + 1 : 0) +
252 ((i_start) ? i_end - i_start + 1 : 0));
253 msp = (pmMetricSpec *)metricAlloc(length);
254
255 /* strings follow pmMetricSpec proper */
256 push = ((char *) msp) +
257 sizeof(pmMetricSpec) +
258 ((ninst > 1) ? (ninst - 1) * sizeof(char *) : 0);
259
260 /* copy metric name */
261 msp->metric = push;
262 pull = m_start;
263 while (pull < m_end) {
264 if (*pull == '\\' && (pull+1) < m_end)
265 pull++;
266 *push++ = *pull++;
267 }
268 *push++ = '\0';
269
270 /* copy host name */
271 if (h_start != NULL) {
272 if (h_end - h_start == 1 && *h_start == '@') {
273 /* PM_CONTEXT_LOCAL special case */
274 msp->isarch = 2;
275 }
276 else {
277 /* PM_CONTEXT_HOST */
278 msp->isarch = 0;
279 }
280 msp->source = push;
281 pull = h_start;
282 while (pull < h_end) {
283 if (*pull == '\\' && (pull+1) < h_end)
284 pull++;
285 *push++ = *pull++;
286 }
287 *push++ = '\0';
288 }
289
290 /* copy archive name */
291 else if (a_start != NULL) {
292 msp->isarch = 1;
293 msp->source = push;
294 pull = a_start;
295 while (pull < a_end) {
296 if (*pull == '\\' && (pull+1) < a_end)
297 pull++;
298 *push++ = *pull++;
299 }
300 *push++ = '\0';
301 }
302
303 /* take default host or archive */
304 else {
305 msp->isarch = isarch;
306 msp->source = source;
307 }
308
309 /* instance names */
310 msp->ninst = ninst;
311 pull = i_start;
312 for (i = 0; i < ninst; i++) {
313 msp->inst[i] = push;
314 do
315 *push++ = *pull;
316 while (*pull++ != '\0');
317 }
318
319 if (i_str)
320 free(i_str);
321 *rslt = msp;
322 return 0;
323 }
324
325 void
326 pmFreeMetricSpec(pmMetricSpec *spec)
327 {
328 free(spec);
329 }
330
331
332 static void
333 hostError(const char *spec, const char *point, char *msg, char **rslt)
334 {
335 parseError("pmParseHostSpec", spec, point, msg, rslt);
336 }
337
338 static char *
339 hostStrndup(const char *name, int namelen)
340 {
|
Event alloc_fn: |
Storage is returned from allocation function "malloc". |
|
Event var_assign: |
Assigning: "s" = "malloc(namelen + 1)". |
| Also see events: |
[noescape][return_alloc] |
341 char *s = malloc(namelen + 1);
342 strncpy(s, name, namelen);
343 s[namelen] = '\0';
344 return s;
345 }
346
347 static pmHostSpec *
348 hostAdd(pmHostSpec *specp, int *count, const char *name, int namelen)
349 {
350 int n = *count;
351 char *host;
352
|
Event alloc_fn: |
Calling allocation function "hostStrndup". [details] |
|
Event var_assign: |
Assigning: "host" = storage returned from "hostStrndup(name, namelen)". |
| Also see events: |
[leaked_storage] |
353 host = hostStrndup(name, namelen);
|
At conditional (1): "!host": Taking false branch.
|
|
At conditional (2): "(specp = realloc(specp, sizeof (pmHostSpec) /*24*/ * (n + 1))) == NULL": Taking true branch.
|
354 if (!host || (specp = realloc(specp, sizeof(pmHostSpec) * (n+1))) == NULL) {
355 *count = 0;
|
Event leaked_storage: |
Variable "host" going out of scope leaks the storage it points to. |
| Also see events: |
[alloc_fn][var_assign] |
356 return NULL;
357 }
358 specp[n].name = host;
359 specp[n].ports = NULL;
360 specp[n].nports = 0;
361
362 *count = n + 1;
363 return specp;
364 }
365
366 int
367 __pmAddHostPorts(pmHostSpec *specp, int *ports, int nports)
368 {
369 int *portlist;
370
371 if ((portlist = malloc(sizeof(int) * (specp->nports + nports))) == NULL)
372 return -ENOMEM;
373 if (specp->nports > 0) {
374 memcpy(portlist, specp->ports, sizeof(int) * specp->nports);
375 free(specp->ports);
376 }
377 memcpy(&portlist[specp->nports], ports, sizeof(int) * nports);
378 specp->ports = portlist;
379 specp->nports = specp->nports + nports;
380 return 0;
381 }
382
383 void
384 __pmDropHostPort(pmHostSpec *specp)
385 {
386 specp->nports--;
387 memmove(&specp->ports[0], &specp->ports[1], specp->nports*sizeof(int));
388 }
389
390 /*
391 * Parse a host specification, with optional ports and proxy host(s).
392 * Examples:
393 * pcp -h app1.aconex.com:44321,4321@firewall.aconex.com:44322
394 * pcp -h app1.aconex.com:44321@firewall.aconex.com:44322
395 * pcp -h app1.aconex.com:44321@firewall.aconex.com
396 * pcp -h app1.aconex.com@firewall.aconex.com
397 * pcp -h app1.aconex.com:44321
398 *
399 * Basic algorithm:
400 * look for first colon, @ or null; preceding text is hostname
401 * if colon, look for comma, @ or null, preceding text is port
402 * while comma, look for comma, @ or null, preceding text is next port
403 * if @, start following host specification at the following character,
404 * by returning to the start and repeating the above for the next chunk.
405 * Note:
406 * Currently only two hosts are useful, but ability to handle more than
407 * one optional proxy host is there (i.e. proxy ->proxy ->... ->pmcd),
408 * in case someone implements the pmproxy->pmproxy protocol extension.
409 */
410
411 int /* 0 -> ok, PM_ERR_GENERIC -> error */
412 __pmParseHostSpec(
413 const char *spec, /* parse this string */
414 pmHostSpec **rslt, /* result allocated and returned here */
415 int *count,
416 char **errmsg) /* error message */
417 {
418 pmHostSpec *hsp = NULL;
419 const char *s, *start;
420 int nhosts = 0, sts = 0;
421
422 for (s = start = spec; s != NULL; s++) {
423 if (*s == ':' || *s == '@' || *s == '\0') {
424 if (s == start)
425 continue;
426 hsp = hostAdd(hsp, &nhosts, start, s - start);
427 if (hsp == NULL) {
428 sts = -ENOMEM;
429 goto fail;
430 }
431 if (*s == ':') {
432 for (++s, start = s; s != NULL; s++) {
433 if (*s == ',' || *s == '@' || *s == '\0') {
434 if (s - start < 1) {
435 hostError(spec, s, "missing port", errmsg);
436 sts = PM_ERR_GENERIC;
437 goto fail;
438 }
439 int port = atoi(start);
440 sts = __pmAddHostPorts(&hsp[nhosts-1], &port, 1);
441 if (sts < 0)
442 goto fail;
443 start = s + 1;
444 if (*s == '@' || *s == '\0')
445 break;
446 continue;
447 }
448 if (isdigit(*s))
449 continue;
450 hostError(spec, s, "non-numeric port", errmsg);
451 sts = PM_ERR_GENERIC;
452 goto fail;
453 }
454 }
455 if (*s == '@') {
456 start = s+1;
457 continue;
458 }
459 break;
460 }
461 }
462 *count = nhosts;
463 *rslt = hsp;
464 return 0;
465
466 fail:
467 __pmFreeHostSpec(hsp, nhosts);
468 *rslt = NULL;
469 *count = 0;
470 return sts;
471 }
472
473 void
474 __pmUnparseHostSpec(pmHostSpec *hostp, int count, char **specp, int specsz)
475 {
476 int i, j, sz = 0;
477
478 for (i = 0; i < count; i++) {
479 if (i > 0)
480 sz += snprintf(*specp, specsz, "@");
481 sz += snprintf(*specp, specsz, "%s", hostp[0].name);
482 for (j = 0; j < hostp[i].nports; j++)
483 sz += snprintf((*specp) + sz, specsz - sz,
484 "%c%u", (j == 0) ? ':' : ',', hostp[i].ports[j]);
485 }
486 }
487
488 void
489 __pmFreeHostSpec(pmHostSpec *specp, int count)
490 {
491 int i;
492
493 for (i = 0; i < count; i++) {
494 free(specp[i].name);
495 specp[i].name = NULL;
496 if (specp[i].nports > 0)
497 free(specp[i].ports);
498 specp[i].ports = NULL;
499 specp[i].nports = 0;
500 }
501 if (specp && count)
502 free(specp);
503 }