summaryrefslogtreecommitdiff
path: root/prtmib.c
diff options
context:
space:
mode:
authorbryan <bryan@veltar.systems>2026-08-26 11:47:10 +0200
committerbryan <bryan@veltar.systems>2026-08-26 11:47:10 +0200
commit002742ad5b1fd76d539c2fb8736622e63e757f98 (patch)
tree483347bf0263b0b463dc8b116f90e1332edd1c3e /prtmib.c
downloadprtmib-002742ad5b1fd76d539c2fb8736622e63e757f98.tar.gz
prtmib-002742ad5b1fd76d539c2fb8736622e63e757f98.zip
Initial commit
Diffstat (limited to 'prtmib.c')
-rw-r--r--prtmib.c1655
1 files changed, 1655 insertions, 0 deletions
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 <winsock2.h>
+# include <ws2tcpip.h>
+# include <io.h>
+# include <windows.h>
+#else
+# include <sys/select.h>
+# include <sys/socket.h>
+# include <arpa/inet.h>
+# include <fcntl.h>
+# include <netdb.h>
+# include <unistd.h>
+#endif
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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;
+}