diff options
| author | bryan <bryan@veltar.systems> | 2026-08-26 11:47:10 +0200 |
|---|---|---|
| committer | bryan <bryan@veltar.systems> | 2026-08-26 11:47:10 +0200 |
| commit | 002742ad5b1fd76d539c2fb8736622e63e757f98 (patch) | |
| tree | 483347bf0263b0b463dc8b116f90e1332edd1c3e | |
| download | prtmib-002742ad5b1fd76d539c2fb8736622e63e757f98.tar.gz prtmib-002742ad5b1fd76d539c2fb8736622e63e757f98.zip | |
Initial commit
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | LICENSE | 15 | ||||
| -rw-r--r-- | Makefile | 16 | ||||
| -rw-r--r-- | README | 60 | ||||
| -rw-r--r-- | config.def.h | 21 | ||||
| -rw-r--r-- | config.mk | 19 | ||||
| -rw-r--r-- | prtmib.c | 1655 |
7 files changed, 1788 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b8aa8c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +config.h +prtmib.exe @@ -0,0 +1,15 @@ +ISC License + +© 2026 prtmib authors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0bc4f23 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +.POSIX: + +include config.mk + +HDR = config.h +SRC = prtmib.c + +all: prtmib$(EXE) + +prtmib$(EXE): $(SRC) $(HDR) + $(CC) $(CFLAGS) -o $@ $(SRC) $(LDFLAGS) + +clean: + -$(RM) prtmib$(EXE) + +.PHONY: all clean @@ -0,0 +1,60 @@ +prtmib — SNMPv2c printer marker dump + + prtmib [-v] + +One UDP GET per host listed in config.h. Prints a dense table of +the values the device actually returned. No JSON, no daemon, no +layout flags. Filter with grep(1). + +Columns + + idx index in the printers[] table (config.h) + host FQDN with trailing .obspm.fr stripped + ms round trip, milliseconds + err SNMP error-status (0 = noError); RFC 1905 names with -v + up sysUpTime as days+hours+minutes + hrDev hrDeviceStatus + hrPrt hrPrinterStatus + eflags hrPrinterDetectedErrorState bits that are set + pages prtMarkerLifeCount, else kmLifeCount, else xrxImpressions + toner Y/M/C/K percent, letters taken from + prtMarkerSuppliesDescription (not slot index). Order + matches Konica PageScope (Yellow, Magenta, Cyan, Black). + F_XRX is a tag only. Else Konica enterprise OIDs. + '*' = <= LOWPCT, '!' = <= CRITPCT + src which mapping filled toner: std | xrx | km | - + model sysDescr up to ';' (Xerox firmware blob stripped), + trailing " vN" dropped, then truncated + +-v also prints every varbind (OID, MIB name, BER tag, value) and a +short packet trace. + +Exit status + + 0 every host answered a parseable GetResponse + 1 at least one DNS failure, timeout, or bad PDU + 2 usage + +Build + + Windows (MinGW, default config.mk): + make + + as(1) and ld(1) must be the MinGW ones; config.mk prepends + MINGWBIN to PATH so a stock prompt is enough. + + Linux: edit config.mk (comment the Windows block, uncomment Linux), + then make. `RM = rm -f` on Unix. + + Hosts, community, timeouts, thresholds: config.h. Recompile. + +Protocol + + SNMPv2c GET, UDP/161. One PDU per host carrying sysDescr, sysName, + sysUpTime, hrDeviceStatus, hrPrinterStatus, + hrPrinterDetectedErrorState, prtMarkerLifeCount, supplies + description/max/level 1-4, Konica 18334 page+toner OIDs, Xerox 253 + impressions. Missing objects arrive as noSuchObject / noSuchInstance. + + Standard supply indices are CMYK. Konica enterprise indices are KCMY. + Supply -1/-2/-3 (other/unknown/some) are not treated as percentages. diff --git a/config.def.h b/config.def.h new file mode 100644 index 0000000..c82aef7 --- /dev/null +++ b/config.def.h @@ -0,0 +1,21 @@ +/* Copy to config.h and edit. Recompile after changing printers. */ + +static const char community[] = "public"; + +enum { + LOWPCT = 5, /* percent: mark with '*' */ + CRITPCT = 3, /* percent: mark with '!' */ + TIMEOUT = 2000, /* milliseconds per attempt */ + RETRIES = 1, /* extra sends after a timeout */ + PORT = 161 +}; + +/* + * comm == NULL means use community[]. + * flags: F_XRX maps toner letters from prtMarkerSuppliesDescription + * (Xerox slots are not CMYK). Konica enterprise OIDs are queried as + * fallback on every host (noSuchInstance is not fatal). + */ +static const struct Printer printers[] = { + { "printer.domain", NULL, 0 }, +}; diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..d701da0 --- /dev/null +++ b/config.mk @@ -0,0 +1,19 @@ +# prtmib — site/toolchain knobs. Edit this, not the Makefile. +# +# Windows (this machine): MinGW-w64 gcc. LLVM clang in PATH targets MSVC +# and needs the VS developer prompt; MinGW does not. +# as/ld live next to gcc; export PATH so the driver finds them. +MINGWBIN = C:/msys64/mingw64/bin +CC = $(MINGWBIN)/gcc +CFLAGS = -std=c99 -Wall -Wextra -pedantic -Os +LDFLAGS = -lws2_32 +EXE = .exe +RM = cmd /c del /q +export PATH := $(MINGWBIN);$(PATH) + +# Linux (uncomment, comment the block above): +# CC = cc +# CFLAGS = -std=c99 -Wall -Wextra -pedantic -Os -D_POSIX_C_SOURCE=200809L +# LDFLAGS = +# EXE = +# RM = rm -f 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; +} |
