1 /*
2 * Copyright (c) 1995-2001 Silicon Graphics, Inc. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program 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 General Public License
12 * for more details.
13 */
14
15 #include "logger.h"
16
17
18 /* return one of these when a status request is made from a PCP 1.x pmlc */
19 typedef struct {
20 __pmTimeval ls_start; /* start time for log */
21 __pmTimeval ls_last; /* last time log written */
22 __pmTimeval ls_timenow; /* current time */
23 int ls_state; /* state of log (from __pmLogCtl) */
24 int ls_vol; /* current volume number of log */
25 __int64_t ls_size; /* size of current volume */
26 char ls_hostname[PM_LOG_MAXHOSTLEN];
27 /* name of pmcd host */
28 char ls_tz[40]; /* $TZ at collection host */
29 char ls_tzlogger[40]; /* $TZ at pmlogger */
30 } __pmLoggerStatus_v1;
31
32 #ifdef PCP_DEBUG
33 /* This crawls over the data structure looking for weirdness */
34 void
35 reality_check(void)
36 {
37 __pmHashNode *hp;
38 task_t *tp;
39 task_t *tp2;
40 fetchctl_t *fp;
41 optreq_t *rqp;
42 pmID pmid;
43 int i = 0, j, k;
44
45 /* check that all fetch_t's f_aux point back to their parent task */
46 for (tp = tasklist; tp != NULL; tp = tp->t_next, i++) {
47 if (tp->t_fetch == NULL)
48 fprintf(stderr, "task[%d] @" PRINTF_P_PFX "%p has no fetch group\n", i, tp);
49 j = 0;
50 for (fp = tp->t_fetch; fp != NULL; fp = fp->f_next) {
51 if (fp->f_aux != (void *)tp)
52 fprintf(stderr, "task[%d] fetch group[%d] has invalid task pointer\n",
53 i, j);
54 j++;
55 }
56
57 /* check that all optreq_t's in hash list have valid r_fetch->f_aux
58 * pointing to a task in the task list.
59 */
60 for (j = 0; j < tp->t_numpmid; j++) {
61 pmid = tp->t_pmidlist[j];
62 for (hp = __pmHashSearch(pmid, &pm_hash); hp != NULL; hp = hp->next) {
63 if (pmid != (pmID)hp->key)
64 continue;
65 rqp = (optreq_t *)hp->data;
66 for (tp2 = tasklist; tp2 != NULL; tp2 = tp2->t_next)
67 if (rqp->r_fetch->f_aux == (void *)tp2)
68 break;
69 if (tp2 == NULL) {
70 fprintf(stderr, "task[%d] pmid %s optreq " PRINTF_P_PFX "%p for [",
71 i, pmIDStr(pmid), rqp);
72 if (rqp->r_numinst == 0)
73 fputs("`all instances' ", stderr);
74 else
75 for (k = 0; k < rqp->r_numinst; k++)
76 fprintf(stderr, "%d ", rqp->r_instlist[k]);
77 fputs("] bad task pointer\n", stderr);
78 }
79 }
80 }
81 }
82
83
84 }
85
86 /* Call this in dbx to dump the task list (dbx doesn't know about stdout) */
87 void
88 dumpit(void)
89 {
90 int i;
91 task_t *tp;
92
93 reality_check();
94 for (tp = tasklist, i = 0; tp != NULL; tp = tp->t_next, i++) {
95
96 fprintf(stderr,
97 "\ntask[%d] @" PRINTF_P_PFX "%p: %s %s \ndelta = %f\n", i, tp,
98 PMLC_GET_MAND(tp->t_state) ? "mandatory " : "advisory ",
99 PMLC_GET_ON(tp->t_state) ? "on " : "off ",
100 tp->t_delta.tv_sec + (float)tp->t_delta.tv_usec / 1.0e6);
101 __pmOptFetchDump(stderr, tp->t_fetch);
102 }
103 }
104
105 /*
106 * stolen from __pmDumpResult
107 */
108 static void
109 dumpcontrol(FILE *f, const pmResult *resp, int dovalue)
110 {
111 int i;
112 int j;
113
114 fprintf(f,"LogControl dump from " PRINTF_P_PFX "%p", resp);
115 fprintf(f, " numpmid: %d\n", resp->numpmid);
116 for (i = 0; i < resp->numpmid; i++) {
117 pmValueSet *vsp = resp->vset[i];
118 fprintf(f," %s :", pmIDStr(vsp->pmid));
119 if (vsp->numval == 0) {
120 fprintf(f, " No values!\n");
121 continue;
122 }
123 else if (vsp->numval < 0) {
124 fprintf(f, " %s\n", pmErrStr(vsp->numval));
125 continue;
126 }
127 fprintf(f, " numval: %d", vsp->numval);
128 fprintf(f, " valfmt: %d", vsp->valfmt);
129 for (j = 0; j < vsp->numval; j++) {
130 pmValue *vp = &vsp->vlist[j];
131 if (vsp->numval > 1 || vp->inst != PM_INDOM_NULL) {
132 fprintf(f," inst [%d]", vp->inst);
133 }
134 else
135 fprintf(f, " singular");
136 if (dovalue) {
137 fprintf(f, " value ");
138 pmPrintValue(f, vsp->valfmt, PM_TYPE_U32, vp, 1);
139 }
140 fputc('\n', f);
141 }
142 }
143 }
144
145 #endif
146
147 /* Called when optFetch or _pmHash routines fail. This is terminal. */
148 void
149 die(char *name, int sts)
150 {
151 __pmNotifyErr(LOG_ERR, "%s error unrecoverable: %s\n", name, pmErrStr(sts));
152 exit(1);
153 }
154
155 static void
156 linkback(task_t *tp)
157 {
158 fetchctl_t *fcp;
159
160 /* link the new fetch groups back to their task */
161 for (fcp = tp->t_fetch; fcp != NULL; fcp = fcp->f_next)
162 fcp->f_aux = (void *)tp;
163 }
164
165 optreq_t *
166 findoptreq(pmID pmid, int inst)
167 {
168 __pmHashNode *hp;
169 optreq_t *rqp;
170 optreq_t *all_rqp = NULL;
171 int j;
172
173 /*
174 * Note:
175 * The logic here assumes that for each metric-inst pair, there is
176 * at most one optreq_t structure, corresponding to the logging
177 * state of ON (mandatory or advisory) else OFF (mandatory). Other
178 * requests change the data structures, but do not leave optreq_t
179 * structures lying about, i.e. MAYBE (mandatory) is the default,
180 * and does not have to be explicitly stored, while OFF (advisory)
181 * reverts to MAYBE (mandatory).
182 * There is one execption to the above assumption, namely for
183 * cases where the initial specification includes "all" instances,
184 * then some later concurrent specification may refer to specific
185 * instances ... in this case, the specific optreq_t structure is
186 * the one that applies.
187 */
188
189 for (hp = __pmHashSearch(pmid, &pm_hash); hp != NULL; hp = hp->next) {
190 if (pmid != (pmID)hp->key)
191 continue;
192 rqp = (optreq_t *)hp->data;
193 if (rqp->r_numinst == 0) {
194 all_rqp = rqp;
195 continue;
196 }
197 for (j = 0; j < rqp->r_numinst; j++)
198 if (inst == rqp->r_instlist[j])
199 return rqp;
200 }
201
202 if (all_rqp != NULL)
203 return all_rqp;
204 else
205 return NULL;
206 }
207
208 /* Determine whether a metric is currently known. Returns
209 * -1 if metric not known
210 * inclusive OR of the flags below if it is known
211 */
212 #define MF_HAS_INDOM 0x1 /* has an instance domain */
213 #define MF_HAS_ALL 0x2 /* has an "all instances" */
214 #define MF_HAS_INST 0x4 /* has specific instance(s) */
215 #define MF_HAS_MAND 0x8 /* has at least one inst with mandatory */
216 /* logging (or is mandatory if no indom) */
217 static int
218 find_metric(pmID pmid)
219 {
220 __pmHashNode *hp;
221 optreq_t *rqp;
222 int result = 0;
223 int found = 0;
224
225 for (hp = __pmHashSearch(pmid, &pm_hash); hp != NULL; hp = hp->next) {
226 if (pmid != (pmID)hp->key)
227 continue;
228 rqp = (optreq_t *)hp->data;
229 if (found++ == 0)
230 if (rqp->r_desc->indom != PM_INDOM_NULL) {
231 result |= MF_HAS_INDOM;
232 if (rqp->r_numinst == 0)
233 result |= MF_HAS_ALL;
234 else
235 result |= MF_HAS_INST;
236 }
237 if (PMLC_GET_MAND(((task_t *)(rqp->r_fetch->f_aux))->t_state))
238 result |= MF_HAS_MAND;
239 }
240 return found ? result : -1;
241 }
242
243 /* Find an optreq_t suitable for adding a new instance */
244
245 /* Add a new metric (given a pmValueSet and a pmDesc) to the specified task.
246 * Allocate and return a new task_t if the specified task pointer is nil.
247 *
248 * Note that this should only be called for metrics not currently in the
249 * logging data structure. All instances in the pmValueSet are added!
250 */
251 static int
252 add_metric(pmValueSet *vsp, task_t **result)
253 {
254 pmID pmid = vsp->pmid;
255 task_t *tp = *result;
256 optreq_t *rqp;
257 pmDesc *dp;
258 char *name;
259 int sts, i, need = 0;
260
261 dp = (pmDesc *)malloc(sizeof(pmDesc));
262 if (dp == NULL) {
263 __pmNoMem("add_metric: new pmDesc malloc", sizeof(pmDesc), PM_FATAL_ERR);
264 }
265 if ((sts = pmLookupDesc(pmid, dp)) < 0)
266 die("add_metric: lookup desc", sts);
267 if ((sts = pmNameID(pmid, &name)) < 0)
268 die("add_metric: lookup name", sts);
269
270 /* allocate a new task if null task pointer passed in */
271 if (tp == NULL) {
272 tp = calloc(1, sizeof(task_t));
273 if (tp == NULL) {
274 __pmNoMem("add_metric: new task calloc", sizeof(task_t), PM_FATAL_ERR);
275 }
276 *result = tp;
277 }
278
279 /* add metric (and any instances specified) to task */
280 i = tp->t_numpmid++;
281 need = tp->t_numpmid * sizeof(pmID);
282 if (!(tp->t_pmidlist = (pmID *)realloc(tp->t_pmidlist, need)))
283 __pmNoMem("add_metric: new task pmidlist realloc", need, PM_FATAL_ERR);
284 need = tp->t_numpmid * sizeof(char *);
285 if (!(tp->t_namelist = (char **)realloc(tp->t_namelist, need)))
286 __pmNoMem("add_metric: new task namelist realloc", need, PM_FATAL_ERR);
287 need = tp->t_numpmid * sizeof(pmDesc);
288 if (!(tp->t_desclist = (pmDesc *)realloc(tp->t_desclist, need)))
289 __pmNoMem("add_metric: new task desclist realloc", need, PM_FATAL_ERR);
290 tp->t_pmidlist[i] = pmid;
291 tp->t_namelist[i] = name;
292 tp->t_desclist[i] = *dp; /* struct assignment */
293
294 rqp = (optreq_t *)calloc(1, sizeof(optreq_t));
295 if (rqp == NULL) {
296 __pmNoMem("add_metric: new task optreq calloc", need, PM_FATAL_ERR);
297 }
298 rqp->r_desc = dp;
299
300 /* Now copy instances if required. Remember that metrics with singular
301 * values actually have one instance specified to distinguish them from the
302 * "all instances" case (which has no instances). Use the pmDesc to check
303 * for this.
304 */
305 if (dp->indom != PM_INDOM_NULL)
306 need = rqp->r_numinst = vsp->numval;
307 if (need) {
308 need *= sizeof(rqp->r_instlist[0]);
309 rqp->r_instlist = (int *)malloc(need);
310 if (rqp->r_instlist == NULL) {
311 __pmNoMem("add_metric: new task optreq instlist malloc", need,
312 PM_FATAL_ERR);
313 }
314 for (i = 0; i < vsp->numval; i++)
315 rqp->r_instlist[i] = vsp->vlist[i].inst;
316 }
317
318 /* Add new metric to task's fetchgroup(s) and global hash table */
319 if ((sts = __pmOptFetchAdd(&tp->t_fetch, rqp)) < 0)
320 die("add_metric: __pmOptFetchAdd", sts);
321 linkback(tp);
322 if ((sts = __pmHashAdd(pmid, (void *)rqp, &pm_hash)) < 0)
323 die("add_metric: __pmHashAdd", sts);
324 return 0;
325 }
326
327 /* Return true if a request for a new logging state (newstate) will be honoured
328 * when current state is curstate.
329 */
330 static int
331 update_ok(int curstate, int newstate)
332 {
333 /* If new state is advisory and current is mandatory, reject request.
334 * Any new mandatory state is accepted. If the new state is advisory
335 * and the current state is advisory, it is accepted.
336 * Note that a new state of maybe (mandatory maybe) counts as mandatory
337 */
338 if (PMLC_GET_MAND(newstate) == 0 && PMLC_GET_MAYBE(newstate) == 0 &&
339 PMLC_GET_MAND(curstate))
340 return 0;
341 else
342 return 1;
343 }
344
345 /* Given a task and a pmID, find an optreq_t associated with the task suitable
346 * for inserting a new instance into.
347 * The one with the smallest number of instances is chosen. We could also
348 * have just used the first, but smallest-first gives a more even distribution.
349 */
350 static optreq_t *
351 find_instoptreq(task_t *tp, pmID pmid)
352 {
353 optreq_t *result = NULL;
354 optreq_t *rqp;
355 int ni = 0;
356 __pmHashNode *hp;
357
358 for (hp = __pmHashSearch(pmid, &pm_hash); hp != NULL;
359 hp = hp->next) {
360 if (pmid != (pmID)hp->key)
361 continue;
362 rqp = (optreq_t *)hp->data;
363 if ((task_t *)rqp->r_fetch->f_aux != tp)
364 continue;
365 if (rqp->r_numinst == 0)
366 continue; /* don't want "all instances" cases */
367 if (ni == 0 || rqp->r_numinst < ni) {
368 result = rqp;
369 ni = rqp->r_numinst;
370 }
371 }
372 return result;
373 }
374
375 /* Delete an optreq_t from its task, free it and remove it from the hash list.
376 */
377 static void
378 del_optreq(optreq_t *rqp)
379 {
380 int sts;
381 task_t *tp = (task_t *)rqp->r_fetch->f_aux;
382
383 if ((sts = __pmOptFetchDel(&tp->t_fetch, rqp)) < 0)
384 die("del_optreq: __pmOptFetchDel", sts);
385 if ((sts = __pmHashDel(rqp->r_desc->pmid, (void *)rqp, &pm_hash)) < 0)
386 die("del_optreq: __pmHashDel", sts);
387 free(rqp->r_desc);
388 if (rqp->r_numinst)
389 free(rqp->r_instlist);
390 free(rqp);
391 /* TODO: remove pmid from task if that was the last optreq_t for it */
392 /* TODO: remove task if last pmid removed */
393 }
394
395 /* Delete every instance of a given metric from the data structure.
396 * The pmid is deleted from the pmidlist of every task containing an instance.
397 * Return a pointer to the first pmDesc found (the only thing salvaged from the
398 * smoking ruins), or nil if no instances were found.
399 */
400 static pmDesc *
401 del_insts(pmID pmid)
402 {
403 optreq_t *rqp;
404 __pmHashNode *hp;
405 task_t *tp;
406 pmDesc *dp = NULL;
407 int i, sts, keep;
408
409 for (hp = __pmHashSearch(pmid, &pm_hash); hp != NULL; ) {
410 /* Do that BEFORE we nuke the node */
411 __pmHashNode * nextnode = hp->next;
412
413 if (pmid == (pmID)hp->key) {
414 rqp = (optreq_t *)hp->data;
415 tp = (task_t *)rqp->r_fetch->f_aux;
416 if ((sts = __pmOptFetchDel(&tp->t_fetch, rqp)) < 0)
417 die("del_insts: __pmOptFetchDel", sts);
418 if ((sts = __pmHashDel(pmid, (void *)rqp, &pm_hash)) < 0)
419 die("del_insts: __pmHashDel", sts);
420
421 /* save the first pmDesc pointer for return and subsequent
422 * re-use, but free all the others
423 */
424 if (dp != NULL)
425 free(rqp->r_desc);
426 else
427 dp = rqp->r_desc;
428
429 if (rqp->r_numinst)
430 free(rqp->r_instlist);
431 free(rqp);
432
433 /* remove pmid from the task's pmid list */
434 for (i = 0; i < tp->t_numpmid; i++)
435 if (tp->t_pmidlist[i] == pmid)
436 break;
437 keep = (tp->t_numpmid - 1 - i) * sizeof(tp->t_pmidlist[0]);
438 if (keep) {
439 memmove(&tp->t_pmidlist[i], &tp->t_pmidlist[i+1], keep);
440 memmove(&tp->t_desclist[i], &tp->t_desclist[i+1], keep);
441 memmove(&tp->t_namelist[i], &tp->t_namelist[i+1], keep);
442 }
443
444 /* don't bother shrinking the pmidlist */
445 tp->t_numpmid--;
446 if (tp->t_numpmid == 0) {
447 /* TODO: nuke the task if that was the last pmID */
448 }
449 }
450 hp = nextnode;
451 }
452
453 return dp;
454 }
455
456 /* Update an existing metric (given a pmValueSet) adding it to the specified
457 * task. Allocate and return a new task_t if the specified task pointer is nil.
458 */
459 static int
460 update_metric(pmValueSet *vsp, int reqstate, int mflags, task_t **result)
461 {
462 pmID pmid = vsp->pmid;
463 task_t *ntp = *result; /* pointer to new task */
464 task_t *ctp; /* pointer to current task */
465 optreq_t *rqp;
466 pmDesc *dp;
467 int i, j, inst;
468 int sts, need = 0;
469 int addpmid = 0;
470 int freedp;
471
472 /* allocate a new task if null task pointer passed in */
473 if (ntp == NULL) {
474 ntp = calloc(1, sizeof(task_t));
475 if (ntp == NULL) {
476 __pmNoMem("update_metric: new task calloc", sizeof(task_t),
477 PM_FATAL_ERR);
478 }
479 *result = ntp;
480 }
481
482 if ((mflags & MF_HAS_INDOM) == 0) {
483 rqp = findoptreq(pmid, 0);
484 ctp = (task_t *)(rqp->r_fetch->f_aux);
485 if (!update_ok(ctp->t_state, reqstate))
486 return 1;
487
488 /* if the new state is advisory off, just remove the metric */
489 if ((PMLC_GET_MAYBE(reqstate)) ||
490 (PMLC_GET_MAND(reqstate) == 0 && PMLC_GET_ON(reqstate) == 0))
491 del_optreq(rqp);
492 else {
493 /* update the optreq. For single valued metrics there are no
494 * instances involved so the sole optreq can just be re-used.
495 */
496 if ((sts = __pmOptFetchDel(&ctp->t_fetch, rqp)) < 0)
497 die("update_metric: 1 metric __pmOptFetchDel", sts);
498 if ((sts = __pmOptFetchAdd(&ntp->t_fetch, rqp)) < 0)
499 die("update_metric: 1 metric __pmOptFetchAdd", sts);
500 linkback(ntp);
501 addpmid = 1;
502 }
503 }
504 else {
505 /* metric has an instance domain */
506 if (vsp->numval > 0) {
507 /* tricky: since optFetch can't handle instance profiles of the
508 * form "all except these specific instances", and managing it
509 * manually is just too hard, reject requests for specific
510 * metric instances if "all instances" of the metric are already
511 * being logged.
512 * Note: advisory off "all instances" is excepted since ANY request
513 * overrides and advisory off. E.g. "advisory off all" followed by
514 * "advisory on someinsts" turns on advisory logging for
515 * "someinsts". mflags will be zero for "advisory off" metrics.
516 */
517 if (mflags & MF_HAS_ALL)
518 return 1; /* can't turn "all" into specific insts */
519
520 for (i = 0; i < vsp->numval; i++) {
521 dp = NULL;
522 freedp = 0;
523 inst = vsp->vlist[i].inst;
524 rqp = findoptreq(pmid, inst);
525 if (rqp != NULL) {
526 dp = rqp->r_desc;
527 ctp = (task_t *)(rqp->r_fetch->f_aux);
528 /* no work required if new task and current are the same */
529 if (ntp == ctp)
530 continue;
531 if (!update_ok(ctp->t_state, reqstate))
532 continue;
533
534 /* remove inst's group from current task */
535 if ((sts = __pmOptFetchDel(&ctp->t_fetch, rqp)) < 0)
536 die("update_metric: instance add __pmOptFetchDel", sts);
537
538 /* put group back if there are any instances left */
539 if (rqp->r_numinst > 1) {
540 /* remove inst from group */
541 for (j = 0; j < rqp->r_numinst; j++)
542 if (inst == rqp->r_instlist[j])
543 break;
544 /* don't call memmove to move zero bytes */
545 if (j < rqp->r_numinst - 1)
546 memmove(&rqp->r_instlist[j], &rqp->r_instlist[j+1],
547 (rqp->r_numinst - 1 - j) *
548 sizeof(rqp->r_instlist[0]));
549 rqp->r_numinst--;
550 /* (don't bother realloc-ing the instlist to a smaller size) */
551
552 if ((sts = __pmOptFetchAdd(&ctp->t_fetch, rqp)) < 0)
553 die("update_metric: instance del __pmOptFetchAdd", sts);
554 linkback(ctp);
555 /* no need to update hash list, rqp already there */
556 }
557 /* if that was the last instance, free the group */
558 else {
559 freedp = 1;
560 free(rqp->r_instlist);
561 free(rqp);
562 if (( sts = __pmHashDel(pmid, (void *)rqp, &pm_hash)) < 0)
563 die("update_metric: instance __pmHashDel", sts);
564 }
565 }
566
567 /* advisory off (mandatory maybe) metrics don't get put into
568 * the data structure
569 */
570 if (PMLC_GET_MAYBE(reqstate) ||
571 (PMLC_GET_MAND(reqstate) == 0 && PMLC_GET_ON(reqstate) == 0)) {
572 if (freedp)
573 free(dp);
574 continue;
575 }
576 addpmid = 1;
577
578 /* try to find an existing optreq_t for the instance */
579 rqp = find_instoptreq(ntp, pmid);
580 if (rqp != NULL) {
581 if ((sts = __pmOptFetchDel(&ntp->t_fetch, rqp)) < 0)
582 die("update_metric: instance add __pmOptFetchDel", sts);
583 }
584 /* no existing optreq_t found, allocate & populate a new one */
585 else {
586 rqp = (optreq_t *)calloc(1, sizeof(optreq_t));
587 if (rqp == NULL) {
588 __pmNoMem("update_metric: optreq calloc",
589 sizeof(optreq_t), PM_FATAL_ERR);
590 }
591 /* if the metric existed but the instance didn't, we don't
592 * have a valid pmDesc (dp), so find one.
593 */
594 if (dp == NULL) {
595 /* find metric and associated pmDesc */
596 __pmHashNode *hp;
597
|
Event var_compare_op: |
Comparing "hp" to null implies that "hp" might be null. |
| Also see events: |
[var_deref_op] |
|
At conditional (1): "hp != NULL": Taking false branch.
|
598 for (hp = __pmHashSearch(pmid, &pm_hash);
599 hp != NULL; hp = hp->next)
600 if (pmid == (pmID)hp->key)
601 break;
|
Event var_deref_op: |
Dereferencing null variable "hp". |
| Also see events: |
[var_compare_op] |
602 dp = ((optreq_t *)hp->data)->r_desc;
603 }
604 /* recycle pmDesc from the old group, if possible */
605 if (freedp) {
606 rqp->r_desc = dp;
607 freedp = 0;
608 }
609 /* otherwise allocate & copy a new pmDesc via dp */
610 else {
611 need = sizeof(pmDesc);
612 rqp->r_desc = (pmDesc *)malloc(need);
613 if (rqp->r_desc == NULL) {
614 __pmNoMem("update_metric: new inst pmDesc malloc",
615 need, PM_FATAL_ERR);
616 }
617 memcpy(rqp->r_desc, dp, need);
618 }
619 if ((sts = __pmHashAdd(pmid, (void *)rqp, &pm_hash)) < 0)
620 die("update_metric: __pmHashAdd", sts);
621 }
622
623 need = (rqp->r_numinst + 1) * sizeof(rqp->r_instlist[0]);
624 rqp->r_instlist = (int *)realloc(rqp->r_instlist, need);
625 if (rqp->r_instlist == NULL) {
626 __pmNoMem("update_metric: inst list resize", need,
627 PM_FATAL_ERR);
628 }
629 rqp->r_instlist[rqp->r_numinst++] = inst;
630 if ((sts = __pmOptFetchAdd(&ntp->t_fetch, rqp)) < 0)
631 die("update_metric: instance add __pmOptFetchAdd", sts);
632 linkback(ntp);
633 if (freedp)
634 free(dp);
635 }
636 }
637 /* the vset has numval == 0, a request for "all instances" */
638 else {
639 /* if the metric is a singular instance that has mandatory logging
640 * or has at least one instance with mandatory logging on, a
641 * request for advisory logging cannot be honoured
642 */
643 if ((mflags & MF_HAS_MAND) &&
644 PMLC_GET_MAND(reqstate) == 0 && PMLC_GET_MAYBE(reqstate) == 0)
645 return 1;
646
647 if (mflags & MF_HAS_ALL) {
648 /* if there is an "all instances" for the metric, it will be
649 * the only optreq_t for the metric
650 */
651 rqp = findoptreq(pmid, 0);
652 ctp = (task_t *)rqp->r_fetch->f_aux;
653
654 /* if the metric is "advisory on, all instances" and the
655 * request is for "mandatory maybe, all instances" the current
656 * advisory logging state of the metric is retained
657 */
658 if (PMLC_GET_MAND(ctp->t_state) == 0 && PMLC_GET_MAYBE(reqstate))
659 return 0;
660
661 /* advisory off & mandatory maybe metrics don't get put into
662 * the data structure
663 */
664 if (PMLC_GET_MAYBE(reqstate) ||
665 (PMLC_GET_MAND(reqstate) == 0 && PMLC_GET_ON(reqstate) == 0)) {
666 del_optreq(rqp);
667 return 0;
668 }
669
670 addpmid = 1;
671 if ((sts = __pmOptFetchDel(&ctp->t_fetch, rqp)) < 0)
672 die("update_metric: all inst __pmOptFetchDel", sts);
673 /* don't delete from hash list, rqp re-used */
674 if ((sts = __pmOptFetchAdd(&ntp->t_fetch, rqp)) < 0)
675 die("update_metric: all inst __pmOptFetchAdd", sts);
676 linkback(ntp);
677 }
678 else {
679 /* there are one or more specific instances for the metric.
680 * The metric cannot have an "all instances" at the same time.
681 *
682 * if the request is for "mandatory maybe, all instances" and
683 * the only instances of the metric all have advisory logging
684 * on, retain the current advisory semantics.
685 */
686 if (PMLC_GET_MAYBE(reqstate) &&
687 (mflags & MF_HAS_INST) && !(mflags & MF_HAS_MAND))
688 return 0;
689
690 dp = del_insts(pmid);
691
692 /* advisory off (mandatory maybe) metrics don't get put into
693 * the data structure
694 */
695 if (PMLC_GET_MAYBE(reqstate) ||
696 (PMLC_GET_MAND(reqstate) == 0 && PMLC_GET_ON(reqstate) == 0)) {
697 free(dp);
698 return 0;
699 }
700
701 addpmid = 1;
702 rqp = (optreq_t *)calloc(1, sizeof(optreq_t));
703 if (rqp == NULL) {
704 __pmNoMem("update_metric: all inst calloc",
705 sizeof(optreq_t), PM_FATAL_ERR);
706 }
707 rqp->r_desc = dp;
708 if ((sts = __pmOptFetchAdd(&ntp->t_fetch, rqp)) < 0)
709 die("update_metric: all inst __pmOptFetchAdd", sts);
710 linkback(ntp);
711 if ((sts = __pmHashAdd(pmid, (void *)rqp, &pm_hash)) < 0)
712 die("update_metric: all inst __pmHashAdd", sts);
713 }
714 }
715 }
716
717 if (!addpmid)
718 return 0;
719
720 /* add pmid to new task if not already there */
721 for (i = 0; i < ntp->t_numpmid; i++)
722 if (pmid == ntp->t_pmidlist[i])
723 break;
724 if (i >= ntp->t_numpmid) {
725 pmDesc desc;
726 char *name;
727 int need;
728
729 if ((sts = pmLookupDesc(pmid, &desc)) < 0)
730 die("update_metric: cannot lookup desc", sts);
731 if ((sts = pmNameID(pmid, &name)) < 0)
732 die("update_metric: cannot lookup name", sts);
733
734 need = (ntp->t_numpmid + 1) * sizeof(pmID);
735 if (!(ntp->t_pmidlist = (pmID *)realloc(ntp->t_pmidlist, need)))
736 __pmNoMem("update_metric: grow task pmidlist", need, PM_FATAL_ERR);
737 need = (ntp->t_numpmid + 1) * sizeof(char *);
738 if (!(ntp->t_namelist = (char **)realloc(ntp->t_namelist, need)))
739 __pmNoMem("update_metric: grow task namelist", need, PM_FATAL_ERR);
740 need = (ntp->t_numpmid + 1) * sizeof(pmDesc);
741 if (!(ntp->t_desclist = (pmDesc *)realloc(ntp->t_desclist, need)))
742 __pmNoMem("update_metric: grow task desclist", need, PM_FATAL_ERR);
743 i = ntp->t_numpmid;
744 ntp->t_pmidlist[i] = pmid;
745 ntp->t_namelist[i] = name;
746 ntp->t_desclist[i] = desc;
747 ntp->t_numpmid++;
748 }
749 return 0;
750 }
751
752 /* Given a state and a delta, return in result the first matching task.
753 * Return true if a matching task was found
754 */
755 static int
756 find_task(int state, int delta, task_t **result)
757 {
758 task_t *tp;
759 int tdelta;
760
761 /* Never return a "once only" task, it may have gone off already and just
762 * be hanging around
763 * TODO: cleanup once only tasks after callback invoked
764 */
765 if (delta == 0)
766 return 0;
767
768 for (tp = tasklist; tp != NULL; tp = tp->t_next) {
769 tdelta = tp->t_delta.tv_sec * 1000 + (tp->t_delta.tv_usec / 1000);
770 if (state == (tp->t_state & 0x3) && delta == tdelta)
771 break;
772 }
773 *result = tp;
774 return tp != NULL;
775 }
776
777 /* Return a mask containing the history flags for a given metric/instance.
778 * the history flags indicate whether the metric/instance is in the log at all
779 * and whether the last fetch of the metric/instance was successful.
780 *
781 * The result is suitable for ORing into the result returned by a control log
782 * request.
783 */
784 static int
785 gethistflags(pmID pmid, int inst)
786 {
787 __pmHashNode *hp;
788 pmidhist_t *php;
789 insthist_t *ihp;
790 int i, found;
791 int val;
792
793 for (hp = __pmHashSearch(pmid, &hist_hash); hp != NULL; hp = hp->next)
794 if ((pmID)hp->key == pmid)
795 break;
796 if (hp == NULL)
797 return 0;
798 php = (pmidhist_t *)hp->data;
799 ihp = &php->ph_instlist[0];
800 val = 0;
801 if (php->ph_indom != PM_INDOM_NULL) {
802 for (i = 0; i < php->ph_numinst; i++, ihp++)
803 if (ihp->ih_inst == inst)
804 break;
805 found = i < php->ph_numinst;
806 }
807 else
808 found = php->ph_numinst > 0;
809 if (found) {
810 PMLC_SET_INLOG(val, 1);
811 val |= ihp->ih_flags; /* only "available flag" is ever set */
812 }
813 return val;
814 }
815
816 /* take a pmResult (from a control log request) and half-clone it: return a
817 * pointer to a new pmResult struct which shares the pmValueSets in the
818 * original that have numval > 0, and has null pointers for the pmValueSets
819 * in the original with numval <= 0
820 */
821 static pmResult *
822 siamise_request(pmResult *request)
823 {
824 int i, need;
825 pmValueSet *vsp;
826 pmResult *result;
827
828 need = sizeof(pmResult) + (request->numpmid - 1) * sizeof(pmValueSet *);
829 result = (pmResult *)malloc(need);
830 if (result == NULL) {
831 __pmNoMem("siamise_request: malloc pmResult", need, PM_FATAL_ERR);
832 }
833 for (i = 0; i < request->numpmid; i++) {
834 vsp = request->vset[i];
835 if (vsp->numval > 0)
836 result->vset[i] = request->vset[i];
837 else
838 result->vset[i] = NULL;
839 }
840 result->timestamp = request->timestamp; /* structure assignment */
841 result->numpmid = request->numpmid;
842
843 return result;
844 }
845
846 /* Temporarily borrow a bit in the metric/instance history to indicate that
847 * the instance currently exists in the instance domain. The macros below
848 * set and get the bit, which is cleared after we are finished with it here.
849 */
850
851 #define PMLC_SET_USEINDOM(val, flag) (val = (val & ~0x1000) | (flag << 12 ))
852 #define PMLC_GET_USEINDOM(val) ((val & 0x1000) >> 12)
853
854 /* create a pmValueSet large enough to contain the union of the current
855 * instance domain of the specified metric and any previous instances from
856 * the history list.
857 */
858 static pmValueSet *
859 build_vset(pmID pmid, int usehist)
860 {
861 __pmHashNode *hp;
862 pmidhist_t *php = NULL;
863 insthist_t *ihp;
864 int need = 0;
865 int i, numindom = 0;
866 pmDesc desc;
867 int have_desc;
868 int *instlist;
869 char **namelist;
870 pmValueSet *vsp;
871
872 if (usehist) {
873 /* find the number of instances of the metric in the history (1 if
874 * single-valued metric)
875 */
876 for (hp = __pmHashSearch(pmid, &hist_hash); hp != NULL; hp = hp->next)
877 if ((pmID)hp->key == pmid)
878 break;
879 if (hp != NULL) {
880 php = (pmidhist_t *)hp->data;
881 need = php->ph_numinst;
882 }
883 }
884 /*
885 * get the current instance domain, so that if the metric hasn't been
886 * logged yet a sensible result is returned.
887 */
888 if ((have_desc = pmLookupDesc(pmid, &desc)) < 0)
889 goto no_info;
890 if (desc.indom == PM_INDOM_NULL)
891 need = 1; /* will be same in history */
892 else {
893 int j;
894
895 if ((numindom = pmGetInDom(desc.indom, &instlist, &namelist)) < 0) {
896 have_desc = numindom;
897 goto no_info;
898 }
899 /* php will be null if usehist is false or there is no history yet */
900 if (php == NULL)
901 need = numindom; /* no history => use indom */
902 else
903 for (i = 0; i < numindom; i++) {
904 int inst = instlist[i];
905
906 for (j = 0; j < php->ph_numinst; j++)
907 if (inst == php->ph_instlist[j].ih_inst)
908 break;
909 /*
910 * if instance is in history but not instance domain, leave
911 * extra space for it in vset, otherwise use the USEINDOM
912 * flag to avoid another NxM comparison when building the vset
913 * instances later.
914 */
915 if (j >= php->ph_numinst)
916 need++;
917 else
918 PMLC_SET_USEINDOM(php->ph_instlist[j].ih_flags, 1);
919 }
920 }
921
922 no_info:
923
924 need = sizeof(pmValueSet) + (need - 1) * sizeof(pmValue);
925 vsp = (pmValueSet *)malloc(need);
926 if (vsp == NULL) {
927 __pmNoMem("build_vset for control/enquire", need, PM_FATAL_ERR);
928 }
929 vsp->pmid = pmid;
930 if (have_desc < 0) {
931 vsp->numval = have_desc;
932 }
933 else if (desc.indom == PM_INDOM_NULL) {
934 vsp->vlist[0].inst = PM_IN_NULL;
935 vsp->numval = 1;
936 }
937 else {
938 int j;
939
940 i = 0;
941 /* get instances out of instance domain first */
942 if (numindom > 0)
943 for (j = 0; j < numindom; j++)
944 vsp->vlist[i++].inst = instlist[j];
945
946 /* then any not in instance domain from history */
947 if (php != NULL) {
948 ihp = &php->ph_instlist[0];
949 for (j = 0; j < php->ph_numinst; j++, ihp++)
950 if (PMLC_GET_USEINDOM(ihp->ih_flags))
951 /* it's already in the indom */
952 PMLC_SET_USEINDOM(ihp->ih_flags, 0);
953 else
954 vsp->vlist[i++].inst = ihp->ih_inst;
955 }
956 vsp->numval = i;
957 free(instlist);
958 free(namelist);
959 }
960
961 return vsp;
962 }
963
964 static int
965 do_control(__pmPDU *pb)
966 {
967 int sts;
968 int control;
969 int state;
970 int delta;
971 pmResult *request;
972 pmResult *result;
973 int siamised = 0; /* the verb from siamese (as in twins) */
974 int i;
975 int j;
976 int val;
977 pmValueSet *vsp;
978 optreq_t *rqp;
979 task_t *tp;
980 time_t now;
981 int reqstate = 0;
982
983 /*
984 * TODO - encoding for logging interval in requests and results?
985 */
986 if ((sts = __pmDecodeLogControl(pb, &request, &control, &state, &delta)) < 0)
987 return sts;
988
989 #ifdef PCP_DEBUG
990 if (pmDebug & DBG_TRACE_LOG) {
991 fprintf(stderr, "do_control: control=%d state=%d delta=%d request ...\n",
992 control, state, delta);
993 dumpcontrol(stderr, request, 0);
994 }
995 #endif
996
997 if (control == PM_LOG_MANDATORY || control == PM_LOG_ADVISORY) {
998 time(&now);
999 fprintf(stderr, "\n%s", ctime(&now));
1000 fprintf(stderr, "pmlc request from %s: %s",
1001 pmlc_host, control == PM_LOG_MANDATORY ? "mandatory" : "advisory");
1002 if (state == PM_LOG_ON) {
1003 if (delta == 0)
1004 fprintf(stderr, " on once\n");
1005 else
1006 fprintf(stderr, " on %.1f sec\n", (float)delta/1000);
1007 }
1008 else if (state == PM_LOG_OFF)
1009 fprintf(stderr, " off\n");
1010 else
1011 fprintf(stderr, " maybe\n");
1012 }
1013
1014 /*
1015 * access control checks
1016 */
1017 sts = 0;
1018 switch (control) {
1019 case PM_LOG_MANDATORY:
1020 if (clientops & PM_OP_LOG_MAND)
1021 sts = PM_ERR_PERMISSION;
1022 break;
1023
1024 case PM_LOG_ADVISORY:
1025 if (clientops & PM_OP_LOG_ADV)
1026 sts = PM_ERR_PERMISSION;
1027 break;
1028
1029 case PM_LOG_ENQUIRE:
1030 if (clientops & PM_OP_LOG_ENQ)
1031 sts = PM_ERR_PERMISSION;
1032 break;
1033
1034 default:
1035 fprintf(stderr, "Bad control PDU type %d\n", control);
1036 sts = PM_ERR_IPC;
1037 break;
1038 }
1039 if (sts < 0) {
1040 if (control == PM_LOG_MANDATORY || control == PM_LOG_ADVISORY)
1041 fprintf(stderr, "Error: %s\n", pmErrStr(sts));
1042 if ((sts = __pmSendError(clientfd, FROM_ANON, sts)) < 0)
1043 __pmNotifyErr(LOG_ERR,
1044 "do_control: error sending Error PDU to client: %s\n",
1045 pmErrStr(sts));
1046 pmFreeResult(request);
1047 return sts;
1048 }
1049
1050 /* handle everything except PM_LOG_ENQUIRE */
1051 if (control == PM_LOG_MANDATORY || control == PM_LOG_ADVISORY) {
1052 /* update the logging status of metrics */
1053
1054 task_t *newtp = NULL; /* task for metrics/insts in request */
1055 int newtask;
1056 int mflags;
1057
1058 /* convert state and control to the bitmask used in pmlogger and values
1059 * returned in results. Remember that reqstate starts with nothing on.
1060 */
1061 if (state == PM_LOG_ON)
1062 PMLC_SET_ON(reqstate, 1);
1063 else
1064 PMLC_SET_ON(reqstate, 0);
1065 if (control == PM_LOG_MANDATORY) {
1066 if (state == PM_LOG_MAYBE)
1067 /* mandatory+maybe => maybe+advisory+off */
1068 PMLC_SET_MAYBE(reqstate, 1);
1069 else
1070 PMLC_SET_MAND(reqstate, 1);
1071 }
1072
1073 /* try to find an existing task for the request */
1074 newtask = !find_task(reqstate, delta, &newtp);
1075
1076 for (i = 0; i < request->numpmid; i++) {
1077 vsp = request->vset[i];
1078 if (vsp->numval < 0)
1079 /*
1080 * request is malformed, as we cannot control logging
1081 * for an undefined instance ... there is no way to
1082 * return an error from here, so simply ignore this
1083 * metric
1084 */
1085 continue;
1086 mflags = find_metric(vsp->pmid);
1087 if (mflags < 0) {
1088 /* only add new metrics if they are ON or MANDATORY OFF
1089 * Careful: mandatory+maybe is mandatory+maybe+off
1090 */
1091 if (PMLC_GET_ON(reqstate) ||
1092 (PMLC_GET_MAND(reqstate) && !PMLC_GET_MAYBE(reqstate)))
1093 add_metric(vsp, &newtp);
1094 }
1095 else
1096 /* already a specification for this metric */
1097 update_metric(vsp, reqstate, mflags, &newtp);
1098 }
1099
1100 /* schedule new logging task if new metric(s) specified */
1101 if (newtask && newtp != NULL) {
1102 if (newtp->t_fetch == NULL) {
1103 /* the new task ended up with no fetch groups, throw it away */
1104 if (newtp->t_pmidlist != NULL)
1105 free(newtp->t_pmidlist);
1106 free(newtp);
1107 }
1108 else {
1109 /* link new task into tasklist */
1110 newtp->t_next = tasklist;
1111 tasklist = newtp;
1112
1113 /* use only the MAND/ADV and ON/OFF bits of reqstate */
1114 newtp->t_state = reqstate & 0x3;
1115 if (PMLC_GET_ON(reqstate)) {
1116 newtp->t_delta.tv_sec = delta / 1000;
1117 newtp->t_delta.tv_usec = (delta % 1000) * 1000;
1118 newtp->t_afid = __pmAFregister(&newtp->t_delta, (void *)newtp,
1119 log_callback);
1120 }
1121 else
1122 newtp->t_delta.tv_sec = newtp->t_delta.tv_usec = 0;
1123 linkback(newtp); /* TODO: really needed? (no) */
1124 }
1125 }
1126 }
1127
1128 #ifdef PCP_DEBUG
1129 if (pmDebug & DBG_TRACE_APPL0)
1130 dumpit();
1131 #endif
1132
1133 /* just ignore advisory+maybe---the returned pmResult will have the metrics
1134 * in their original state indicating that the request could not be
1135 * satisfied.
1136 */
1137
1138 result = request;
1139 result->timestamp.tv_sec = result->timestamp.tv_usec = 0; /* for purify */
1140 /* write the current state of affairs into the result _pmResult */
1141 for (i = 0; i < request->numpmid; i++) {
1142
1143 if (control == PM_LOG_MANDATORY || control == PM_LOG_ADVISORY) {
1144 char *p;
1145
1146 sts = pmNameID(request->vset[i]->pmid, &p);
1147 if (sts < 0)
1148 fprintf(stderr, " metric: %s", pmIDStr(request->vset[i]->pmid));
1149 else {
1150 fprintf(stderr, " metric: %s", p);
1151 free(p);
1152 }
1153 }
1154
1155 if (request->vset[i]->numval <= 0 && !siamised) {
1156 result = siamise_request(request);
1157 siamised = 1;
1158 }
1159 /*
1160 * pmids with numval <= 0 in the request have a null vset ptr in the
1161 * in the corresponding place in the siamised result.
1162 */
1163 if (result->vset[i] != NULL)
1164 vsp = result->vset[i];
1165 else {
1166 /* the result should also contain the history for an all instances
1167 * enquire request. Control requests just get the current indom
1168 * since the user of pmlc really wants to see what's being logged
1169 * now rather than in the past.
1170 */
1171 vsp = build_vset(request->vset[i]->pmid, control == PM_LOG_ENQUIRE);
1172 result->vset[i] = vsp;
1173 }
1174 vsp->valfmt = PM_VAL_INSITU;
1175 for (j = 0; j < vsp->numval; j++) {
1176 rqp = findoptreq(vsp->pmid, vsp->vlist[j].inst);
1177 val = 0;
1178 if (rqp == NULL) {
1179 PMLC_SET_STATE(val, 0);
1180 PMLC_SET_DELTA(val, 0);
1181 }
1182 else {
1183 tp = rqp->r_fetch->f_aux;
1184 PMLC_SET_STATE(val, tp->t_state);
1185 PMLC_SET_DELTA(val, (tp->t_delta.tv_sec*1000 + tp->t_delta.tv_usec/1000));
1186 }
1187
1188 val |= gethistflags(vsp->pmid, vsp->vlist[j].inst);
1189 vsp->vlist[j].value.lval = val;
1190
1191 if (control == PM_LOG_MANDATORY || control == PM_LOG_ADVISORY) {
1192 int expstate = 0;
1193 int statemask = 0;
1194 int expdelta;
1195 if (rqp != NULL && rqp->r_desc->indom != PM_INDOM_NULL) {
1196 char *p;
1197 if (j == 0)
1198 fputc('\n', stderr);
1199 if (pmNameInDom(rqp->r_desc->indom, vsp->vlist[j].inst, &p) >= 0) {
1200 fprintf(stderr, " instance: %s", p);
1201 free(p);
1202 }
1203 else
1204 fprintf(stderr, " instance: #%d", vsp->vlist[j].inst);
1205 }
1206 else {
1207 /* no pmDesc ... punt */
1208 if (vsp->numval > 1 || vsp->vlist[j].inst != PM_IN_NULL) {
1209 if (j == 0)
1210 fputc('\n', stderr);
1211 fprintf(stderr, " instance: #%d", vsp->vlist[j].inst);
1212 }
1213 }
1214 if (state != PM_LOG_MAYBE) {
1215 if (control == PM_LOG_MANDATORY)
1216 PMLC_SET_MAND(expstate, 1);
1217 else
1218 PMLC_SET_MAND(expstate, 0);
1219 if (state == PM_LOG_ON)
1220 PMLC_SET_ON(expstate, 1);
1221 else
1222 PMLC_SET_ON(expstate, 0);
1223 PMLC_SET_MAND(statemask, 1);
1224 PMLC_SET_ON(statemask, 1);
1225 }
1226 else {
1227 PMLC_SET_MAND(expstate, 0);
1228 PMLC_SET_MAND(statemask, 1);
1229 }
1230 expdelta = PMLC_GET_ON(expstate) ? delta : 0;
1231 if ((PMLC_GET_STATE(val) & statemask) != expstate ||
1232 PMLC_GET_DELTA(val) != expdelta)
1233 fprintf(stderr, " [request failed]");
1234 fputc('\n', stderr);
1235 }
1236 }
1237 }
1238
1239 #ifdef PCP_DEBUG
1240 if (pmDebug & DBG_TRACE_LOG) {
1241 __pmDumpResult(stderr, result);
1242 }
1243 #endif
1244
1245 if ((sts = __pmSendResult(clientfd, FROM_ANON, result)) < 0)
1246 __pmNotifyErr(LOG_ERR,
1247 "do_control: error sending Error PDU to client: %s\n",
1248 pmErrStr(sts));
1249
1250 if (siamised) {
1251 for (i = 0; i < request->numpmid; i++)
1252 if (request->vset[i]->numval <= 0)
1253 free(result->vset[i]);
1254 free(result);
1255 }
1256 pmFreeResult(request);
1257
1258 return 0;
1259 }
1260
1261 /*
1262 * sendstatus
1263 * (data_x send is kept for backwards compatability with PCP 1.x)
1264 */
1265 static int
1266 sendstatus(void)
1267 {
1268 int rv;
1269 int end;
1270 int version;
1271 static int firsttime = 1;
1272 static char *tzlogger;
1273 struct timeval now;
1274 struct hostent *hep = NULL;
1275
1276 if (firsttime) {
1277 tzlogger = __pmTimezone();
1278 firsttime = 0;
1279 }
1280
1281 if ((version = __pmVersionIPC(clientfd)) < 0)
1282 return version;
1283
1284 if (version >= LOG_PDU_VERSION2) {
1285 __pmLoggerStatus ls;
1286
1287 if ((ls.ls_state = logctl.l_state) == PM_LOG_STATE_NEW)
1288 ls.ls_start.tv_sec = ls.ls_start.tv_usec = 0;
1289 else
1290 memcpy(&ls.ls_start, &logctl.l_label.ill_start, sizeof(ls.ls_start));
1291 memcpy(&ls.ls_last, &last_stamp, sizeof(ls.ls_last));
1292 __pmtimevalNow(&now);
1293 ls.ls_timenow.tv_sec = (__int32_t)now.tv_sec;
1294 ls.ls_timenow.tv_usec = (__int32_t)now.tv_usec;
1295 ls.ls_vol = logctl.l_curvol;
1296 ls.ls_size = ftell(logctl.l_mfp);
1297
1298 /* be careful of buffer size mismatches when copying strings */
1299 end = sizeof(ls.ls_hostname) - 1;
1300 strncpy(ls.ls_hostname, logctl.l_label.ill_hostname, end);
1301 ls.ls_hostname[end] = '\0';
1302 end = sizeof(ls.ls_fqdn) - 1;
1303 hep = gethostbyname(logctl.l_label.ill_hostname);
1304 if (hep != NULL)
1305 /* send the fully qualified domain name */
1306 strncpy(ls.ls_fqdn, hep->h_name, end);
1307 else
1308 /* use the hostname from -h ... on command line */
1309 strncpy(ls.ls_fqdn, logctl.l_label.ill_hostname, end);
1310 ls.ls_fqdn[end] = '\0';
1311
1312 end = sizeof(ls.ls_tz) - 1;
1313 strncpy(ls.ls_tz, logctl.l_label.ill_tz, end);
1314 ls.ls_tz[end] = '\0';
1315 end = sizeof(ls.ls_tzlogger) - 1;
1316 if (tzlogger != NULL)
1317 strncpy(ls.ls_tzlogger, tzlogger, end);
1318 else
1319 end = 0;
1320 ls.ls_tzlogger[end] = '\0';
1321
1322 rv = __pmSendLogStatus(clientfd, &ls);
1323 }
1324 else
1325 rv = PM_ERR_IPC;
1326 return rv;
1327 }
1328
1329 static int
1330 do_request(__pmPDU *pb)
1331 {
1332 int sts;
1333 int type;
1334
1335 if ((sts = __pmDecodeLogRequest(pb, &type)) < 0) {
1336 __pmNotifyErr(LOG_ERR, "do_request: error decoding PDU: %s\n", pmErrStr(sts));
1337 return PM_ERR_IPC;
1338 }
1339
1340 switch (type) {
1341 case LOG_REQUEST_STATUS:
1342 sts = sendstatus();
1343 break;
1344
1345 case LOG_REQUEST_NEWVOLUME:
1346 sts = newvolume(VOL_SW_PMLC);
1347 if (sts >= 0)
1348 sts = logctl.l_label.ill_vol;
1349 __pmSendError(clientfd, FROM_ANON, sts);
1350 break;
1351
1352 case LOG_REQUEST_SYNC:
1353 sts = do_flush();
1354 __pmSendError(clientfd, FROM_ANON, sts);
1355 break;
1356
1357 /*
1358 * QA support ... intended for error injection
1359 * If the request is > QA_OFF then this is a code to enable
1360 * a specific style of error behaviour. If the request
1361 * is QA_OFF, this disables the error behaviour.
1362 *
1363 * Supported behaviours.
1364 * QA_SLEEPY
1365 * After this exchange with pmlc, sleep for 5 seconds
1366 * after each incoming pmlc request ... allows testing
1367 * of timeout logic in pmlc
1368 */
1369
1370 case QA_OFF:
1371 qa_case = 0;
1372 __pmSendError(clientfd, FROM_ANON, 0);
1373 break;
1374
1375 case QA_SLEEPY:
1376 qa_case = type;
1377 __pmSendError(clientfd, FROM_ANON, 0);
1378 break;
1379
1380 default:
1381 fprintf(stderr, "do_request: bad request type %d\n", type);
1382 sts = PM_ERR_IPC;
1383 break;
1384 }
1385 return sts;
1386 }
1387
1388 static int
1389 do_creds(__pmPDU *pb)
1390 {
1391 int i;
1392 int sts;
1393 int version = UNKNOWN_VERSION;
1394 int credcount;
1395 int sender;
1396 __pmCred *credlist = NULL;
1397
1398 if ((sts = __pmDecodeCreds(pb, &sender, &credcount, &credlist)) < 0) {
1399 __pmNotifyErr(LOG_ERR, "do_creds: error decoding PDU: %s\n", pmErrStr(sts));
1400 return PM_ERR_IPC;
1401 }
1402
1403 for (i = 0; i < credcount; i++) {
1404 if (credlist[i].c_type == CVERSION) {
1405 version = credlist[i].c_vala;
1406 if ((sts = __pmSetVersionIPC(clientfd, version)) < 0) {
1407 free(credlist);
1408 return sts;
1409 }
1410 }
1411 }
1412
1413 if (credlist)
1414 free(credlist);
1415
1416 #ifdef PCP_DEBUG
1417 if (pmDebug & DBG_TRACE_APPL1)
1418 fprintf(stderr, "do_creds: pmlc version=%d\n", version);
1419 #endif
1420
1421 return sts;
1422 }
1423
1424 /*
1425 * Service a request from the pmlogger client.
1426 * Return non-zero if the client has closed the connection.
1427 */
1428 int
1429 client_req(void)
1430 {
1431 int sts;
1432 __pmPDU *pb;
1433 __pmPDUHdr *php;
1434
1435 if ((sts = __pmGetPDU(clientfd, ANY_SIZE, TIMEOUT_DEFAULT, &pb)) <= 0) {
1436 if (sts != 0)
1437 fprintf(stderr, "client_req: %s\n", pmErrStr(sts));
1438 __pmResetIPC(clientfd);
1439 close(clientfd);
1440 clientfd = -1;
1441 return 1;
1442 }
1443 if (qa_case == QA_SLEEPY) {
1444 /* error injection - delay before processing and responding */
1445 sleep(5);
1446 }
1447 php = (__pmPDUHdr *)pb;
1448 sts = 0;
1449
1450 switch (php->type) {
1451 case PDU_CREDS: /* version 2 PDU */
1452 sts = do_creds(pb);
1453 break;
1454 case PDU_LOG_REQUEST: /* version 2 PDU */
1455 sts = do_request(pb);
1456 break;
1457 case PDU_LOG_CONTROL: /* version 2 PDU */
1458 sts = do_control(pb);
1459 break;
1460 default: /* unknown PDU */
1461 fprintf(stderr, "client_req: bad PDU type 0x%x\n", php->type);
1462 sts = PM_ERR_IPC;
1463 break;
1464 }
1465
1466 if (sts >= 0)
1467 return 0;
1468 else {
1469 /* the client isn't playing by the rules; disconnect it */
1470 __pmSendError(clientfd, FROM_ANON, sts);
1471 __pmCloseSocket(clientfd);
1472 clientfd = -1;
1473 return 1;
1474 }
1475 }