/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2012 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
*/
#include <alloca.h>
#include <unistd.h>
#include <limits.h>
#include <strings.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <ctype.h>
#include <regex.h>
#include <dirent.h>
#include <pthread.h>
#include <fmdump.h>
#define FMDUMP_EXIT_SUCCESS 0
const char *g_pname;
char *g_root;
/*PRINTFLIKE2*/
void
{
g_errs++;
}
}
void
{
g_errs++;
}
/*PRINTFLIKE1*/
void
{
}
static void
{
}
/*PRINTFLIKE1*/
static void
{
}
/*PRINTFLIKE1*/
static void
{
}
char *
{
fmdump_warn("record time is too large for 32-bit utility\n");
} else {
} else {
}
}
return (buf);
}
char *
{
#ifdef _ILP32
fmdump_warn("record time is too large for 32-bit utility\n");
} else {
#endif
#ifdef _ILP32
}
#endif
return (buf);
}
/* BEGIN CSTYLED */
static const char *synopsis =
"Usage: %s [[-e | -i | -I] | -A ] [-f] [-mvVp] [-c class] [-R root]\n"
"\t [-t time ][-T time] [-u uuid] [-n name[.name]*[=value]] "
"[file]...\n "
"Log selection: [-e | -i | -I] or one [file]; default is the fault log\n"
"\t-e display error log content\n"
"\t-i display infolog content\n"
"\t-I display the high-value-infolog content\n"
"\t-R set root directory for pathname expansions\n "
"Command behaviour:\n"
"\t-A Aggregate specified [file]s or, if no [file], all known logs\n"
"\t-f follow growth of log file by waiting for additional data\n "
"Output options:\n"
"\t-m display human-readable messages (only for fault logs)\n"
"\t-v set verbose mode: display additional event detail\n"
"\t-V set very verbose mode: display complete event contents\n"
"\t-p Used with -V: apply some output prettification\n"
"\t-j Used with -V: emit JSON-formatted output\n "
"Selection filters:\n"
"\t-c select events that match the specified class\n"
"\t-t select events that occurred after the specified time\n"
"\t-T select events that occurred before the specified time\n"
"\t-u select events that match the specified diagnosis uuid\n"
"\t-n select events containing named nvpair (with matching value)\n";
/* END CSTYLED */
static int
{
return (FMDUMP_EXIT_USAGE);
}
/*ARGSUSED*/
static int
{
fmdump_warn("skipping record: %s\n",
return (0);
}
/*
* Yet another disgusting argument parsing function (TM). We attempt to parse
* a time argument in a variety of strptime(3C) formats, in which case it is
* interpreted as a local time and is converted to a timeval using mktime(3C).
* If those formats fail, we look to see if the time is a decimal integer
* followed by one of our magic suffixes, in which case the time is interpreted
* as a time delta *before* the current time-of-day (i.e. "1h" = "1 hour ago").
*/
static struct timeval *
{
const struct {
const char *name;
} suffix[] = {
{ NULL }
};
char *p;
fmdump_fatal("failed to allocate memory");
fmdump_fatal("failed to get tod");
/*
* First try a variety of strptime() calls. If these all fail, we'll
* try parsing an integer followed by one of our suffix[] strings.
*/
int i;
errno = 0;
break;
}
}
fmdump_usage("time delta precedes UTC time origin "
"-- %s\n", arg);
} else if (*p == '\0' || *p == '.') {
/*
* If tm_year is zero, we matched [%b %d] %H:%M[:%S]; use
* the result of localtime(&tod.tv_sec) to fill in the rest.
*/
if (d > 0) {
}
}
errno = 0;
/*
* If our mktime() set tm_isdst, adjust the result for DST by
* subtracting the offset between the main and alternate zones.
*/
if (p[0] == '.') {
arg = p;
errno = 0;
fmdump_usage("illegal time suffix -- .%s\n",
arg);
}
} else {
}
return (tvp);
}
/*
* If the -u option is specified in combination with the -e option, we iterate
* over each record in the fault log with a matching UUID finding xrefs to the
* error log, and then use this function to iterate over every xref'd record.
*/
int
{
int i, rv = 0;
}
return (rv);
}
int
{
}
/*
* Initialize fmd_log_filter_nvarg_t from -n name=value argument string.
*/
static fmd_log_filter_nvarg_t *
{
char *value;
int rv;
value_regex = NULL;
} else {
/*
* Skip white space before value to facilitate direct
*/
value++;
fmdump_fatal("failed to allocate memory");
/* compile regular expression for possible string match */
REG_NOSUB|REG_NEWLINE)) != 0) {
sizeof (errstr));
fmdump_usage("unexpected regular expression in "
}
}
fmdump_fatal("failed to allocate memory");
return (argt);
}
/*
* If the -a option is not present, filter out fault records that correspond
* to events that the producer requested not be messaged for administrators.
*/
/*ARGSUSED*/
int
{
char *class;
/*
* If -A was used then apply this filter only to events of list class
*/
if (opt_A) {
sizeof (FM_LIST_EVENT)) != 0)
return (1);
}
}
struct loglink {
char *path;
long suffix;
};
static void
{
char *str;
fmdump_fatal("failed to allocate memory");
}
/*
* Find and return all the rotated logs.
*/
static struct loglink *
{
*logname++ = '\0';
g_errs++;
return (NULL);
}
/*
* Search the log directory for logs named "<logname>.0",
* "<logname>.1", etc and add to the link in the
* reverse numeric order.
*/
continue;
/*
* "*.0-" file normally should not be seen. It may
* exist when user manually run 'fmadm rotate'.
* In such case, we put it at the end of the list so
* it'll be dumped after all the rotated logs, before
* the current one.
*/
}
return (head);
}
/*
* Aggregate log files. If ifiles is not NULL then one or more files
* were listed on the command line, and we will merge just those files.
* Otherwise we will merge all known log file types, and include the
* rotated logs for each type (you can suppress the inclusion of
* some logtypes through use of FMDUMP_AGGREGATE_IGNORE in the process
* log filenames to ignore).
*
* We will not attempt to perform a chronological sort across all log records
* of all files. Indeed, we won't even sort individual log files -
* we will not re-order events differently to how they appeared in their
* original log file. This is because log files are already inherently
* ordered by the order in which fmd receives and processes events.
* So we determine the output order by comparing the "next" record
* off the top of each log file.
*
* We will construct a number of log record source "pipelines". As above,
* the next record to render in the overall output is that from the
* pipeline with the oldest event.
*
* For the case that input logfiles were listed on the command line, each
* pipeline will process exactly one of those logfiles. Distinct pipelines
* may process logfiles of the same "type" - eg if two "error" logs and
* one "fault" logs are specified then there'll be two pipelines producing
* events from "error" logs.
*
* If we are merging all known log types then we will construct exactly
* one pipeline for each known log type - one for error, one for fault, etc.
* Each pipeline will process first the rotated logs of that type and then
* move on to the current log of that type.
*
* The output from all pipelines flows into a serializer which selects
* the next record once all pipelines have asserted their output state.
* The output state of a pipeline is one of:
*
* - record available: the next record from this pipeline is available
* for comparison and consumption
*
* - done: this pipeline will produce no more records
*
* - polling: this pipeline is polling for new records and will
*
* - processing: output state will be updated shortly
*
* A pipeline iterates over each file queued to it using fmd_log_xiter.
* We do this in a separate thread for each pipeline. The callback on
* each iteration must update the serializer to let it know that
* a new record is available. In the serializer thread we decide whether
* we have all records expected have arrived and it is time to choose
* the next output record.
*/
/*
* A pipeline descriptor. The pl_cv condition variable is used together
* with pl_lock for initial synchronisation, and thereafter with the
* lock for the serializer for pausing and continuing this pipeline.
*/
struct fmdump_pipeline {
};
enum fmdump_pipestate {
};
/*
* Each pipeline has an associated output slot in the serializer. This
* must be updated with the serializer locked. After update evaluate
* whether there are enough slots decided that we should select a
* record to output.
*/
struct fmdump_srlzer_slot {
};
/*
* All pipelines are linked to a single serializer. The serializer
* structure must be updated under the ds_lock; this mutex is also
* paired with the pl_cv of individual pipelines (one mutex, many condvars)
* in pausing and continuing individual pipelines.
*/
struct fmdump_srlzer {
};
/*
* All known log types. When aggregation is requested an no file list
* is provided we will process the logs identified here (if lt_enabled
* is true and not over-ridden by environment settings). We also
* use this in determining the appropriate ops structure for each distinct
* label.
*/
static struct fmdump_logtype {
} logtypes[] = {
{
"error",
"errlog",
},
{
"fault",
"fltlog",
},
{
"info",
"infolog",
},
{
"info",
"infolog_hival",
},
{
"asru",
B_FALSE, /* not included unless in file list */
NULL,
&fmdump_asru_ops /* but we need ops when it is */
}
};
/*
* Disable logtypes per environment setting. Does not apply when a list
* of logs is provided on the command line.
*/
static void
do_disables(void)
{
int i;
return;
continue;
}
}
}
}
static void
{
}
static void
{
}
static struct fmdump_pipeline *
{
int i;
i++, slot++) {
continue;
if (first) {
oldestidx = i;
first = 0;
continue;
}
oldestidx = i;
}
}
}
static void
{
}
static void
{
}
/*
* Called on each pipeline record iteration to make a new record
* available for input to the serializer. Returns 0 to indicate that
* the caller must stall the pipeline, or 1 to indicate that the
* caller should go ahead and render their record. If this record
* addition fills the serializer then choose a pipeline that must
* render output.
*/
static int
{
/*
* Once all pipelines are polling we just render in arrival order.
*/
return (1);
/*
* If not all pipelines have asserted an output yet then the
* caller must block.
*/
return (0);
/*
* Right so it's time to turn the crank by choosing which of the
* filled line of slots should produce output. If it is the slot
* for our caller then return their index to them, otherwise return
* -1 to the caller to make them block and cv_signal the winner.
*/
return (1);
/* Wake the oldest, and return 0 to put the caller to sleep */
return (0);
}
static void
{
}
static void
{
}
static void
{
return;
}
static int
{
g_errs++;
return (0);
}
static int
{
int rc;
return (rc);
}
static void
{
int err;
int i;
fmdump_warn("failed to open %s: %s\n",
g_errs++;
return;
}
break;
}
}
fmdump_warn("unknown log type %s for %s\n",
g_errs++;
return;
}
do {
NULL) != 0) {
fmdump_warn("failed to dump %s: %s\n",
g_errs++;
return;
}
if (follow) {
(void) sleep(1);
}
} while (follow);
}
static void *
{
return (NULL);
}
static int
{
int fmt;
int i;
if (!pipeline)
fmdump_fatal("failed to allocate memory");
for (i = 0; i < n_ifiles; i++)
} else {
sizeof (struct fmdump_pipeline));
if (!pipeline)
fmdump_fatal("failed to allocate memory");
do_disables();
npipe = 0;
char *logpath;
continue;
fmdump_fatal("failed to allocate memory");
}
}
if (opt_V)
else if (opt_v)
fmt = FMDUMP_VERB1;
else
fmt = FMDUMP_SHORT;
fmdump_fatal("failed to allocate memory");
pl->pl_srlzeridx = i;
pipeline_thr, (void *)pl) != 0)
fmdump_fatal("pthread_create for pipeline %d failed",
i);
}
while (!pl->pl_started)
}
for (i = 0; i < npipe; i++)
}
return (FMDUMP_EXIT_SUCCESS);
}
static void
{
int i;
return;
for (i = 0; i < n_ifiles; i++) {
}
}
}
int
{
int opt_A = 0;
int n_ifiles;
int ifileidx = 0;
int iflags = 0;
void *farg;
int c, err;
while ((c =
switch (c) {
case 'A':
opt_A++;
break;
case 'a':
opt_a++;
break;
case 'c':
break;
case 'e':
if (opt_i)
opt_e++;
break;
case 'f':
opt_f++;
break;
case 'H':
opt_H++;
break;
case 'i':
opt_i++;
break;
case 'I':
opt_I++;
break;
case 'j':
if (opt_p)
opt_j++;
break;
case 'm':
opt_m++;
break;
case 'O':
break;
case 'p':
if (opt_j)
opt_p++;
break;
case 'R':
break;
case 't':
break;
case 'T':
break;
case 'u':
opt_u++;
opt_a++; /* -u implies -a */
break;
case 'n': {
break;
}
case 'v':
opt_v++;
break;
case 'V':
opt_V++;
break;
default:
}
}
fmdump_usage("-A excludes all of "
"-e, -i, -I, -m and -u\n");
char *dest;
"failed to allocate memory for "
"%d input file%s", n_ifiles,
}
}
fmdump_usage("illegal argument -- %s\n",
fmdump_fatal("failed to allocate memory");
}
}
if (opt_A) {
int rc;
if (!opt_a) {
}
return (rc);
} else {
fmdump_fatal("failed to allocate memory");
} else {
}
}
if (*ifile == '\0') {
pfx = "flt";
sfx = "";
} else {
if (opt_e) {
pfx = "err";
sfx = "";
} else {
pfx = "info";
}
}
/*
* logadm may rotate the logs. When no input file is specified,
* we try to dump all the rotated logs as well in the right
* order.
*/
fmdump_usage("-R option is not appropriate "
"when file operand is present\n");
}
fmdump_fatal("failed to initialize libfmd_msg");
}
if (opt_H) {
return (FMDUMP_EXIT_SUCCESS);
}
}
ops = &fmdump_err_ops;
ops = &fmdump_flt_ops;
ops = &fmdump_asru_ops;
ops = &fmdump_info_ops;
else
ops = &fmdump_err_ops;
}
if (opt_V) {
} else if (opt_v) {
} else if (opt_m) {
} else
fmdump_usage("-m mode is not supported for "
}
if (iflags & FMD_LOG_XITER_OFFS)
} else {
}
if (iflags & FMD_LOG_XITER_OFFS) {
}
== NULL) {
fmdump_warn("failed to open %s: %s\n",
g_errs++;
continue;
}
recs = 0;
g_errs++;
}
}
do {
recs = 0;
g_errs++;
}
if (opt_f)
(void) sleep(1);
} while (opt_f);
else
}