From 002742ad5b1fd76d539c2fb8736622e63e757f98 Mon Sep 17 00:00:00 2001 From: bryan Date: Wed, 26 Aug 2026 11:47:10 +0200 Subject: Initial commit --- prtmib.c | 1655 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1655 insertions(+) create mode 100644 prtmib.c (limited to 'prtmib.c') diff --git a/prtmib.c b/prtmib.c new file mode 100644 index 0000000..e56e571 --- /dev/null +++ b/prtmib.c @@ -0,0 +1,1655 @@ +/* prtmib — SNMPv2c printer marker dump. See LICENSE and README. */ + +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +/* winsock2.h before windows.h or the latter includes winsock.h. */ +# include +# include +# include +# include +#else +# include +# include +# include +# include +# include +# include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include + +#define LEN(x) (sizeof(x) / sizeof((x)[0])) + +#ifdef _WIN32 +typedef SOCKET Sock; +# define sockclose(s) closesocket(s) +#else +typedef int Sock; +# define sockclose(s) close(s) +# ifndef INVALID_SOCKET +# define INVALID_SOCKET (-1) +# endif +#endif + +enum { + OIDMAX = 24, + MAXVAR = 40, + MAXPRT = 64, + PKTMAX = 2048, + RECVMAX = 8192, + HOSTW = 22, + TONW = 7, + TONCOL = 4 * TONW + 3, + SRCW = 3 +}; + +enum { + F_XRX = 1 << 0 +}; + +enum { + ST_WAIT = 0, + ST_OK, + ST_TIMEOUT, + ST_DNS, + ST_BADPDU +}; + +enum { + BER_INT = 0x02, + BER_BITSTR = 0x03, + BER_OCTET = 0x04, + BER_NULL = 0x05, + BER_OID = 0x06, + BER_ENUM = 0x0A, + BER_SEQ = 0x30, + BER_COUNTER = 0x41, + BER_GAUGE = 0x42, + BER_TIMETICK = 0x43, + BER_COUNTER64= 0x46, + BER_NOSUCHOBJ= 0x80, + BER_NOSUCHINS= 0x81, + BER_ENDOFMIB = 0x82, + BER_GETREQ = 0xA0, + BER_GETRESP = 0xA2 +}; + +struct Printer { + const char *host; + const char *comm; + unsigned flags; +}; + +#include "config.h" + +struct Buf { + unsigned char *p; + int n; + int max; +}; + +struct Oid { + uint32_t v[OIDMAX]; + int n; + const char *name; +}; + +struct Bind { + uint32_t oid[OIDMAX]; + int n; + unsigned kind; + long long val; + char str[128]; + int nstr; +}; + +struct Result { + int state; + int errstat; + int erridx; + int reqid; + int tries; + long rtt; + long long sent; + char addr[16]; + struct sockaddr_in sin; + struct Bind bind[MAXVAR]; + int nbind; +}; + +struct Toner { + int color; + long long lvl; + long long mx; + int pct; +}; + +static char *argv0; +static int verbose; +static int usecolor; +static struct Result res[MAXPRT]; + +static const struct Oid qoid[] = { + { {1, 3, 6, 1, 2, 1, 1, 1, 0}, 9, "sysDescr" }, + { {1, 3, 6, 1, 2, 1, 1, 5, 0}, 9, "sysName" }, + { {1, 3, 6, 1, 2, 1, 1, 3, 0}, 9, "sysUpTime" }, + { {1, 3, 6, 1, 2, 1, 25, 3, 2, 1, 5, 1}, 12, + "hrDeviceStatus" }, + { {1, 3, 6, 1, 2, 1, 25, 3, 5, 1, 1, 1}, 12, + "hrPrinterStatus" }, + { {1, 3, 6, 1, 2, 1, 25, 3, 5, 1, 2, 1}, 12, + "hrPrinterDetectedErrorState" }, + { {1, 3, 6, 1, 2, 1, 43, 10, 2, 1, 4, 1, 1}, 13, + "prtMarkerLifeCount" }, + { {1, 3, 6, 1, 2, 1, 43, 11, 1, 1, 6, 1, 1}, 13, + "prtMarkerSuppliesDescription.1" }, + { {1, 3, 6, 1, 2, 1, 43, 11, 1, 1, 6, 1, 2}, 13, + "prtMarkerSuppliesDescription.2" }, + { {1, 3, 6, 1, 2, 1, 43, 11, 1, 1, 6, 1, 3}, 13, + "prtMarkerSuppliesDescription.3" }, + { {1, 3, 6, 1, 2, 1, 43, 11, 1, 1, 6, 1, 4}, 13, + "prtMarkerSuppliesDescription.4" }, + { {1, 3, 6, 1, 2, 1, 43, 11, 1, 1, 8, 1, 1}, 13, + "prtMarkerSuppliesMaxCapacity.1" }, + { {1, 3, 6, 1, 2, 1, 43, 11, 1, 1, 8, 1, 2}, 13, + "prtMarkerSuppliesMaxCapacity.2" }, + { {1, 3, 6, 1, 2, 1, 43, 11, 1, 1, 8, 1, 3}, 13, + "prtMarkerSuppliesMaxCapacity.3" }, + { {1, 3, 6, 1, 2, 1, 43, 11, 1, 1, 8, 1, 4}, 13, + "prtMarkerSuppliesMaxCapacity.4" }, + { {1, 3, 6, 1, 2, 1, 43, 11, 1, 1, 9, 1, 1}, 13, + "prtMarkerSuppliesLevel.1" }, + { {1, 3, 6, 1, 2, 1, 43, 11, 1, 1, 9, 1, 2}, 13, + "prtMarkerSuppliesLevel.2" }, + { {1, 3, 6, 1, 2, 1, 43, 11, 1, 1, 9, 1, 3}, 13, + "prtMarkerSuppliesLevel.3" }, + { {1, 3, 6, 1, 2, 1, 43, 11, 1, 1, 9, 1, 4}, 13, + "prtMarkerSuppliesLevel.4" }, + { {1, 3, 6, 1, 4, 1, 18334, 1, 1, 1, 5, 7, 2, 1, 5, 1}, + 16, "kmLifeCount" }, + { {1, 3, 6, 1, 4, 1, 18334, 1, 1, 1, 5, 7, 2, 2, 1, 5, 1}, + 17, "kmLevel.1" }, + { {1, 3, 6, 1, 4, 1, 18334, 1, 1, 1, 5, 7, 2, 2, 1, 5, 2}, + 17, "kmLevel.2" }, + { {1, 3, 6, 1, 4, 1, 18334, 1, 1, 1, 5, 7, 2, 2, 1, 5, 3}, + 17, "kmLevel.3" }, + { {1, 3, 6, 1, 4, 1, 18334, 1, 1, 1, 5, 7, 2, 2, 1, 5, 4}, + 17, "kmLevel.4" }, + { {1, 3, 6, 1, 4, 1, 18334, 1, 1, 1, 5, 7, 2, 2, 1, 6, 1}, + 17, "kmMax.1" }, + { {1, 3, 6, 1, 4, 1, 18334, 1, 1, 1, 5, 7, 2, 2, 1, 6, 2}, + 17, "kmMax.2" }, + { {1, 3, 6, 1, 4, 1, 18334, 1, 1, 1, 5, 7, 2, 2, 1, 6, 3}, + 17, "kmMax.3" }, + { {1, 3, 6, 1, 4, 1, 18334, 1, 1, 1, 5, 7, 2, 2, 1, 6, 4}, + 17, "kmMax.4" }, + { {1, 3, 6, 1, 4, 1, 253, 8, 53, 13, 2, 1, 6, 1, 20, 1}, + 16, "xrxImpressions" }, +}; + +static void die(const char *fmt, ...); +static int sockerr(void); +static long long nowms(void); +static int putbyte(struct Buf *b, unsigned char c); +static int putbytes(struct Buf *b, const unsigned char *s, int n); +static int putlen(unsigned char *p, unsigned n); +static int closeber(struct Buf *b, int start, unsigned char tag); +static int putint(struct Buf *b, long long v); +static int putnull(struct Buf *b); +static int putoctets(struct Buf *b, const char *s, int n); +static int putsub(struct Buf *b, uint32_t v); +static int putoid(struct Buf *b, const uint32_t *oid, int n); +static int snmpget(unsigned char *out, int max, int reqid, + const char *comm); +static int tlv(const unsigned char *p, int rem, unsigned *tag, + int *hdr, int *vlen); +static int getint(const unsigned char *p, int n, long long *out); +static int getuint(const unsigned char *p, int n, + unsigned long long *out); +static int parseoid(const unsigned char *p, int n, uint32_t *oid, + int *noid); +static int parseval(struct Bind *b, unsigned tag, + const unsigned char *p, int n); +static int snmparse(const unsigned char *p, int n, struct Result *r); +static int resolve(const char *host, struct sockaddr_in *sin); +static const struct Bind *bget(const struct Result *r, + const char *name); +static int bindint(const struct Bind *b, long long *v); +static const char *errstr(int e); +static const char *devstr(long long v); +static const char *prtstr(long long v); +static const char *tagstr(unsigned t); +static void fmtup(long long ticks, char *out, int max); +static void fmteflags(const struct Bind *b, char *out, int max); +static void putdescr(const struct Bind *b, int max); +static void putmodel(const struct Bind *b, int max); +static void puthex(const struct Bind *b); +static int allprint(const struct Bind *b); +static void putoidn(const uint32_t *oid, int n); +static const char *oidname(const uint32_t *oid, int n); +static int stdhas(const struct Result *r); +static int desccolor(const struct Bind *b); +static int colorslot(int col); +static void fmthost(const char *host, char *out, int max); +static void fillslot(struct Toner *t, const struct Bind *lb, + const struct Bind *mb); +static int loadtoner(const struct Result *r, unsigned flags, + struct Toner *t, const char **src); +static const char *inkseq(int c, int pct); +static void putone(const struct Toner *t); +static void putverbose(const struct Result *r); +static void putrow(int i, const struct Result *r); +static void puttable(struct Result *rs, int nprt); +static void txone(Sock s, int i, struct Result *r); +static void socknb(Sock s); +static int wouldblock(void); +static void takepkt(struct Result *rs, int nprt, + const unsigned char *in, int n, + const struct sockaddr_in *from); +static void queryall(Sock s, struct Result *rs, int nprt); +static void usage(void); + +static void +die(const char *fmt, ...) +{ + va_list ap; + size_t n; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + n = strlen(fmt); + if (n && fmt[n - 1] == ':') + fprintf(stderr, " %d\n", sockerr()); + else + fputc('\n', stderr); + exit(1); +} + +static int +sockerr(void) +{ +#ifdef _WIN32 + return WSAGetLastError(); +#else + return errno; +#endif +} + +static long long +nowms(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + return (long long)tv.tv_sec * 1000 + tv.tv_usec / 1000; +} + +static int +putbyte(struct Buf *b, unsigned char c) +{ + if (b->n >= b->max) + return -1; + b->p[b->n++] = c; + return 0; +} + +static int +putbytes(struct Buf *b, const unsigned char *s, int n) +{ + if (n < 0 || b->n + n > b->max) + return -1; + memcpy(b->p + b->n, s, n); + b->n += n; + return 0; +} + +static int +putlen(unsigned char *p, unsigned n) +{ + if (n < 128) { + p[0] = (unsigned char)n; + return 1; + } + if (n < 256) { + p[0] = 0x81; + p[1] = (unsigned char)n; + return 2; + } + p[0] = 0x82; + p[1] = (unsigned char)(n >> 8); + p[2] = (unsigned char)n; + return 3; +} + +static int +closeber(struct Buf *b, int start, unsigned char tag) +{ + unsigned char lbuf[4]; + int inner, nl; + + inner = b->n - start; + if (inner < 0) + return -1; + nl = putlen(lbuf, (unsigned)inner); + if (b->n + 1 + nl > b->max) + return -1; + memmove(b->p + start + 1 + nl, b->p + start, inner); + b->p[start] = tag; + memcpy(b->p + start + 1, lbuf, nl); + b->n += 1 + nl; + return 0; +} + +static int +putint(struct Buf *b, long long v) +{ + unsigned char t[8]; + unsigned long long u; + int i, s, start; + + start = b->n; + u = (unsigned long long)v; + for (i = 7; i >= 0; i--) { + t[i] = (unsigned char)(u & 0xff); + u >>= 8; + } + s = 0; + while (s < 7 && ((t[s] == 0x00 && !(t[s + 1] & 0x80)) || + (t[s] == 0xff && (t[s + 1] & 0x80)))) + s++; + if (putbytes(b, t + s, 8 - s) < 0) + return -1; + return closeber(b, start, BER_INT); +} + +static int +putnull(struct Buf *b) +{ + if (putbyte(b, BER_NULL) < 0) + return -1; + return putbyte(b, 0x00); +} + +static int +putoctets(struct Buf *b, const char *s, int n) +{ + int start; + + start = b->n; + if (putbytes(b, (const unsigned char *)s, n) < 0) + return -1; + return closeber(b, start, BER_OCTET); +} + +static int +putsub(struct Buf *b, uint32_t v) +{ + unsigned char t[5]; + int n, i; + + n = 0; + t[n++] = (unsigned char)(v & 0x7f); + v >>= 7; + while (v) { + if (n >= 5) + return -1; + t[n++] = (unsigned char)(v & 0x7f); + v >>= 7; + } + for (i = n - 1; i >= 1; i--) + if (putbyte(b, t[i] | 0x80) < 0) + return -1; + return putbyte(b, t[0]); +} + +static int +putoid(struct Buf *b, const uint32_t *oid, int n) +{ + int i, start; + + if (n < 2) + return -1; + start = b->n; + if (putbyte(b, (unsigned char)(40 * oid[0] + oid[1])) < 0) + return -1; + for (i = 2; i < n; i++) + if (putsub(b, oid[i]) < 0) + return -1; + return closeber(b, start, BER_OID); +} + +static int +snmpget(unsigned char *out, int max, int reqid, const char *comm) +{ + struct Buf b; + int i, seq, pdu, vbl, vb, cn; + + b.p = out; + b.n = 0; + b.max = max; + cn = (int)strlen(comm); + seq = b.n; + if (putint(&b, 1) < 0) + return -1; + if (putoctets(&b, comm, cn) < 0) + return -1; + pdu = b.n; + if (putint(&b, reqid) < 0) + return -1; + if (putint(&b, 0) < 0) + return -1; + if (putint(&b, 0) < 0) + return -1; + vbl = b.n; + for (i = 0; i < (int)LEN(qoid); i++) { + vb = b.n; + if (putoid(&b, qoid[i].v, qoid[i].n) < 0) + return -1; + if (putnull(&b) < 0) + return -1; + if (closeber(&b, vb, BER_SEQ) < 0) + return -1; + } + if (closeber(&b, vbl, BER_SEQ) < 0) + return -1; + if (closeber(&b, pdu, BER_GETREQ) < 0) + return -1; + if (closeber(&b, seq, BER_SEQ) < 0) + return -1; + return b.n; +} + +static int +tlv(const unsigned char *p, int rem, unsigned *tag, int *hdr, int *vlen) +{ + int i, n, vl; + + if (rem < 2) + return -1; + *tag = p[0]; + if ((p[1] & 0x80) == 0) { + *hdr = 2; + *vlen = p[1]; + } else { + n = p[1] & 0x7f; + if (n < 1 || n > 3 || rem < 2 + n) + return -1; + vl = 0; + for (i = 0; i < n; i++) + vl = (vl << 8) | p[2 + i]; + *hdr = 2 + n; + *vlen = vl; + } + if (*vlen < 0 || *hdr + *vlen > rem) + return -1; + return 0; +} + +static int +getint(const unsigned char *p, int n, long long *out) +{ + int i; + long long v; + + if (n < 1 || n > 8) + return -1; + v = (p[0] & 0x80) ? -1 : 0; + for (i = 0; i < n; i++) + v = (v << 8) | p[i]; + *out = v; + return 0; +} + +static int +getuint(const unsigned char *p, int n, unsigned long long *out) +{ + int i; + unsigned long long v; + + if (n < 1 || n > 8) + return -1; + v = 0; + for (i = 0; i < n; i++) + v = (v << 8) | p[i]; + *out = v; + return 0; +} + +static int +parseoid(const unsigned char *p, int n, uint32_t *oid, int *noid) +{ + int i, k; + uint32_t v; + + if (n < 1) + return -1; + if (p[0] < 80) { + oid[0] = p[0] / 40; + oid[1] = p[0] % 40; + } else { + oid[0] = 2; + oid[1] = p[0] - 80; + } + k = 2; + i = 1; + while (i < n) { + v = 0; + for (;;) { + if (i >= n || k >= OIDMAX) + return -1; + if (v > (0xffffffffu >> 7)) + return -1; + v = (v << 7) | (p[i] & 0x7f); + if ((p[i++] & 0x80) == 0) + break; + } + oid[k++] = v; + } + *noid = k; + return 0; +} + +static int +parseval(struct Bind *b, unsigned tag, const unsigned char *p, int n) +{ + unsigned long long u; + int m; + + b->kind = tag; + b->val = 0; + b->nstr = 0; + b->str[0] = '\0'; + switch (tag) { + case BER_INT: /* FALLTHROUGH */ + case BER_ENUM: + return getint(p, n, &b->val); + case BER_COUNTER: /* FALLTHROUGH */ + case BER_GAUGE: /* FALLTHROUGH */ + case BER_TIMETICK: /* FALLTHROUGH */ + case BER_COUNTER64: + if (getuint(p, n, &u) < 0) + return -1; + b->val = (long long)u; + return 0; + case BER_OCTET: /* FALLTHROUGH */ + case BER_BITSTR: + m = n; + if (m > (int)sizeof(b->str) - 1) + m = (int)sizeof(b->str) - 1; + memcpy(b->str, p, m); + b->nstr = m; + b->str[m] = '\0'; + return 0; + default: + return 0; + } +} + +static int +snmparse(const unsigned char *p, int n, struct Result *r) +{ + unsigned tag, t1, t2; + int hdr, vlen, rem, off, vbrem, h1, l1, h2, l2, blen; + long long ver, reqid, err, idx; + const unsigned char *vb, *body; + struct Bind *bn; + + r->nbind = 0; + off = 0; + rem = n; + if (tlv(p + off, rem, &tag, &hdr, &vlen) < 0 || tag != BER_SEQ) + return -1; + off += hdr; + rem = vlen; + + if (tlv(p + off, rem, &tag, &hdr, &vlen) < 0 || tag != BER_INT) + return -1; + if (getint(p + off + hdr, vlen, &ver) < 0) + return -1; + if (ver != 0 && ver != 1) + return -1; + off += hdr + vlen; + rem -= hdr + vlen; + + if (tlv(p + off, rem, &tag, &hdr, &vlen) < 0 || tag != BER_OCTET) + return -1; + off += hdr + vlen; + rem -= hdr + vlen; + + if (tlv(p + off, rem, &tag, &hdr, &vlen) < 0) + return -1; + if (tag != BER_GETRESP) + return -1; + off += hdr; + rem = vlen; + + if (tlv(p + off, rem, &tag, &hdr, &vlen) < 0 || tag != BER_INT) + return -1; + if (getint(p + off + hdr, vlen, &reqid) < 0) + return -1; + r->reqid = (int)reqid; + off += hdr + vlen; + rem -= hdr + vlen; + + if (tlv(p + off, rem, &tag, &hdr, &vlen) < 0 || tag != BER_INT) + return -1; + if (getint(p + off + hdr, vlen, &err) < 0) + return -1; + r->errstat = (int)err; + off += hdr + vlen; + rem -= hdr + vlen; + + if (tlv(p + off, rem, &tag, &hdr, &vlen) < 0 || tag != BER_INT) + return -1; + if (getint(p + off + hdr, vlen, &idx) < 0) + return -1; + r->erridx = (int)idx; + off += hdr + vlen; + rem -= hdr + vlen; + + if (tlv(p + off, rem, &tag, &hdr, &vlen) < 0 || tag != BER_SEQ) + return -1; + vb = p + off + hdr; + vbrem = vlen; + + while (vbrem > 0) { + if (r->nbind >= MAXVAR) + break; + if (tlv(vb, vbrem, &tag, &hdr, &vlen) < 0 || + tag != BER_SEQ) + return -1; + body = vb + hdr; + blen = vlen; + if (tlv(body, blen, &t1, &h1, &l1) < 0 || t1 != BER_OID) + return -1; + bn = &r->bind[r->nbind]; + memset(bn, 0, sizeof(*bn)); + if (parseoid(body + h1, l1, bn->oid, &bn->n) < 0) + return -1; + body += h1 + l1; + blen -= h1 + l1; + if (tlv(body, blen, &t2, &h2, &l2) < 0) + return -1; + if (parseval(bn, t2, body + h2, l2) < 0) + return -1; + r->nbind++; + vb += hdr + vlen; + vbrem -= hdr + vlen; + } + return 0; +} + +static int +resolve(const char *host, struct sockaddr_in *sin) +{ + struct addrinfo hints, *ai; + int e; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + e = getaddrinfo(host, NULL, &hints, &ai); + if (e || !ai) + return -1; + memcpy(sin, ai->ai_addr, sizeof(*sin)); + sin->sin_port = htons((unsigned short)PORT); + freeaddrinfo(ai); + return 0; +} + +static const struct Bind * +bget(const struct Result *r, const char *name) +{ + int i, j; + + for (i = 0; i < (int)LEN(qoid); i++) { + if (strcmp(qoid[i].name, name) != 0) + continue; + for (j = 0; j < r->nbind; j++) + if (r->bind[j].n == qoid[i].n && + memcmp(r->bind[j].oid, qoid[i].v, + qoid[i].n * sizeof(uint32_t)) == 0) + return &r->bind[j]; + return NULL; + } + return NULL; +} + +static int +bindint(const struct Bind *b, long long *v) +{ + if (!b) + return -1; + switch (b->kind) { + case BER_INT: /* FALLTHROUGH */ + case BER_ENUM: /* FALLTHROUGH */ + case BER_COUNTER: /* FALLTHROUGH */ + case BER_GAUGE: /* FALLTHROUGH */ + case BER_TIMETICK: /* FALLTHROUGH */ + case BER_COUNTER64: + *v = b->val; + return 0; + } + return -1; +} + +static const char * +errstr(int e) +{ + static const char *n[] = { + "noError", "tooBig", "noSuchName", "badValue", + "readOnly", "genErr", "noAccess", "wrongType", + "wrongLength", "wrongEncoding", "wrongValue", + "noCreation", "inconsistentValue", + "resourceUnavailable", "commitFailed", "undoFailed", + "authorizationError", "notWritable", "inconsistentName" + }; + + if (e >= 0 && e < (int)LEN(n)) + return n[e]; + return "?"; +} + +static const char * +devstr(long long v) +{ + static const char *n[] = { + "?", "unknown", "running", "warning", "testing", "down" + }; + + if (v >= 1 && v < (long long)LEN(n)) + return n[v]; + return "?"; +} + +static const char * +prtstr(long long v) +{ + static const char *n[] = { + "?", "other", "unknown", "idle", "printing", "warmup" + }; + + if (v >= 1 && v < (long long)LEN(n)) + return n[v]; + return "?"; +} + +static const char * +tagstr(unsigned t) +{ + switch (t) { + case BER_INT: return "INTEGER"; + case BER_BITSTR: return "BITSTRING"; + case BER_OCTET: return "OCTETSTRING"; + case BER_NULL: return "NULL"; + case BER_OID: return "OID"; + case BER_ENUM: return "ENUM"; + case BER_COUNTER: return "COUNTER32"; + case BER_GAUGE: return "GAUGE32"; + case BER_TIMETICK: return "TIMETICKS"; + case BER_COUNTER64:return "COUNTER64"; + case BER_NOSUCHOBJ:return "noSuchObject"; + case BER_NOSUCHINS:return "noSuchInstance"; + case BER_ENDOFMIB: return "endOfMibView"; + default: return "OTHER"; + } +} + +static void +fmtup(long long ticks, char *out, int max) +{ + unsigned long s, d, h, m; + + if (ticks < 0) { + snprintf(out, max, "-"); + return; + } + s = (unsigned long)(ticks / 100); + d = s / 86400UL; + s %= 86400UL; + h = s / 3600UL; + s %= 3600UL; + m = s / 60UL; + snprintf(out, max, "%lud%02luh%02lum", d, h, m); +} + +static void +fmteflags(const struct Bind *b, char *out, int max) +{ + static const char *bit[] = { + "lowPaper", "noPaper", "lowToner", "noToner", + "doorOpen", "jammed", "offline", "svcReq", + "inTrayMiss", "outTrayMiss", "supplyMiss", + "outNearFull", "outFull", "inEmpty", "overduePM" + }; + int i, first, byte, off, n; + unsigned char c; + + if (!b || b->nstr <= 0) { + snprintf(out, max, "-"); + return; + } + n = 0; + out[0] = '\0'; + first = 1; + for (i = 0; i < (int)LEN(bit); i++) { + byte = i / 8; + off = 7 - (i % 8); + if (byte >= b->nstr) + break; + c = (unsigned char)b->str[byte]; + if (!(c & (1u << off))) + continue; + if (!first && n + 1 < max) { + out[n++] = ','; + out[n] = '\0'; + } + first = 0; + if (n + (int)strlen(bit[i]) >= max) + break; + memcpy(out + n, bit[i], strlen(bit[i]) + 1); + n += (int)strlen(bit[i]); + } + if (first) + snprintf(out, max, "-"); +} + +static void +putdescr(const struct Bind *b, int max) +{ + int i, n, out; + + if (!b || b->nstr <= 0) { + fputs("-", stdout); + return; + } + n = b->nstr; + out = 0; + for (i = 0; i < n && out < max; i++) { + if (b->str[i] >= 0x20 && b->str[i] < 0x7f) { + putchar(b->str[i]); + out++; + } else if (isspace((unsigned char)b->str[i]) && + out > 0) { + putchar(' '); + out++; + } + } + if (out == 0) + fputs("-", stdout); +} + +static void +putmodel(const struct Bind *b, int max) +{ + char buf[128]; + int i, n, out; + char *p, *v; + + if (!b || b->nstr <= 0) { + fputs("-", stdout); + return; + } + n = b->nstr; + if (n > (int)sizeof(buf) - 1) + n = (int)sizeof(buf) - 1; + out = 0; + for (i = 0; i < n; i++) { + if (b->str[i] == ';') + break; + if (b->str[i] >= 0x20 && b->str[i] < 0x7f) + buf[out++] = b->str[i]; + } + while (out > 0 && buf[out - 1] == ' ') + out--; + buf[out] = '\0'; + /* "WorkCentre 7835 v1" -> "WorkCentre 7835" */ + p = buf; + v = NULL; + while (*p) { + if (p[0] == ' ' && (p[1] == 'v' || p[1] == 'V') && + p[2] >= '0' && p[2] <= '9') + v = p; + p++; + } + if (v) { + p = v + 2; + while (*p >= '0' && *p <= '9') + p++; + if (*p == '\0') + *v = '\0'; + } + n = (int)strlen(buf); + if (n > max) + n = max; + if (n <= 0) + fputs("-", stdout); + else + fwrite(buf, 1, n, stdout); +} + +static void +puthex(const struct Bind *b) +{ + int i; + + if (b->nstr <= 0) { + fputs("-", stdout); + return; + } + for (i = 0; i < b->nstr; i++) { + if (i) + putchar(' '); + printf("%02x", (unsigned char)b->str[i]); + } +} + +static int +allprint(const struct Bind *b) +{ + int i; + + if (b->nstr <= 0) + return 0; + for (i = 0; i < b->nstr; i++) + if ((unsigned char)b->str[i] < 0x20 || + (unsigned char)b->str[i] >= 0x7f) + return 0; + return 1; +} + +static void +putoidn(const uint32_t *oid, int n) +{ + int i; + + for (i = 0; i < n; i++) { + if (i) + putchar('.'); + printf("%u", (unsigned)oid[i]); + } +} + +static const char * +oidname(const uint32_t *oid, int n) +{ + int i; + + for (i = 0; i < (int)LEN(qoid); i++) + if (qoid[i].n == n && + memcmp(qoid[i].v, oid, n * sizeof(uint32_t)) == 0) + return qoid[i].name; + return "-"; +} + +static int +stdhas(const struct Result *r) +{ + int i; + char name[40]; + const struct Bind *b; + long long v; + + for (i = 1; i <= 4; i++) { + snprintf(name, sizeof(name), + "prtMarkerSuppliesLevel.%d", i); + b = bget(r, name); + if (bindint(b, &v) == 0 && v >= 0) + return 1; + } + return 0; +} + +static int +desccolor(const struct Bind *b) +{ + char buf[128]; + int i, n; + + if (!b || b->nstr <= 0) + return 0; + n = b->nstr; + if (n > (int)sizeof(buf) - 1) + n = (int)sizeof(buf) - 1; + for (i = 0; i < n; i++) + buf[i] = (char)tolower((unsigned char)b->str[i]); + buf[n] = '\0'; + if (strstr(buf, "cyan")) + return 'C'; + if (strstr(buf, "magenta")) + return 'M'; + if (strstr(buf, "yellow") || strstr(buf, "jaune")) + return 'Y'; + if (strstr(buf, "black") || strstr(buf, "noir")) + return 'K'; + return 0; +} + +/* Display order matches Konica WCD: Yellow, Magenta, Cyan, Black. */ +static int +colorslot(int col) +{ + switch (col) { + case 'Y': + return 0; + case 'M': + return 1; + case 'C': + return 2; + case 'K': + return 3; + } + return -1; +} + +static void +fmthost(const char *host, char *out, int max) +{ + static const char suf[] = ".obspm.fr"; + size_t n, sl; + + n = strlen(host); + sl = sizeof(suf) - 1; + if (n > sl && memcmp(host + n - sl, suf, sl) == 0) + n -= sl; + if ((int)n >= max) + n = max - 1; + memcpy(out, host, n); + out[n] = '\0'; +} + +static void +fillslot(struct Toner *t, const struct Bind *lb, const struct Bind *mb) +{ + t->lvl = -1; + t->mx = -1; + t->pct = -1; + if (bindint(lb, &t->lvl) < 0) + t->lvl = -1; + if (bindint(mb, &t->mx) < 0) + t->mx = -1; + if (t->lvl >= 0 && t->mx > 0) + t->pct = (int)(t->lvl * 100 / t->mx); +} + +static int +loadtoner(const struct Result *r, unsigned flags, + struct Toner *t, const char **src) +{ + static const char order[] = { 'Y', 'M', 'C', 'K' }; + static const char stdi[] = { 'C', 'M', 'Y', 'K' }; + static const char kmc[] = { 'K', 'C', 'M', 'Y' }; + char ln[40], mn[40], dn[48]; + const struct Bind *lb, *mb; + int i, n, slot, usekm; + + for (i = 0; i < 4; i++) { + t[i].color = order[i]; + t[i].lvl = -1; + t[i].mx = -1; + t[i].pct = -1; + } + n = 0; + for (i = 1; i <= 4; i++) { + snprintf(dn, sizeof(dn), + "prtMarkerSuppliesDescription.%d", i); + snprintf(ln, sizeof(ln), + "prtMarkerSuppliesLevel.%d", i); + snprintf(mn, sizeof(mn), + "prtMarkerSuppliesMaxCapacity.%d", i); + slot = colorslot(desccolor(bget(r, dn))); + if (slot < 0) + continue; + fillslot(&t[slot], bget(r, ln), bget(r, mn)); + if (t[slot].lvl >= 0) + n++; + } + if (n > 0) { + *src = (flags & F_XRX) ? "xrx" : "std"; + return n; + } + usekm = !stdhas(r); + *src = usekm ? "km" : "std"; + for (i = 0; i < 4; i++) { + if (usekm) { + t[i].color = kmc[i]; + snprintf(ln, sizeof(ln), "kmLevel.%d", i + 1); + snprintf(mn, sizeof(mn), "kmMax.%d", i + 1); + } else { + t[i].color = stdi[i]; + snprintf(ln, sizeof(ln), + "prtMarkerSuppliesLevel.%d", i + 1); + snprintf(mn, sizeof(mn), + "prtMarkerSuppliesMaxCapacity.%d", i + 1); + } + lb = bget(r, ln); + mb = bget(r, mn); + fillslot(&t[i], lb, mb); + if (t[i].lvl >= 0) + n++; + } + if (n == 0) + *src = "-"; + return n; +} + +static const char * +inkseq(int c, int pct) +{ + if (!usecolor) + return ""; + if (pct >= 0 && pct <= CRITPCT) { + switch (c) { + case 'C': return "\033[7;36m"; + case 'M': return "\033[7;35m"; + case 'Y': return "\033[7;33m"; + default: return "\033[7;37m"; + } + } + if (pct >= 0 && pct <= LOWPCT) { + switch (c) { + case 'C': return "\033[1;36m"; + case 'M': return "\033[1;35m"; + case 'Y': return "\033[1;33m"; + default: return "\033[1;37m"; + } + } + switch (c) { + case 'C': return "\033[36m"; + case 'M': return "\033[35m"; + case 'Y': return "\033[33m"; + default: return "\033[37m"; + } +} + +static void +putone(const struct Toner *t) +{ + const char *rs; + int vis; + + rs = usecolor ? "\033[0m" : ""; + fputs(inkseq(t->color, t->pct), stdout); + if (t->pct >= 0) + vis = printf("%c=%d", t->color, t->pct); + else if (t->lvl >= 0) + vis = printf("%c=%lld", t->color, t->lvl); + else + vis = printf("%c=-", t->color); + if (t->pct >= 0 && t->pct <= CRITPCT) { + putchar('!'); + vis++; + } else if (t->pct >= 0 && t->pct <= LOWPCT) { + putchar('*'); + vis++; + } + fputs(rs, stdout); + while (vis < TONW) { + putchar(' '); + vis++; + } +} + +static void +putverbose(const struct Result *r) +{ + int i; + const struct Bind *b; + + for (i = 0; i < r->nbind; i++) { + b = &r->bind[i]; + fputs(" ", stdout); + putoidn(b->oid, b->n); + printf(" %s %s ", oidname(b->oid, b->n), + tagstr(b->kind)); + switch (b->kind) { + case BER_INT: /* FALLTHROUGH */ + case BER_ENUM: /* FALLTHROUGH */ + case BER_COUNTER: /* FALLTHROUGH */ + case BER_GAUGE: /* FALLTHROUGH */ + case BER_TIMETICK: /* FALLTHROUGH */ + case BER_COUNTER64: + printf("%lld\n", b->val); + break; + case BER_OCTET: /* FALLTHROUGH */ + case BER_BITSTR: + if (allprint(b)) + putdescr(b, 80); + else + puthex(b); + putchar('\n'); + break; + case BER_NOSUCHOBJ: /* FALLTHROUGH */ + case BER_NOSUCHINS: /* FALLTHROUGH */ + case BER_ENDOFMIB: + printf("%s\n", tagstr(b->kind)); + break; + default: + printf("tag=0x%02x\n", b->kind); + break; + } + } +} + +static void +putrow(int i, const struct Result *r) +{ + const struct Bind *b; + struct Toner t[4]; + long long v, pages; + int k, nt; + const char *src, *hd, *hp; + char up[16], ef[48], host[HOSTW + 1]; + + fmthost(printers[i].host, host, sizeof(host)); + printf("%2d %-*.*s ", i, HOSTW, HOSTW, host); + if (r->state != ST_OK) { + switch (r->state) { + case ST_DNS: + src = "dns"; + break; + case ST_TIMEOUT: + src = "timeout"; + break; + case ST_BADPDU: + src = "badpdu"; + break; + default: + src = "?"; + break; + } + if (r->state == ST_BADPDU) + printf("%4ld ", r->rtt); + else + fputs(" - ", stdout); + printf("%3s %-10s %-8s %-8s %-16s %7s %-*s %-*.*s %s\n", + "-", "-", "-", "-", "-", "-", TONCOL, "-", + SRCW, SRCW, "-", src); + return; + } + printf("%4ld %3d ", r->rtt, r->errstat); + + b = bget(r, "sysUpTime"); + v = -1; + bindint(b, &v); + fmtup(v, up, sizeof(up)); + + b = bget(r, "hrDeviceStatus"); + hd = (bindint(b, &v) == 0) ? devstr(v) : "-"; + b = bget(r, "hrPrinterStatus"); + hp = (bindint(b, &v) == 0) ? prtstr(v) : "-"; + fmteflags(bget(r, "hrPrinterDetectedErrorState"), + ef, sizeof(ef)); + + pages = -1; + b = bget(r, "prtMarkerLifeCount"); + if (bindint(b, &v) == 0 && v >= 0) + pages = v; + else if (bindint(bget(r, "kmLifeCount"), &v) == 0 && v >= 0) + pages = v; + else if (bindint(bget(r, "xrxImpressions"), &v) == 0 && v >= 0) + pages = v; + + printf("%-10s %-8s %-8s %-16s ", up, hd, hp, ef); + if (pages >= 0) + printf("%7lld ", pages); + else + printf("%7s ", "-"); + + src = "-"; + nt = loadtoner(r, printers[i].flags, t, &src); + if (nt == 0) + printf("%-*s", TONCOL, "-"); + else + for (k = 0; k < 4; k++) { + if (k) + putchar(' '); + putone(&t[k]); + } + printf(" %-*.*s ", SRCW, SRCW, src); + putmodel(bget(r, "sysDescr"), 40); + putchar('\n'); + if (verbose) + putverbose(r); +} + +static void +puttable(struct Result *rs, int nprt) +{ + int i, k, nok, nto, ndns, nbad, nlow, ncrit, nt; + struct Toner t[4]; + const char *src; + + printf("prtmib n=%d SNMPv2c udp/%d timeout=%dms " + "retry=%d comm=%s low=%d crit=%d\n", + nprt, PORT, TIMEOUT, RETRIES, community, LOWPCT, CRITPCT); + printf("%-2s %-*.*s %4s %3s %-10s %-8s %-8s %-16s " + "%7s %-*s %-*.*s %s\n", + "i", HOSTW, HOSTW, "host", "ms", "err", "up", + "hrDev", "hrPrt", "eflags", "pages", TONCOL, "toner", + SRCW, SRCW, "src", "model"); + nok = nto = ndns = nbad = nlow = ncrit = 0; + for (i = 0; i < nprt; i++) { + putrow(i, &rs[i]); + switch (rs[i].state) { + case ST_OK: + nok++; + src = "-"; + nt = loadtoner(&rs[i], printers[i].flags, t, + &src); + if (nt > 0) + for (k = 0; k < 4; k++) { + if (t[k].pct < 0) + continue; + if (t[k].pct <= CRITPCT) + ncrit++; + else if (t[k].pct <= LOWPCT) + nlow++; + } + break; + case ST_TIMEOUT: + nto++; + break; + case ST_DNS: + ndns++; + break; + case ST_BADPDU: + nbad++; + break; + } + } + printf("%d host %d ok %d timeout %d dns %d badpdu " + "%d low %d crit\n", + nprt, nok, nto, ndns, nbad, nlow, ncrit); +} + +static void +txone(Sock s, int i, struct Result *r) +{ + unsigned char pkt[PKTMAX]; + const char *comm; + int n; + + comm = printers[i].comm ? printers[i].comm : community; + n = snmpget(pkt, PKTMAX, r->reqid, comm); + if (n < 0) + die("snmp encode failed"); + r->tries++; + r->sent = nowms(); + if (sendto(s, (const char *)pkt, n, 0, + (struct sockaddr *)&r->sin, sizeof(r->sin)) < 0) { + if (verbose) + fprintf(stderr, "tx %s: %d\n", + printers[i].host, sockerr()); + return; + } + if (verbose) + fprintf(stderr, "tx %s reqid=%d bytes=%d try=%d\n", + printers[i].host, r->reqid, n, r->tries); +} + +static void +socknb(Sock s) +{ + int rcv; +#ifdef _WIN32 + u_long nb; + + nb = 1; + ioctlsocket(s, FIONBIO, &nb); +#else + int fl; + + fl = fcntl(s, F_GETFL, 0); + if (fl >= 0) + fcntl(s, F_SETFL, fl | O_NONBLOCK); +#endif + rcv = 65536; + setsockopt(s, SOL_SOCKET, SO_RCVBUF, (const char *)&rcv, + sizeof(rcv)); +} + +static int +wouldblock(void) +{ +#ifdef _WIN32 + return sockerr() == WSAEWOULDBLOCK; +#else + return errno == EAGAIN || errno == EWOULDBLOCK; +#endif +} + +static void +takepkt(struct Result *rs, int nprt, const unsigned char *in, int n, + const struct sockaddr_in *from) +{ + struct Result tmp; + int j, found; + + memset(&tmp, 0, sizeof(tmp)); + if (snmparse(in, n, &tmp) < 0) { + if (verbose) + fprintf(stderr, "rx %d bytes: bad pdu\n", n); + for (j = 0; j < nprt; j++) { + if (rs[j].state != ST_WAIT) + continue; + if (rs[j].sin.sin_addr.s_addr == + from->sin_addr.s_addr) { + rs[j].state = ST_BADPDU; + rs[j].rtt = (long)(nowms() - rs[j].sent); + break; + } + } + return; + } + found = -1; + for (j = 0; j < nprt; j++) + if (rs[j].state == ST_WAIT && + rs[j].reqid == tmp.reqid) { + found = j; + break; + } + if (found < 0) { + if (verbose) + fprintf(stderr, "rx unmatched reqid=%d\n", + tmp.reqid); + return; + } + rs[found].errstat = tmp.errstat; + rs[found].erridx = tmp.erridx; + rs[found].nbind = tmp.nbind; + memcpy(rs[found].bind, tmp.bind, sizeof(tmp.bind)); + rs[found].rtt = (long)(nowms() - rs[found].sent); + rs[found].state = ST_OK; + if (verbose) + fprintf(stderr, + "rx %s bytes=%d err=%d(%s) binds=%d rtt=%ld\n", + printers[found].host, n, tmp.errstat, + errstr(tmp.errstat), tmp.nbind, rs[found].rtt); +} + +static void +queryall(Sock s, struct Result *rs, int nprt) +{ + unsigned char in[RECVMAX]; + struct sockaddr_in from; +#ifdef _WIN32 + int fromlen; +#else + socklen_t fromlen; +#endif + struct timeval tv; + fd_set rds; + int i, pending, nr, n; + long long now, rem, wait; + + socknb(s); + for (i = 0; i < nprt; i++) { + if (rs[i].state != ST_WAIT) + continue; + txone(s, i, &rs[i]); + } + for (;;) { + pending = 0; + wait = -1; + now = nowms(); + for (i = 0; i < nprt; i++) { + if (rs[i].state != ST_WAIT) + continue; + if (rs[i].sent == 0) { + txone(s, i, &rs[i]); + if (rs[i].sent == 0) { + rs[i].state = ST_TIMEOUT; + continue; + } + } + rem = rs[i].sent + TIMEOUT - now; + if (rem <= 0) { + if (rs[i].tries <= RETRIES) + txone(s, i, &rs[i]); + else { + rs[i].state = ST_TIMEOUT; + continue; + } + rem = TIMEOUT; + } + pending++; + if (wait < 0 || rem < wait) + wait = rem; + } + if (!pending) + break; + if (wait < 1) + wait = 1; + tv.tv_sec = (long)(wait / 1000); + tv.tv_usec = (long)((wait % 1000) * 1000); + FD_ZERO(&rds); + FD_SET(s, &rds); +#ifdef _WIN32 + nr = select(0, &rds, NULL, NULL, &tv); +#else + nr = select((int)s + 1, &rds, NULL, NULL, &tv); +#endif + if (nr < 0) + die("select:"); + if (nr == 0) + continue; + for (;;) { + fromlen = sizeof(from); + n = recvfrom(s, (char *)in, RECVMAX, 0, + (struct sockaddr *)&from, &fromlen); + if (n < 0) { + if (!wouldblock() && verbose) + fprintf(stderr, "recvfrom: %d\n", + sockerr()); + break; + } + takepkt(rs, nprt, in, n, &from); + } + } +} + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-v]\n", argv0); + exit(2); +} + +int +main(int argc, char *argv[]) +{ + Sock s; + int i, nprt, rc; + char *a, *p; +#ifdef _WIN32 + WSADATA wsa; + HANDLE h; + DWORD m; +#endif + + argv0 = argv[0] ? argv[0] : "prtmib"; + for (i = 1; i < argc; i++) { + a = argv[i]; + if (a[0] != '-' || a[1] == '\0') + usage(); + if (a[1] == '-' && a[2] == '\0') { + i++; + break; + } + for (p = a + 1; *p; p++) + switch (*p) { + case 'v': + verbose = 1; + break; + case 'h': + usage(); + break; /* NOTREACHED */ + default: + usage(); + } + } + if (i != argc) + usage(); + + nprt = (int)LEN(printers); + if (nprt < 1) + die("no printers in config.h"); + if (nprt > MAXPRT) + die("too many printers (MAXPRT)"); + +#ifdef _WIN32 + if (WSAStartup(MAKEWORD(2, 2), &wsa)) + die("WSAStartup:"); + h = GetStdHandle(STD_OUTPUT_HANDLE); + if (h != INVALID_HANDLE_VALUE && GetConsoleMode(h, &m)) + SetConsoleMode(h, m | 0x0004); + usecolor = _isatty(_fileno(stdout)) ? 1 : 0; +#else + usecolor = isatty(1) ? 1 : 0; +#endif + + memset(res, 0, sizeof(res)); + for (i = 0; i < nprt; i++) { + res[i].reqid = i + 1; + if (resolve(printers[i].host, &res[i].sin) < 0) { + res[i].state = ST_DNS; + snprintf(res[i].addr, sizeof(res[i].addr), + "-"); + if (verbose) + fprintf(stderr, "dns %s failed\n", + printers[i].host); + continue; + } + res[i].state = ST_WAIT; + inet_ntop(AF_INET, &res[i].sin.sin_addr, + res[i].addr, sizeof(res[i].addr)); + if (verbose) + fprintf(stderr, "dns %s %s\n", + printers[i].host, res[i].addr); + } + + s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s == INVALID_SOCKET) + die("socket:"); + queryall(s, res, nprt); + sockclose(s); + + puttable(res, nprt); + rc = 0; + for (i = 0; i < nprt; i++) + if (res[i].state != ST_OK) + rc = 1; +#ifdef _WIN32 + WSACleanup(); +#endif + return rc; +} -- cgit v1.2.3