Exemplo n.º 1
0
static int SelectProcTimeCounterRangeMatch(char *name1, char *name2, time_t min, time_t max, char **names, char **line)
{
    if ((min == CF_NOINT) || (max == CF_NOINT))
    {
        return false;
    }

    int i = GetProcColumnIndex(name1, name2, names);
    if (i != -1)
    {
        time_t value = (time_t) TimeCounter2Int(line[i]);

        if (value == CF_NOINT)
        {
            Log(LOG_LEVEL_INFO,
                "Failed to extract a valid integer from %c => '%s' in process list",
                name1[i], line[i]);
            return false;
        }

        if ((min <= value) && (value <= max))
        {
            Log(LOG_LEVEL_VERBOSE, "Selection filter matched counter range"
                " '%s/%s' = '%s' in [%jd,%jd] (= %jd secs)",
                  name1, name2, line[i], (intmax_t)min, (intmax_t)max, (intmax_t)value);
            return true;
        }
        else
        {
            LogDebug(LOG_MOD_PS, "Selection filter REJECTED counter range"
                     " '%s/%s' = '%s' in [%jd,%jd] (= %jd secs)",
                     name1, name2, line[i],
                     (intmax_t) min, (intmax_t) max, (intmax_t) value);
            return false;
        }
    }

    return false;
}
Exemplo n.º 2
0
static int SelectProcTimeCounterRangeMatch(char *name1, char *name2, time_t min, time_t max, char **names, char **line)
{
    int i;
    time_t value;

    if (min == CF_NOINT || max == CF_NOINT)
    {
        return false;
    }

    if ((i = GetProcColumnIndex(name1, name2, names)) != -1)
    {
        value = (time_t) TimeCounter2Int(line[i]);

        if (value == CF_NOINT)
        {
            CfOut(cf_inform, "", "Failed to extract a valid integer from %c => \"%s\" in process list\n", name1[i],
                  line[i]);
            return false;
        }

        if (min <= value && value <= max)
        {
            CfOut(cf_verbose, "", "Selection filter matched counter range %s/%s = %s in [%ld,%ld] (= %ld secs)\n",
                  name1, name2, line[i], min, max, value);
            return true;
        }
        else
        {
            CfDebug("Selection filter REJECTED counter range %s/%s = %s in [%ld,%ld] (= %ld secs)\n", name1, name2,
                    line[i], min, max, value);
            return false;
        }
    }

    return false;
}
Exemplo n.º 3
0
static void MaybeFixStartTime(const char *line,
                              time_t pstime,
                              char **names,
                              char **fields)
{
    /* Since start times can be very imprecise (e.g. just a past day's
     * date, or a past year), calculate a better value from elapsed
     * time, if available: */
    int k = GetProcColumnIndex("ELAPSED", "ELAPSED", names);
    if (k != -1)
    {
        const long elapsed = TimeCounter2Int(fields[k]);
        if (elapsed != CF_NOINT) /* Only use if parsed successfully ! */
        {
            int j = GetProcColumnIndex("STIME", "START", names), ns[3];
            /* Trust the reported value if it matches hh:mm[:ss], though: */
            if (sscanf(fields[j], "%d:%d:%d", ns, ns + 1, ns + 2) < 2)
            {
                time_t value = pstime - (time_t) elapsed;

                Log(LOG_LEVEL_DEBUG,
                    "processes: Replacing parsed start time %s with %s",
                    fields[j], ctime(&value));

                free(fields[j]);
                xasprintf(fields + j, "%ld", value);
            }
        }
        else if (fields[k])
        {
            Log(LOG_LEVEL_VERBOSE,
                "Parsing problem was in ELAPSED field of '%s'",
                line);
        }
    }
}
static int SplitProcLine(const char *proc, time_t pstime,
                         char **names, int *start, int *end,
                         char **line)
{
    if (proc == NULL || proc[0] == '\0')
    {
        return false;
    }

    memset(line, 0, sizeof(char *) * CF_PROCCOLS);

    int prior = -1; /* End of last header-selected field. */
    const size_t linelen = strlen(proc);
    const char *sp = proc; /* Just after last space-separated field. */
    /* Scan in parallel for two heuristics: space-delimited fields
     * found using sp, and ones inferred from the column headers. */

    /* TODO: we test with isspace() mostly, which makes sense as TAB
     * might be used to enable field alignment; but we make no attempt
     * to account for the alignment offsets this may introduce, that
     * would mess up the heuristic based on column headers.  Hard to
     * do robustly, as we'd need to assume a tab-width and apply the
     * same accounting to the headers (where start and end are indices
     * of bytes, we'd need a third array for tab-induced offsets
     * between headers). */

    for (int i = 0; i < CF_PROCCOLS && names[i] != NULL; i++)
    {
        /* Space-delimited heuristic, from sp to just before ep: */
        while (isspace((unsigned char) sp[0]))
        {
            sp++;
        }
        const char *ep = sp;

        /* Header-driven heuristic, field from proc[s] to proc[e].
         * Start with the column header's position and maybe grow
         * outwards. */
        int s = start[i], e;

        /* If the previous field over-spilled into this one, our start
         * may be under our header, not directly below the header's
         * start; but only believe this if the earlier field is
         * followed by one space and the subsequent field does start
         * under our header.  Otherwise, allow that the prior field
         * may be abutting this field, or this field may have
         * overflowed left into the prior field causing the prior to
         * (mistakenly) think it includes the present. */
        if (s <= prior &&
            prior + 2 <= end[i] &&
            proc[prior + 1] == ' ' &&
            proc[prior + 2] != '\0' &&
            !isspace((unsigned char) proc[prior + 2]))
        {
            s = prior + 2;
        }

        if (i + 1 == CF_PROCCOLS || names[i + 1] == NULL)
        {
            e = linelen - 1;
            /* Extend space-delimited field to line end: */
            while (ep[0] && ep[0] != '\n')
            {
                ep++;
            }
            /* But trim trailing hspace: */
            while (isspace((unsigned char)ep[-1]))
            {
                ep--;
            }
        }
        else
        {
            e = end[i];

            /* It's possible the present field has been shunted left
             * to make way for an over-sized number or date to the
             * right.  This can confuse the numeric check below, on
             * which left-overspill is conditioned - it fails to
             * recognise this field as numeric, so doesn't check for
             * it overspilling to the left.  Look to see whether we
             * should move e left a bit: this is a conservative check,
             * that'll be revisited below. */
            int back = start[i + 1];
            while (back > s && !isspace((unsigned char) proc[back]))
            {
                back--;
            }
            if (back > s && back <= e &&
                /* back > s implies isspace(proc[back]), with no
                 * further space before the next field; if this is a
                 * single space, it's credibly the separator between
                 * our field, shunted left, and this over-spilled
                 * field: */
                proc[back] == ' ' &&
                !isspace((unsigned char) proc[back - 1]))
            {
                /* So we have a non-empty field that ends under our
                 * header but before e; adjust e. */
                e = back - 1;
            }

            /* Extend space-delimited to next space: */
            while (ep[0] && !isspace((unsigned char)ep[0]))
            {
                ep++;
            }
        }
        /* ep points at the space (or '\0') *following* the word or
         * final field. */

        /* Some ps stimes may contain spaces, e.g. "Jan 25" */
        if (strcmp(names[i], "STIME") == 0 &&
            ep - sp == 3)
        {
            const char *np = ep;
            while (isspace((unsigned char) np[0]))
            {
                np++;
            }
            if (isdigit((unsigned char) np[0]))
            {
                do
                {
                    np++;
                } while (isdigit((unsigned char) np[0]));
                ep = np;
            }
        }

        /* Numeric columns, including times, are apt to grow leftwards
         * and be right-aligned; identify candidates for this by
         * proc[e] being a digit.  Text columns are typically
         * left-aligned and may grow rightwards; identify candidates
         * for this by proc[s] being alphabetic.  Some columns shall
         * match both (e.g. STIME).  Some numeric columns may grow
         * left even as far as under the heading of the next column
         * (seen with ps -fel's SZ spilling left into ADDR's space on
         * Linux).  While widening, it's OK to include a stray space;
         * we'll trim that afterwards.  Overspill from neighbouring
         * columns can muck up alignment, so "digit" means any
         * character that can appear in a numeric field (we may need
         * to tweak "alphabetic" likewise for text fields; the user
         * field can, for example, have a '+' appended). */
        bool overspilt = false;

        /* Numeric fields may include [-.:] and perhaps other
         * punctuators: */
#define IsNumberish(c) (isdigit((unsigned char)(c)) || ispunct((unsigned char)(c)))

        /* Right-aligned numeric: move s left until we run outside the
         * field or find space. */
        if (IsNumberish(proc[e]))
        {
            bool number = i > 0; /* Should we check for under-spill ? */
            int outer = number ? end[i - 1] + 1 : 0;
            while (s >= outer && !isspace((unsigned char) proc[s]))
            {
                if (number && !IsNumberish(proc[s]))
                {
                    number = false;
                }
                s--;
            }

            /* Numeric field might overspill under previous header: */
            if (s < outer)
            {
                int spill = s;
                s = outer; /* By default, don't overlap previous column. */

                if (number && IsNumberish(proc[spill]))
                {
                    outer = start[i - 1];
                    /* Explore all the way to the start-column of the
                     * previous field's header.  If we get there, in
                     * digits-and-punctuation, we've got two numeric
                     * fields that abut; we can't do better than assume
                     * the boundary is under the right end of the previous
                     * column label (which is what our parsing of the
                     * previous column assumed).  So leave s where it is,
                     * just after the previous field's header's
                     * end-column. */

                    while (spill > outer)
                    {
                        spill--;
                        if (!IsNumberish(proc[spill]))
                        {
                            s = spill + 1; /* Confirmed overlap. */
                            break;
                        }
                    }
                }
            }

            overspilt = IsNumberish(proc[e + 1]);
        }
#undef IsNumberish

        bool abut = false;
        /* Left-aligned text or numeric misaligned by overspill; move
         * e right (if there's any right to move into; last column
         * already reaches end of proc): */
        if (proc[e + 1] &&
            (overspilt || isalpha((unsigned char) proc[s])))
        {
            assert(i + 1 < CF_PROCCOLS && names[i + 1]);
            int outer = start[i + 1]; /* Start of next field's header. */
            int beyond = end[i + 1]; /* End of next field's header. */
            if (beyond > linelen)
            {
                /* Command is shorter than its header. */
                assert(i + 2 >= CF_PROCCOLS || NULL == names[i + 2]);
                beyond = linelen;
            }
            assert(beyond >= outer);

            int out = e;
            do
            {
                out++;
            } while (out < beyond && !isspace((unsigned char) proc[out]));

            if (out < outer)
            {
                /* Simple extension to the right, no overlap: we're on
                 * a space just before the next field's header. */
                e = out - 1;
            }
            else if (out == beyond)
            {
                /* This looks like we're actually looking at the next
                 * field over-spilling left so far that it reaches
                 * under our header; but we did previously check for
                 * this and amend e left-wards if it's credible; so
                 * we're now looking at a case where it isn't;
                 * probably our columns abut, with no space in
                 * between.  The best we can do is include the text
                 * between columns as part of both columns :-( */
                abut = true;
                prior = e; /* Before adjusting it: */
                e = outer - 1;
            }
            else
            {
                /* Our word appears to over-spill under the next
                 * header.  See if that's credible.  If the next is a
                 * left-aligned field, it'll follow ours after exactly
                 * a simple space.  If it's right-aligned (numeric),
                 * it'll still follow after a single space unless it
                 * has enough space to fit under its header after more
                 * than one space. */

                assert(out < beyond && isspace((unsigned char) proc[out]));
                if ((proc[out] == ' ' && proc[out + 1] &&
                     !isspace((unsigned char) proc[out + 1])) ||
                    (isdigit((unsigned char) proc[beyond]) &&
                     isspace((unsigned char) proc[beyond + 1])))
                {
                    e = out - 1;
                }
                else
                {
                    e = outer - 1;
                }
            }
        }
        if (!abut)
        {
            prior = e;
        }

        /* Strip off any leading and trailing spaces: */
        while (isspace((unsigned char) proc[s]))
        {
            s++;
        }
        /* ... but stop if the field is empty ! */
        while (s <= e && isspace((unsigned char) proc[e]))
        {
            e--;
        }

        /* Grumble if the two heuristics don't agree: */
        size_t wordlen = ep - sp;
        if (e < s ? ep > sp : (sp != proc + s || ep != proc + e + 1))
        {
            char word[CF_SMALLBUF];
            if (wordlen >= CF_SMALLBUF)
            {
                wordlen = CF_SMALLBUF - 1;
            }
            memcpy(word, sp, wordlen);
            word[wordlen] = '\0';

            char column[CF_SMALLBUF];
            if (s <= e)
            {
                /* Copy proc[s through e] inclusive:  */
                const size_t len = MIN(1 + e - s, CF_SMALLBUF - 1);
                memcpy(column, proc + s, len);
                column[len] = '\0';
            }
            else
            {
                column[0] = '\0';
            }

            Log(LOG_LEVEL_VERBOSE,
                "Unreliable fuzzy parsing of ps output (%s) %s: '%s' != '%s'",
                proc, names[i], word, column);
        }

        /* Fall back on word if column got an empty answer: */
        line[i] = e < s ? xstrndup(sp, ep - sp) : xstrndup(proc + s, 1 + e - s);
        sp = ep;
    }

    /* Since start times can be very imprecise (e.g. just a past day's
     * date, or a past year), calculate a better value from elapsed
     * time, if available: */
    int k = GetProcColumnIndex("ELAPSED", "ELAPSED", names);
    if (k != -1)
    {
        const long elapsed = TimeCounter2Int(line[k]);
        if (elapsed != CF_NOINT) /* Only use if parsed successfully ! */
        {
            int j = GetProcColumnIndex("STIME", "START", names), ns[3];
            /* Trust the reported value if it matches hh:mm[:ss], though: */
            if (sscanf(line[j], "%d:%d:%d", ns, ns + 1, ns + 2) < 2)
            {
                time_t value = pstime - (time_t) elapsed;

                Log(LOG_LEVEL_DEBUG,
                    "SplitProcLine: Replacing parsed start time %s with %s",
                    line[j], ctime(&value));

                free(line[j]);
                xasprintf(line + j, "%ld", value);
            }
        }
    }

    return true;
}
Exemplo n.º 5
0
static int SplitProcLine(const char *proc,
                         char **names, int *start, int *end,
                         char **line)
{
    if (proc == NULL || proc[0] == '\0')
    {
        return false;
    }

    memset(line, 0, sizeof(char *) * CF_PROCCOLS);

    const char *sp = proc;
    /* Scan in parallel for two heuristics: space-delimited fields
     * found using sp, and ones inferred from the column headers. */

    for (int i = 0; i < CF_PROCCOLS && names[i] != NULL; i++)
    {
        /* Space-delimited heuristic, from sp to just before ep: */
        while (isspace((unsigned char) sp[0]))
        {
            sp++;
        }
        const char *ep = sp;

        /* Header-driven heuristic, field from proc[s] to proc[e].
         * Start with the column header's position and maybe grow
         * outwards. */
        int s = start[i], e;
        if (i + 1 == CF_PROCCOLS || names[i + 1] == NULL)
        {
            e = strlen(proc) - 1;
            /* Extend space-delimited field to line end: */
            while (ep[0] && ep[0] != '\n')
            {
                ep++;
            }
            /* But trim trailing hspace: */
            while (isspace((unsigned char)ep[-1]))
            {
                ep--;
            }
        }
        else
        {
            e = end[i];
            /* Extend space-delimited to next space: */
            while (ep[0] && !isspace((unsigned char)ep[0]))
            {
                ep++;
            }
        }
        /* ep points at the space (or '\0') *following* the word or
         * final field. */

        /* Some ps stimes may contain spaces, e.g. "Jan 25" */
        if (strcmp(names[i], "STIME") == 0 &&
            ep - sp == 3)
        {
            const char *np = ep;
            while (isspace((unsigned char) np[0]))
            {
                np++;
            }
            if (isdigit((unsigned char) np[0]))
            {
                do
                {
                    np++;
                }
                while (isdigit((unsigned char) np[0]));
                ep = np;
            }
        }

        /* Numeric columns, including times, are apt to grow leftwards
         * and be right-aligned; identify candidates for this by
         * proc[e] being a digit.  Text columns are typically
         * left-aligned and may grow rightwards; identify candidates
         * for this by proc[s] being alphabetic.  Some columns shall
         * match both (e.g. STIME).  Some numeric columns may grow
         * left even as far as under the heading of the next column
         * (seen with ps -fel's SZ spilling left into ADDR's space on
         * Linux).  While widening, it's OK to include a stray space;
         * we'll trim that afterwards.  Overspill from neighbouring
         * columns can muck up alignment, so "digit" means any
         * character that can appear in a numeric field (we may need
         * to tweak "alphabetic" likewise for text fields; the user
         * field can, for example, have a '+' appended). */
        bool overspilt = false;

        /* Numeric fields may include [-.:] and perhaps other
         * punctuators: */
#define IsNumberish(c) (isdigit((unsigned char)(c)) || ispunct((unsigned char)(c)))

        /* Right-aligned numeric: move s left until we run outside the
         * field or find space. */
        if (IsNumberish(proc[e]))
        {
            bool number = i > 0; /* Should we check for under-spill ? */
            int outer = number ? end[i - 1] + 1 : 0;
            while (s >= outer && !isspace((unsigned char) proc[s]))
            {
                if (number && !IsNumberish(proc[s]))
                {
                    number = false;
                }
                s--;
            }

            /* Numeric field might overspill under previous header: */
            if (s < outer)
            {
                int spill = s;
                s = outer; /* By default, don't overlap previous column. */

                if (number && IsNumberish(proc[spill]))
                {
                    outer = start[i - 1];
                    /* Explore all the way to the start-column of the
                     * previous field's header.  If we get there, in
                     * digits-and-punctuation, we've got two numeric
                     * fields that abut; we can't do better than assume
                     * the boundary is under the right end of the previous
                     * column label (which is what our parsing of the
                     * previous column assumed).  So leave s where it is,
                     * just after the previous field's header's
                     * end-column. */

                    while (spill > outer)
                    {
                        spill--;
                        if (!IsNumberish(proc[spill]))
                        {
                            s = spill + 1; /* Confirmed overlap. */
                            break;
                        }
                    }
                }
            }

            overspilt = IsNumberish(proc[e + 1]);
        }
#undef IsNumberish

        /* Left-aligned text or numeric misaligned by overspill;
         * move e right (but never under next heading): */
        if (overspilt || isalpha((unsigned char) proc[s]))
        {
            int outer;
            if (i + 1 < CF_PROCCOLS && names[i + 1])
            {
                outer = start[i + 1];
            }
            else
            {
                outer = strlen(proc);
            }
            /* Simplify loop condition; we want e to end *before* next
             * field, not on its first byte (or the terminator): */
            outer--;

            while (e < outer && !isspace((unsigned char) proc[e]))
            {
                e++;
            }
        }

        /* Strip off any leading and trailing spaces: */
        while (isspace((unsigned char) proc[s]))
        {
            s++;
        }
        /* ... but stop if the field is empty ! */
        while (s <= e && isspace((unsigned char) proc[e]))
        {
            e--;
        }

        /* Grumble if the two heuristics don't agree: */
        size_t wordlen = ep - sp;
        if (e < s ? ep > sp : (sp != proc + s || ep != proc + e + 1))
        {
            char word[CF_SMALLBUF];
            if (wordlen >= CF_SMALLBUF)
            {
                wordlen = CF_SMALLBUF - 1;
            }
            memcpy(word, sp, wordlen);
            word[wordlen] = '\0';

            char column[CF_SMALLBUF];
            if (s <= e)
            {
                /* Copy proc[s through e] inclusive:  */
                const size_t len = MIN(1 + e - s, CF_SMALLBUF - 1);
                memcpy(column, proc + s, len);
                column[len] = '\0';
            }
            else
            {
                column[0] = '\0';
            }

            Log(LOG_LEVEL_INFO,
                "Unacceptable model uncertainty examining process(%s): '%s' != '%s'",
                proc, word, column);
        }

        /* Fall back on word if column got an empty answer: */
        line[i] = e < s ? xstrndup(sp, ep - sp) : xstrndup(proc + s, 1 + e - s);
        sp = ep;
    }

    /* Since start times can be very imprecise (e.g. just a past day's
     * date, or a past year), calculate a better value from elapsed
     * time, if available: */
    int k = GetProcColumnIndex("ELAPSED", "ELAPSED", names);
    if (k != -1)
    {
        const long elapsed = TimeCounter2Int(line[k]);
        if (elapsed != CF_NOINT) /* Only use if parsed successfully ! */
        {
            int j = GetProcColumnIndex("STIME", "START", names), ns[3];
            /* Trust the reported value if it matches hh:mm[:ss], though: */
            if (sscanf(line[j], "%d:%d:%d", ns, ns + 1, ns + 2) < 2)
            {
                /* TODO: use time of ps-run, not time(NULL), which may be later. */
                time_t value = time(NULL) - (time_t) elapsed;

                Log(LOG_LEVEL_DEBUG,
                    "SplitProcLine: Replacing parsed start time %s with %s",
                    line[j], ctime(&value));

                free(line[j]);
                xasprintf(line + j, "%ld", value);
            }
        }
    }

    return true;
}