/* 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; }