1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <sys/fm/protocol.h>
27 #include <sys/types.h>
28 #include <sys/mkdev.h>
29
30 #include <alloca.h>
31 #include <unistd.h>
32 #include <limits.h>
33 #include <strings.h>
34 #include <stdlib.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <libgen.h>
38 #include <dirent.h>
39 #include <fmd_log_impl.h>
40 #include <fmd_log.h>
41
42 #define CAT_FMA_RGROUP (EXT_GROUP | EXC_DEFAULT | EXD_GROUP_RFMA)
43 #define CAT_FMA_GROUP (EXT_GROUP | EXC_DEFAULT | EXD_GROUP_FMA)
44
45 #define CAT_FMA_LABEL (EXT_STRING | EXC_DEFAULT | EXD_FMA_LABEL)
46 #define CAT_FMA_VERSION (EXT_STRING | EXC_DEFAULT | EXD_FMA_VERSION)
47 #define CAT_FMA_OSREL (EXT_STRING | EXC_DEFAULT | EXD_FMA_OSREL)
48 #define CAT_FMA_OSVER (EXT_STRING | EXC_DEFAULT | EXD_FMA_OSVER)
49 #define CAT_FMA_PLAT (EXT_STRING | EXC_DEFAULT | EXD_FMA_PLAT)
50 #define CAT_FMA_UUID (EXT_STRING | EXC_DEFAULT | EXD_FMA_UUID)
51 #define CAT_FMA_TODSEC (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_TODSEC)
52 #define CAT_FMA_TODNSEC (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_TODNSEC)
53 #define CAT_FMA_NVLIST (EXT_RAW | EXC_DEFAULT | EXD_FMA_NVLIST)
54 #define CAT_FMA_MAJOR (EXT_UINT32 | EXC_DEFAULT | EXD_FMA_MAJOR)
55 #define CAT_FMA_MINOR (EXT_UINT32 | EXC_DEFAULT | EXD_FMA_MINOR)
56 #define CAT_FMA_INODE (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_INODE)
57 #define CAT_FMA_OFFSET (EXT_UINT64 | EXC_DEFAULT | EXD_FMA_OFFSET)
58
59 static int fmd_log_load_record(fmd_log_t *, uint_t, fmd_log_record_t *);
60 static void fmd_log_free_record(fmd_log_record_t *);
61 static int fmd_log_load_xrefs(fmd_log_t *, uint_t, fmd_log_record_t *);
62
63 static const char FMD_CREATOR[] = "fmd";
64
65 /*
66 * fmd_log_set_errno is used as a utility function throughout the library. It
67 * sets both lp->log_errno and errno to the specified value. If the current
68 * error is EFDL_EXACCT, we store it internally as that value plus ea_error().
69 * If no ea_error() is present, we assume EFDL_BADTAG (catalog tag mismatch).
70 */
71 static int
fmd_log_set_errno(fmd_log_t * lp,int err)72 fmd_log_set_errno(fmd_log_t *lp, int err)
73 {
74 if (err == EFDL_EXACCT && ea_error() != EXR_OK)
75 lp->log_errno = EFDL_EXACCT + ea_error();
76 else if (err == EFDL_EXACCT)
77 lp->log_errno = EFDL_BADTAG;
78 else
79 lp->log_errno = err;
80
81 errno = lp->log_errno;
82 return (-1);
83 }
84
85 /*PRINTFLIKE2*/
86 static void
fmd_log_dprintf(fmd_log_t * lp,const char * format,...)87 fmd_log_dprintf(fmd_log_t *lp, const char *format, ...)
88 {
89 va_list ap;
90
91 if (lp->log_flags & FMD_LF_DEBUG) {
92 (void) fputs("fmd_log DEBUG: ", stderr);
93 va_start(ap, format);
94 (void) vfprintf(stderr, format, ap);
95 va_end(ap);
96 }
97 }
98
99 /*
100 * fmd_log_load_record() is used to load the exacct object at the current file
101 * location into the specified fmd_log_record structure. Once the caller has
102 * made use of this information, it can clean up using fmd_log_free_record().
103 */
104 static int
fmd_log_load_record(fmd_log_t * lp,uint_t iflags,fmd_log_record_t * rp)105 fmd_log_load_record(fmd_log_t *lp, uint_t iflags, fmd_log_record_t *rp)
106 {
107 ea_object_t *grp, *obj;
108 off64_t off;
109 int err;
110
111 if (iflags & FMD_LOG_XITER_OFFS) {
112 ea_clear(&lp->log_ea);
113 off = lseek64(lp->log_fd, 0, SEEK_CUR);
114 }
115
116 if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL)
117 return (fmd_log_set_errno(lp, EFDL_EXACCT));
118
119 if (grp->eo_catalog != CAT_FMA_RGROUP &&
120 grp->eo_catalog != CAT_FMA_GROUP) {
121 fmd_log_dprintf(lp, "bad catalog tag 0x%x\n", grp->eo_catalog);
122 ea_free_object(grp, EUP_ALLOC);
123 return (fmd_log_set_errno(lp, EFDL_EXACCT));
124 }
125
126 bzero(rp, sizeof (fmd_log_record_t));
127 rp->rec_grp = grp;
128
129 if (iflags & FMD_LOG_XITER_OFFS)
130 rp->rec_off = off;
131
132 for (obj = grp->eo_group.eg_objs; obj != NULL; obj = obj->eo_next) {
133 switch (obj->eo_catalog) {
134 case CAT_FMA_NVLIST:
135 if ((err = nvlist_unpack(obj->eo_item.ei_raw,
136 obj->eo_item.ei_size, &rp->rec_nvl, 0)) != 0) {
137 fmd_log_free_record(rp);
138 return (fmd_log_set_errno(lp, err));
139 }
140 break;
141
142 case CAT_FMA_TODSEC:
143 rp->rec_sec = obj->eo_item.ei_uint64;
144 break;
145
146 case CAT_FMA_TODNSEC:
147 rp->rec_nsec = obj->eo_item.ei_uint64;
148 break;
149
150 case CAT_FMA_GROUP:
151 rp->rec_nrefs += obj->eo_group.eg_nobjs;
152 break;
153 }
154 }
155
156 if (rp->rec_nvl == NULL || nvlist_lookup_string(rp->rec_nvl,
157 FM_CLASS, (char **)&rp->rec_class) != 0) {
158 fmd_log_free_record(rp);
159 return (fmd_log_set_errno(lp, EFDL_NOCLASS));
160 }
161
162 if (rp->rec_nrefs != 0 && fmd_log_load_xrefs(lp, iflags, rp) != 0) {
163 err = errno; /* errno is set for us */
164 fmd_log_free_record(rp);
165 return (fmd_log_set_errno(lp, err));
166 }
167
168 return (0);
169 }
170
171 /*
172 * fmd_log_free_record frees memory associated with the specified record. If
173 * cross-references are contained in this record, we proceed recursively.
174 */
175 static void
fmd_log_free_record(fmd_log_record_t * rp)176 fmd_log_free_record(fmd_log_record_t *rp)
177 {
178 uint_t i;
179
180 if (rp->rec_xrefs != NULL) {
181 for (i = 0; i < rp->rec_nrefs; i++)
182 fmd_log_free_record(&rp->rec_xrefs[i]);
183 free(rp->rec_xrefs);
184 }
185
186 nvlist_free(rp->rec_nvl);
187 ea_free_object(rp->rec_grp, EUP_ALLOC);
188 }
189
190 /*
191 * fmd_log_load_xref loads the cross-reference represented by the specified
192 * exacct group 'grp' into the next empty slot in rp->rec_xrefs. This function
193 * is called repeatedly by fmd_log_load_xrefs() for each embedded reference.
194 */
195 static int
fmd_log_load_xref(fmd_log_t * lp,uint_t iflags,fmd_log_record_t * rp,ea_object_t * grp)196 fmd_log_load_xref(fmd_log_t *lp, uint_t iflags,
197 fmd_log_record_t *rp, ea_object_t *grp)
198 {
199 ea_object_t *obj;
200 fmd_log_t *xlp;
201 dev_t dev;
202
203 off64_t off = (off64_t)-1L;
204 major_t maj = (major_t)-1L;
205 minor_t min = (minor_t)-1L;
206 ino64_t ino = (ino64_t)-1L;
207 char *uuid = NULL;
208
209 for (obj = grp->eo_group.eg_objs; obj != NULL; obj = obj->eo_next) {
210 switch (obj->eo_catalog) {
211 case CAT_FMA_MAJOR:
212 maj = obj->eo_item.ei_uint32;
213 break;
214 case CAT_FMA_MINOR:
215 min = obj->eo_item.ei_uint32;
216 break;
217 case CAT_FMA_INODE:
218 ino = obj->eo_item.ei_uint64;
219 break;
220 case CAT_FMA_OFFSET:
221 off = obj->eo_item.ei_uint64;
222 break;
223 case CAT_FMA_UUID:
224 uuid = obj->eo_item.ei_string;
225 break;
226 }
227 }
228
229 if (off == (off64_t)-1L || (uuid == NULL && (ino == (ino64_t)-1L ||
230 maj == (major_t)-1L || min == (minor_t)-1L)))
231 return (fmd_log_set_errno(lp, EFDL_BADREF));
232
233 if (uuid == NULL && (dev = makedev(maj, min)) == NODEV)
234 return (fmd_log_set_errno(lp, EFDL_BADDEV));
235
236 /*
237 * Search our xref list for matching (dev_t, ino64_t) or (uuid).
238 * If we can't find one, return silently without
239 * doing anything. We expect log xrefs to be broken whenever log
240 * files are trimmed or removed; their only purpose is to help us
241 * debug diagnosis engine algorithms.
242 */
243 for (xlp = lp->log_xrefs; xlp != NULL; xlp = xlp->log_xnext) {
244 if (uuid == NULL) {
245 if (xlp->log_stat.st_ino == ino &&
246 xlp->log_stat.st_dev == dev)
247 break;
248 } else if (xlp->log_uuid != NULL &&
249 strcmp(xlp->log_uuid, uuid) == 0)
250 break;
251 }
252
253 if (xlp == NULL) {
254 if (uuid == NULL)
255 fmd_log_dprintf(lp, "broken xref dev=%lx ino=%llx\n",
256 (ulong_t)dev, (u_longlong_t)ino);
257 else
258 fmd_log_dprintf(lp, "broken xref uuid=%s\n", uuid);
259
260 return (0);
261 }
262
263 xlp->log_flags &= ~FMD_LF_START;
264 ea_clear(&xlp->log_ea);
265 (void) lseek64(xlp->log_fd, off, SEEK_SET);
266
267 return (fmd_log_load_record(xlp,
268 iflags, &rp->rec_xrefs[rp->rec_nrefs++]));
269 }
270
271 /*
272 * fmd_log_load_xrdir is called by fmd_log_load_xrefs when the FMD_LF_XREFS bit
273 * is not yet set, indicating we haven't looked for cross-referenced files. We
274 * open the directory associated with the specified log file and attempt to
275 * perform an fmd_log_open() on every file found there (i.e. /var/fm/fmd). If
276 * we are successful, the files are chained on to lp->log_xrefs, where the
277 * fmd_log_load_xref() function can find them by comparing dev/ino to log_stat.
278 */
279 static void
fmd_log_load_xrdir(fmd_log_t * lp)280 fmd_log_load_xrdir(fmd_log_t *lp)
281 {
282 fmd_log_t *xlp;
283 char dirbuf[PATH_MAX], path[PATH_MAX], *dirpath;
284 struct dirent *dp;
285 DIR *dirp;
286 struct stat statbuf;
287
288 lp->log_flags |= FMD_LF_XREFS;
289 (void) strlcpy(dirbuf, lp->log_path, sizeof (dirbuf));
290 dirpath = dirname(dirbuf);
291
292 if ((dirp = opendir(dirpath)) == NULL)
293 return; /* failed to open directory; just skip it */
294
295 while ((dp = readdir(dirp)) != NULL) {
296 if (dp->d_name[0] == '.')
297 continue; /* skip "." and ".." and hidden files */
298
299 (void) snprintf(path, sizeof (path),
300 "%s/%s", dirpath, dp->d_name);
301
302 if (strcmp(path, lp->log_path) != 0 &&
303 stat(path, &statbuf) != -1 &&
304 (statbuf.st_mode & S_IFMT) == S_IFREG &&
305 (xlp = fmd_log_open(lp->log_abi, path, NULL)) != NULL) {
306 fmd_log_dprintf(lp, "%s loaded %s for xrefs\n",
307 lp->log_path, xlp->log_path);
308 xlp->log_xnext = lp->log_xrefs;
309 lp->log_xrefs = xlp;
310 }
311 }
312 }
313
314 /*
315 * fmd_log_load_xrefs iterates again over the record's exacct group and for
316 * each cross-reference (embedded CAT_FMA_GROUP), attempts to fill in the
317 * corresponding xref. rp->rec_nrefs is reset to the number of valid items
318 * in the finished rp->rec_xrefs array; see fmd_log_load_xref() for more info.
319 */
320 static int
fmd_log_load_xrefs(fmd_log_t * lp,uint_t iflags,fmd_log_record_t * rp)321 fmd_log_load_xrefs(fmd_log_t *lp, uint_t iflags, fmd_log_record_t *rp)
322 {
323 size_t size = sizeof (fmd_log_record_t) * rp->rec_nrefs;
324 ea_object_t *rgrp = rp->rec_grp;
325 ea_object_t *grp, *obj;
326
327 if (!(iflags & FMD_LOG_XITER_REFS))
328 return (0); /* do not load any xrefs */
329
330 if (!(lp->log_flags & FMD_LF_XREFS))
331 fmd_log_load_xrdir(lp);
332
333 if ((rp->rec_xrefs = malloc(size)) == NULL)
334 return (fmd_log_set_errno(lp, EFDL_NOMEM));
335
336 bzero(rp->rec_xrefs, size);
337 rp->rec_nrefs = 0;
338
339 /*
340 * Make a second pass through the record group to locate and process
341 * each cross-reference sub-group. The structure of the groups is
342 * as follows (left-hand-side symbols named after the variables used):
343 *
344 * rgrp := CAT_FMA_TODSEC CAT_FMA_TODNSEC CAT_FMA_NVLIST grp*
345 * grp := obj* (i.e. zero or more groups of xref items)
346 * obj := CAT_FMA_MAJOR CAT_FMA_MINOR CAT_FMA_INODE CAT_FMA_OFFSET
347 *
348 * For each xref 'obj', we call fmd_log_load_xref() to parse the four
349 * xref members and then load the specified record into rp->rec_xrefs.
350 */
351 for (grp = rgrp->eo_group.eg_objs; grp != NULL; grp = grp->eo_next) {
352 if (grp->eo_catalog != CAT_FMA_GROUP)
353 continue; /* ignore anything that isn't a group */
354
355 for (obj = grp->eo_group.eg_objs;
356 obj != NULL; obj = obj->eo_next) {
357 if (fmd_log_load_xref(lp, iflags, rp, obj) != 0)
358 return (-1); /* errno is set for us */
359 }
360 }
361
362 return (0);
363 }
364
365 static fmd_log_t *
fmd_log_open_err(fmd_log_t * lp,int * errp,int err)366 fmd_log_open_err(fmd_log_t *lp, int *errp, int err)
367 {
368 if (errp != NULL)
369 *errp = err == EFDL_EXACCT ? EFDL_EXACCT + ea_error() : err;
370
371 if (lp != NULL)
372 fmd_log_close(lp);
373
374 return (NULL);
375 }
376
377 fmd_log_t *
fmd_log_open(int abi,const char * name,int * errp)378 fmd_log_open(int abi, const char *name, int *errp)
379 {
380 ea_object_t *grp, *obj;
381 fmd_log_t *lp;
382 int fd;
383
384 if (abi > FMD_LOG_VERSION)
385 return (fmd_log_open_err(NULL, errp, EFDL_VERSION));
386
387 if ((lp = malloc(sizeof (fmd_log_t))) == NULL)
388 return (fmd_log_open_err(NULL, errp, EFDL_NOMEM));
389
390 bzero(lp, sizeof (fmd_log_t));
391
392 if ((lp->log_path = strdup(name)) == NULL)
393 return (fmd_log_open_err(lp, errp, EFDL_NOMEM));
394
395 if ((lp->log_fd = open64(name, O_RDONLY)) == -1 ||
396 fstat64(lp->log_fd, &lp->log_stat) == -1 ||
397 (fd = dup(lp->log_fd)) == -1)
398 return (fmd_log_open_err(lp, errp, errno));
399
400 if (ea_fdopen(&lp->log_ea, fd, FMD_CREATOR,
401 EO_VALID_HDR | EO_HEAD, O_RDONLY) == -1) {
402 (void) close(fd);
403 return (fmd_log_open_err(lp, errp, EFDL_EXACCT));
404 }
405
406 lp->log_abi = abi;
407 lp->log_flags |= FMD_LF_EAOPEN;
408 if (getenv("FMD_LOG_DEBUG") != NULL)
409 lp->log_flags |= FMD_LF_DEBUG;
410
411 /*
412 * Read the first group of log meta-data: the write-once read-only
413 * file header. We read all records in this group, ignoring all but
414 * the VERSION and LABEL, which are required and must be verified.
415 */
416 if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL)
417 return (fmd_log_open_err(lp, errp, EFDL_EXACCT));
418
419 if (grp->eo_catalog != CAT_FMA_GROUP) {
420 ea_free_object(grp, EUP_ALLOC);
421 return (fmd_log_open_err(lp, errp, EFDL_EXACCT));
422 }
423
424 for (obj = grp->eo_group.eg_objs; obj != NULL; obj = obj->eo_next) {
425 switch (obj->eo_catalog) {
426 case CAT_FMA_VERSION:
427 lp->log_version = strdup(obj->eo_item.ei_string);
428 if (lp->log_version == NULL) {
429 ea_free_object(grp, EUP_ALLOC);
430 return (fmd_log_open_err(lp, errp, EFDL_NOMEM));
431 }
432 break;
433 case CAT_FMA_LABEL:
434 lp->log_label = strdup(obj->eo_item.ei_string);
435 if (lp->log_label == NULL) {
436 ea_free_object(grp, EUP_ALLOC);
437 return (fmd_log_open_err(lp, errp, EFDL_NOMEM));
438 }
439 break;
440 case CAT_FMA_OSREL:
441 lp->log_osrelease = strdup(obj->eo_item.ei_string);
442 if (lp->log_osrelease == NULL) {
443 ea_free_object(grp, EUP_ALLOC);
444 return (fmd_log_open_err(lp, errp, EFDL_NOMEM));
445 }
446 break;
447 case CAT_FMA_OSVER:
448 lp->log_osversion = strdup(obj->eo_item.ei_string);
449 if (lp->log_osversion == NULL) {
450 ea_free_object(grp, EUP_ALLOC);
451 return (fmd_log_open_err(lp, errp, EFDL_NOMEM));
452 }
453 break;
454 case CAT_FMA_PLAT:
455 lp->log_platform = strdup(obj->eo_item.ei_string);
456 if (lp->log_platform == NULL) {
457 ea_free_object(grp, EUP_ALLOC);
458 return (fmd_log_open_err(lp, errp, EFDL_NOMEM));
459 }
460 break;
461 case CAT_FMA_UUID:
462 lp->log_uuid = strdup(obj->eo_item.ei_string);
463 if (lp->log_uuid == NULL) {
464 ea_free_object(grp, EUP_ALLOC);
465 return (fmd_log_open_err(lp, errp, EFDL_NOMEM));
466 }
467 break;
468 }
469 }
470
471 ea_free_object(grp, EUP_ALLOC);
472
473 if (lp->log_version == NULL || lp->log_label == NULL)
474 return (fmd_log_open_err(lp, errp, EFDL_BADHDR));
475
476 /*
477 * Read the second group of log meta-data: the table of contents. At
478 * present there are no records libfmd_log needs in here, so we just
479 * skip over this entire group so that fmd_log_xiter() starts after it.
480 */
481 if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL)
482 return (fmd_log_open_err(lp, errp, EFDL_EXACCT));
483
484 if (grp->eo_catalog != CAT_FMA_GROUP) {
485 ea_free_object(grp, EUP_ALLOC);
486 return (fmd_log_open_err(lp, errp, EFDL_EXACCT));
487 }
488
489 ea_free_object(grp, EUP_ALLOC);
490 lp->log_flags |= FMD_LF_START;
491
492 fmd_log_dprintf(lp, "open log %s dev=%lx ino=%llx\n", lp->log_path,
493 (ulong_t)lp->log_stat.st_dev, (u_longlong_t)lp->log_stat.st_ino);
494
495 return (lp);
496 }
497
498 void
fmd_log_close(fmd_log_t * lp)499 fmd_log_close(fmd_log_t *lp)
500 {
501 fmd_log_t *xlp, *nlp;
502
503 if (lp == NULL)
504 return; /* permit null lp to simply caller code */
505
506 for (xlp = lp->log_xrefs; xlp != NULL; xlp = nlp) {
507 nlp = xlp->log_xnext;
508 fmd_log_close(xlp);
509 }
510
511 if (lp->log_flags & FMD_LF_EAOPEN)
512 (void) ea_close(&lp->log_ea);
513
514 if (lp->log_fd >= 0)
515 (void) close(lp->log_fd);
516
517 free(lp->log_path);
518 free(lp->log_version);
519 free(lp->log_label);
520 free(lp->log_osrelease);
521 free(lp->log_osversion);
522 free(lp->log_platform);
523 free(lp->log_uuid);
524
525 free(lp);
526 }
527
528 const char *
fmd_log_label(fmd_log_t * lp)529 fmd_log_label(fmd_log_t *lp)
530 {
531 return (lp->log_label);
532 }
533
534 void
fmd_log_header(fmd_log_t * lp,fmd_log_header_t * hp)535 fmd_log_header(fmd_log_t *lp, fmd_log_header_t *hp)
536 {
537 const char *creator = ea_get_creator(&lp->log_ea);
538 const char *hostname = ea_get_hostname(&lp->log_ea);
539
540 hp->log_creator = creator ? creator : "";
541 hp->log_hostname = hostname ? hostname : "";
542 hp->log_label = lp->log_label ? lp->log_label : "";
543 hp->log_version = lp->log_version ? lp->log_version : "";
544 hp->log_osrelease = lp->log_osrelease ? lp->log_osrelease : "";
545 hp->log_osversion = lp->log_osversion ? lp->log_osversion : "";
546 hp->log_platform = lp->log_platform ? lp->log_platform : "";
547 if (lp->log_abi > 1)
548 hp->log_uuid = lp->log_uuid ? lp->log_uuid : "";
549 }
550
551 /*
552 * Note: this will be verrrry slow for big files. If this function becomes
553 * important, we'll need to add a function to libexacct to let us rewind.
554 * Currently libexacct has no notion of seeking other than record-at-a-time.
555 */
556 int
fmd_log_rewind(fmd_log_t * lp)557 fmd_log_rewind(fmd_log_t *lp)
558 {
559 ea_object_t obj, *grp;
560
561 if (!(lp->log_flags & FMD_LF_START)) {
562 while (ea_previous_object(&lp->log_ea, &obj) != EO_ERROR)
563 continue; /* rewind until beginning of file */
564
565 if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL)
566 return (fmd_log_set_errno(lp, EFDL_EXACCT));
567 else
568 ea_free_object(grp, EUP_ALLOC); /* hdr group */
569
570 if ((grp = ea_get_object_tree(&lp->log_ea, 1)) == NULL)
571 return (fmd_log_set_errno(lp, EFDL_EXACCT));
572 else
573 ea_free_object(grp, EUP_ALLOC); /* toc group */
574
575 lp->log_flags |= FMD_LF_START;
576 }
577
578 return (0);
579 }
580
581 static int
fmd_log_xiter_filter(fmd_log_t * lp,const fmd_log_record_t * rp,uint_t fac,const fmd_log_filtvec_t * fav)582 fmd_log_xiter_filter(fmd_log_t *lp, const fmd_log_record_t *rp,
583 uint_t fac, const fmd_log_filtvec_t *fav)
584 {
585 uint_t i, j;
586
587 for (i = 0; i < fac; i++) {
588 for (j = 0; j < fav[i].filt_argc; j++) {
589 if (fav[i].filt_argv[j].filt_func(lp, rp,
590 fav[i].filt_argv[j].filt_arg) != 0)
591 break; /* logical OR of this class is true */
592 }
593
594 if (j == fav[i].filt_argc)
595 return (0); /* logical AND of filter is false */
596 }
597
598 return (1); /* logical AND of filter is true */
599 }
600
601 static int
fmd_log_xiter_filtcmp(const void * lp,const void * rp)602 fmd_log_xiter_filtcmp(const void *lp, const void *rp)
603 {
604 return ((intptr_t)((fmd_log_filter_t *)lp)->filt_func -
605 (intptr_t)((fmd_log_filter_t *)rp)->filt_func);
606 }
607
608 int
fmd_log_filter(fmd_log_t * lp,uint_t fc,fmd_log_filter_t * fv,const fmd_log_record_t * rp)609 fmd_log_filter(fmd_log_t *lp, uint_t fc, fmd_log_filter_t *fv,
610 const fmd_log_record_t *rp)
611 {
612 fmd_log_filtvec_t *fav = alloca(fc * sizeof (fmd_log_filtvec_t));
613 uint_t i, fac = 0;
614
615 /*
616 * If a filter array was provided, create an array of filtvec structs
617 * to perform logical AND/OR processing. See fmd_log_xiter(), below.
618 */
619 bzero(fav, fc * sizeof (fmd_log_filtvec_t));
620 qsort(fv, fc, sizeof (fmd_log_filter_t), fmd_log_xiter_filtcmp);
621
622 for (i = 0; i < fc; i++) {
623 if (i == 0 || fv[i].filt_func != fv[i - 1].filt_func)
624 fav[fac++].filt_argv = &fv[i];
625 fav[fac - 1].filt_argc++;
626 }
627
628 return (fmd_log_xiter_filter(lp, rp, fac, fav));
629 }
630
631 int
fmd_log_xiter(fmd_log_t * lp,uint_t flag,uint_t fc,fmd_log_filter_t * fv,fmd_log_rec_f * rfunc,fmd_log_err_f * efunc,void * private,ulong_t * rcntp)632 fmd_log_xiter(fmd_log_t *lp, uint_t flag, uint_t fc, fmd_log_filter_t *fv,
633 fmd_log_rec_f *rfunc, fmd_log_err_f *efunc, void *private, ulong_t *rcntp)
634 {
635 fmd_log_record_t rec;
636 fmd_log_filtvec_t *fav = NULL;
637 uint_t i, fac = 0;
638 ulong_t rcnt = 0;
639 int rv = 0;
640
641 if (flag & ~FMD_LOG_XITER_MASK)
642 return (fmd_log_set_errno(lp, EINVAL));
643
644 /*
645 * If a filter array was provided, create an array of filtvec structs
646 * where each filtvec holds a pointer to an equivalent list of filters,
647 * as determined by their filt_func. We sort the input array by func,
648 * and then fill in the filtvec struct array. We can then compute the
649 * logical OR of equivalent filters by iterating over filt_argv, and
650 * we can compute the logical AND of 'fv' by iterating over filt_argc.
651 */
652 if (fc != 0) {
653 if ((fav = calloc(fc, sizeof (fmd_log_filtvec_t))) == NULL)
654 return (fmd_log_set_errno(lp, EFDL_NOMEM));
655
656 qsort(fv, fc, sizeof (fmd_log_filter_t), fmd_log_xiter_filtcmp);
657
658 for (i = 0; i < fc; i++) {
659 if (i == 0 || fv[i].filt_func != fv[i - 1].filt_func)
660 fav[fac++].filt_argv = &fv[i];
661 fav[fac - 1].filt_argc++;
662 }
663 }
664
665 lp->log_flags &= ~FMD_LF_START;
666 ea_clear(&lp->log_ea);
667
668 do {
669 if (fmd_log_load_record(lp, flag, &rec) != 0) {
670 if (lp->log_errno == EFDL_EXACCT + EXR_EOF)
671 break; /* end-of-file reached */
672 rv = efunc ? efunc(lp, private) : -1;
673 rcnt++;
674 } else {
675 if (fc == 0 || fmd_log_xiter_filter(lp, &rec, fac, fav))
676 rv = rfunc(lp, &rec, private);
677
678 fmd_log_free_record(&rec);
679 rcnt++;
680 }
681 } while (rv == 0);
682
683 if (fac != 0)
684 free(fav);
685
686 if (rcntp != NULL)
687 *rcntp = rcnt;
688
689 return (rv);
690 }
691
692 int
fmd_log_iter(fmd_log_t * lp,fmd_log_rec_f * rfunc,void * private)693 fmd_log_iter(fmd_log_t *lp, fmd_log_rec_f *rfunc, void *private)
694 {
695 return (fmd_log_xiter(lp, 0, 0, NULL, rfunc, NULL, private, NULL));
696 }
697
698 int
fmd_log_seek(fmd_log_t * lp,off64_t off)699 fmd_log_seek(fmd_log_t *lp, off64_t off)
700 {
701 lp->log_flags &= ~FMD_LF_START;
702 ea_clear(&lp->log_ea);
703
704 if (lseek64(lp->log_fd, off, SEEK_SET) != off)
705 return (fmd_log_set_errno(lp, errno));
706
707 return (0);
708 }
709
710 static const char *const _fmd_errs[] = {
711 "client requires newer version of libfmd_log", /* EFDL_VERSION */
712 "required memory allocation failed", /* EFDL_NOMEM */
713 "log header did not contain required field", /* EFDL_BADHDR */
714 "log record did not contain protocol class", /* EFDL_NOCLASS */
715 "log record has invalid catalog tag", /* EFDL_BADTAG */
716 "log record has invalid cross-reference group", /* EFDL_BADREF */
717 "log record has invalid cross-reference dev_t", /* EFDL_BADDEV */
718 "log record was not of expected type", /* EFDL_EXACCT + OK */
719 "log access system call failed", /* EXR_SYSCALL_FAIL */
720 "log file corruption detected", /* EXR_CORRUPT_FILE */
721 "end-of-file reached", /* EXR_EOF */
722 "log file does not have appropriate creator", /* EXR_NO_CREATOR */
723 "invalid unpack buffer specified", /* EXR_INVALID_BUF */
724 "invalid exacct operation for log file", /* EXR_NOTSUPP */
725 "log file requires newer version of libexacct", /* EXR_UNKN_VERSION */
726 "invalid object buffer specified", /* EXR_INVALID_OBJ */
727 };
728
729 static const int _fmd_nerr = sizeof (_fmd_errs) / sizeof (_fmd_errs[0]);
730
731 /*ARGSUSED*/
732 const char *
fmd_log_errmsg(fmd_log_t * lp,int err)733 fmd_log_errmsg(fmd_log_t *lp, int err)
734 {
735 const char *msg;
736
737 if (err >= EFDL_BASE && err - EFDL_BASE < _fmd_nerr)
738 msg = _fmd_errs[err - EFDL_BASE];
739 else
740 msg = strerror(err);
741
742 return (msg ? msg : "unknown error");
743 }
744
745 int
fmd_log_errno(fmd_log_t * lp)746 fmd_log_errno(fmd_log_t *lp)
747 {
748 return (lp->log_errno);
749 }
750