1154133Sharti/*-
2154133Sharti * Copyright (c) 2005-2006 The FreeBSD Project
3154133Sharti * All rights reserved.
4154133Sharti *
5154133Sharti * Author: Victor Cruceru <soc-victor@freebsd.org>
6154133Sharti *
7154133Sharti * Redistribution of this software and documentation and use in source and
8154133Sharti * binary forms, with or without modification, are permitted provided that
9154133Sharti * the following conditions are met:
10154133Sharti *
11154133Sharti * 1. Redistributions of source code or documentation must retain the above
12154133Sharti *    copyright notice, this list of conditions and the following disclaimer.
13154133Sharti * 2. Redistributions in binary form must reproduce the above copyright
14154133Sharti *    notice, this list of conditions and the following disclaimer in the
15154133Sharti *    documentation and/or other materials provided with the distribution.
16154133Sharti *
17154133Sharti * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18154133Sharti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19154133Sharti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20154133Sharti * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21154133Sharti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22154133Sharti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23154133Sharti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24154133Sharti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25154133Sharti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26154133Sharti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27154133Sharti * SUCH DAMAGE.
28154133Sharti *
29154133Sharti * $FreeBSD$
30154133Sharti */
31154133Sharti
32154133Sharti/*
33154133Sharti * Host Resources MIB implementation for SNMPd: instrumentation for
34154133Sharti * hrPrinterTable
35154133Sharti */
36154133Sharti
37154133Sharti#include <sys/param.h>
38154133Sharti#include <sys/stat.h>
39154133Sharti
40154133Sharti#include <assert.h>
41154133Sharti#include <err.h>
42154133Sharti#include <errno.h>
43154133Sharti#include <paths.h>
44154133Sharti#include <stdlib.h>
45154133Sharti#include <string.h>
46154133Sharti#include <syslog.h>
47154133Sharti#include <unistd.h>
48154133Sharti
49154133Sharti#include "hostres_snmp.h"
50154133Sharti#include "hostres_oid.h"
51154133Sharti#include "hostres_tree.h"
52154133Sharti
53154133Sharti#include <sys/dirent.h>
54154133Sharti#include "lp.h"
55154133Sharti
56154133Sharti/* Constants */
57154133Shartistatic const struct asn_oid OIDX_hrDevicePrinter_c = OIDX_hrDevicePrinter;
58154133Sharti
59154133Shartienum PrinterStatus {
60154133Sharti	PS_OTHER	= 1,
61154133Sharti	PS_UNKNOWN	= 2,
62154133Sharti	PS_IDLE		= 3,
63154133Sharti	PS_PRINTING	= 4,
64154133Sharti	PS_WARMUP	= 5
65154133Sharti};
66154133Sharti
67154133Sharti/*
68154133Sharti * This structure is used to hold a SNMP table entry
69154133Sharti * for HOST-RESOURCES-MIB's hrPrinterTable.
70154133Sharti */
71154133Shartistruct printer_entry {
72154133Sharti	int32_t		index;
73154133Sharti	int32_t		status;  /* values from PrinterStatus enum above */
74154133Sharti	u_char		detectedErrorState[2];
75154133Sharti	TAILQ_ENTRY(printer_entry) link;
76160341Sharti#define	HR_PRINTER_FOUND		0x001
77154133Sharti	uint32_t	flags;
78154133Sharti
79154133Sharti};
80154133ShartiTAILQ_HEAD(printer_tbl, printer_entry);
81154133Sharti
82154133Sharti/* the hrPrinterTable */
83154133Shartistatic struct printer_tbl printer_tbl = TAILQ_HEAD_INITIALIZER(printer_tbl);
84154133Sharti
85154133Sharti/* last (agent) tick when hrPrinterTable was updated */
86154133Shartistatic uint64_t printer_tick;
87154133Sharti
88154133Sharti/**
89154133Sharti * Create entry into the printer table.
90154133Sharti */
91154133Shartistatic struct printer_entry *
92154133Shartiprinter_entry_create(const struct device_entry *devEntry)
93154133Sharti{
94154133Sharti	struct printer_entry *entry = NULL;
95154133Sharti
96154133Sharti	assert(devEntry != NULL);
97154133Sharti	if (devEntry == NULL)
98154133Sharti		return (NULL);
99154133Sharti
100154133Sharti	if ((entry = malloc(sizeof(*entry))) == NULL) {
101154133Sharti		syslog(LOG_WARNING, "hrPrinterTable: %s: %m", __func__);
102154133Sharti		return (NULL);
103154133Sharti	}
104154133Sharti	memset(entry, 0, sizeof(*entry));
105154133Sharti	entry->index = devEntry->index;
106154133Sharti	INSERT_OBJECT_INT(entry, &printer_tbl);
107154133Sharti	return (entry);
108154133Sharti}
109154133Sharti
110154133Sharti/**
111154133Sharti * Delete entry from the printer table.
112154133Sharti */
113154133Shartistatic void
114154133Shartiprinter_entry_delete(struct printer_entry *entry)
115154133Sharti{
116154133Sharti
117154133Sharti	assert(entry != NULL);
118154133Sharti	if (entry == NULL)
119154133Sharti		return;
120154133Sharti
121154133Sharti	TAILQ_REMOVE(&printer_tbl, entry, link);
122154133Sharti	free(entry);
123154133Sharti}
124154133Sharti
125154133Sharti/**
126154133Sharti * Find a printer by its index
127154133Sharti */
128154133Shartistatic struct printer_entry *
129154133Shartiprinter_find_by_index(int32_t idx)
130154133Sharti{
131154133Sharti	struct printer_entry *entry;
132154133Sharti
133154133Sharti	TAILQ_FOREACH(entry, &printer_tbl, link)
134154133Sharti		if (entry->index == idx)
135154133Sharti			return (entry);
136154133Sharti
137154133Sharti	return (NULL);
138154133Sharti}
139154133Sharti
140154133Sharti/**
141154133Sharti * Get the status of a printer
142154133Sharti */
143154133Shartistatic enum PrinterStatus
144154133Shartiget_printer_status(const struct printer *pp)
145154133Sharti{
146154133Sharti	char statfile[MAXPATHLEN];
147154133Sharti	char lockfile[MAXPATHLEN];
148154133Sharti	char fline[128];
149154133Sharti	int fd;
150154133Sharti	FILE *f = NULL;
151154133Sharti	enum PrinterStatus ps = PS_UNKNOWN;
152154133Sharti
153154133Sharti	if (pp->lock_file[0] == '/')
154154133Sharti		strlcpy(lockfile, pp->lock_file, sizeof(lockfile));
155154133Sharti	else
156154133Sharti		snprintf(lockfile, sizeof(lockfile), "%s/%s",
157154133Sharti		    pp->spool_dir, pp->lock_file);
158154133Sharti
159154133Sharti	fd = open(lockfile, O_RDONLY);
160154133Sharti	if (fd < 0 || flock(fd, LOCK_SH | LOCK_NB) == 0) {
161154133Sharti		ps = PS_IDLE;
162154133Sharti		goto LABEL_DONE;
163154133Sharti	}
164154133Sharti
165154133Sharti	if (pp->status_file[0] == '/')
166154133Sharti		strlcpy(statfile, pp->status_file, sizeof(statfile));
167154133Sharti	else
168154133Sharti		snprintf(statfile, sizeof(statfile), "%s/%s",
169154133Sharti		    pp->spool_dir, pp->status_file);
170154133Sharti
171154133Sharti	f = fopen(statfile, "r");
172154133Sharti	if (f == NULL) {
173154133Sharti		syslog(LOG_ERR, "cannot open status file: %s", strerror(errno));
174154133Sharti		ps = PS_UNKNOWN;
175154133Sharti		goto LABEL_DONE;
176154133Sharti	}
177154133Sharti
178285851Spfg	memset(&fline[0], '\0', sizeof(fline));
179154133Sharti	if (fgets(fline, sizeof(fline) -1, f) == NULL) {
180154133Sharti		ps = PS_UNKNOWN;
181154133Sharti		goto LABEL_DONE;
182154133Sharti	}
183154133Sharti
184154133Sharti	if (strstr(fline, "is ready and printing") != NULL) {
185154133Sharti		ps = PS_PRINTING;
186154133Sharti		goto LABEL_DONE;
187154133Sharti	}
188154133Sharti
189154133Sharti	if (strstr(fline, "to become ready (offline?)") != NULL) {
190154133Sharti		ps = PS_OTHER;
191154133Sharti		goto LABEL_DONE;
192154133Sharti	}
193154133Sharti
194154133ShartiLABEL_DONE:
195154133Sharti	if (fd >= 0)
196154133Sharti		(void)close(fd);	/* unlocks as well */
197154133Sharti
198154133Sharti	if (f != NULL)
199154133Sharti		(void)fclose(f);
200154133Sharti
201154133Sharti	return (ps);
202154133Sharti}
203154133Sharti
204154133Sharti/**
205154133Sharti * Called for each printer found in /etc/printcap.
206154133Sharti */
207154133Shartistatic void
208154133Shartihandle_printer(struct printer *pp)
209154133Sharti{
210154133Sharti	struct device_entry *dev_entry;
211154133Sharti	struct printer_entry *printer_entry;
212154133Sharti	char dev_only[128];
213154133Sharti	struct stat sb;
214154133Sharti
215154133Sharti	if (pp->remote_host != NULL) {
216154133Sharti		HRDBG("skipped %s -- remote", pp->printer);
217154133Sharti		return;
218154133Sharti	}
219154133Sharti
220154133Sharti	if (strncmp(pp->lp, _PATH_DEV, strlen(_PATH_DEV)) != 0) {
221154133Sharti		HRDBG("skipped %s [device %s] -- remote", pp->printer, pp->lp);
222154133Sharti		return;
223154133Sharti	}
224154133Sharti
225154133Sharti	memset(dev_only, '\0', sizeof(dev_only));
226154133Sharti	snprintf(dev_only, sizeof(dev_only), "%s", pp->lp + strlen(_PATH_DEV));
227154133Sharti
228154133Sharti	HRDBG("printer %s has device %s", pp->printer, dev_only);
229154133Sharti
230154133Sharti	if (stat(pp->lp, &sb) < 0) {
231154133Sharti		if (errno == ENOENT) {
232154133Sharti			HRDBG("skipped %s -- device %s missing",
233154133Sharti			    pp->printer, pp->lp);
234154133Sharti			return;
235154133Sharti		}
236154133Sharti	}
237154133Sharti
238154133Sharti	if ((dev_entry = device_find_by_name(dev_only)) == NULL) {
239154133Sharti		HRDBG("%s not in hrDeviceTable", pp->lp);
240154133Sharti		return;
241154133Sharti	}
242154133Sharti	HRDBG("%s found in hrDeviceTable", pp->lp);
243160341Sharti	dev_entry->type = &OIDX_hrDevicePrinter_c;
244154133Sharti
245154133Sharti	dev_entry->flags |= HR_DEVICE_IMMUTABLE;
246154133Sharti
247154133Sharti	/* Then check hrPrinterTable for this device */
248154133Sharti	if ((printer_entry = printer_find_by_index(dev_entry->index)) == NULL &&
249154133Sharti	    (printer_entry = printer_entry_create(dev_entry)) == NULL)
250154133Sharti		return;
251154133Sharti
252154133Sharti	printer_entry->flags |= HR_PRINTER_FOUND;
253154133Sharti	printer_entry->status = get_printer_status(pp);
254154133Sharti	memset(printer_entry->detectedErrorState, 0,
255154133Sharti	    sizeof(printer_entry->detectedErrorState));
256154133Sharti}
257154133Sharti
258154133Shartistatic void
259154133ShartihrPrinter_get_OS_entries(void)
260154133Sharti{
261154133Sharti	int  status, more;
262154133Sharti	struct printer myprinter, *pp = &myprinter;
263154133Sharti
264154133Sharti	init_printer(pp);
265154133Sharti	HRDBG("---->Getting printers .....");
266154133Sharti	more = firstprinter(pp, &status);
267154133Sharti	if (status)
268154133Sharti		goto errloop;
269154133Sharti
270154133Sharti	while (more) {
271154133Sharti		do {
272154133Sharti			HRDBG("---->Got printer %s", pp->printer);
273154133Sharti
274154133Sharti			handle_printer(pp);
275154133Sharti			more = nextprinter(pp, &status);
276154133Shartierrloop:
277154133Sharti			if (status)
278154133Sharti				syslog(LOG_WARNING,
279154133Sharti				    "hrPrinterTable: printcap entry for %s "
280154133Sharti				    "has errors, skipping",
281154133Sharti				    pp->printer ? pp->printer : "<noname?>");
282154133Sharti		} while (more && status);
283154133Sharti	}
284154133Sharti
285154133Sharti	lastprinter();
286154133Sharti	printer_tick = this_tick;
287154133Sharti}
288154133Sharti
289154133Sharti/**
290154133Sharti * Init the things for hrPrinterTable
291154133Sharti */
292154133Shartivoid
293154133Shartiinit_printer_tbl(void)
294154133Sharti{
295154133Sharti
296154133Sharti	hrPrinter_get_OS_entries();
297154133Sharti}
298154133Sharti
299154133Sharti/**
300154133Sharti * Finalization routine for hrPrinterTable
301154133Sharti * It destroys the lists and frees any allocated heap memory
302154133Sharti */
303154133Shartivoid
304154133Shartifini_printer_tbl(void)
305154133Sharti{
306154133Sharti	struct printer_entry *n1;
307154133Sharti
308154133Sharti	while ((n1 = TAILQ_FIRST(&printer_tbl)) != NULL) {
309154133Sharti		TAILQ_REMOVE(&printer_tbl, n1, link);
310154133Sharti		free(n1);
311154133Sharti	}
312154133Sharti}
313154133Sharti
314154133Sharti/**
315154133Sharti * Refresh the printer table if needed.
316154133Sharti */
317154133Shartivoid
318154133Shartirefresh_printer_tbl(void)
319154133Sharti{
320154133Sharti	struct printer_entry *entry;
321154133Sharti	struct printer_entry *entry_tmp;
322154133Sharti
323154133Sharti	if (this_tick <= printer_tick) {
324154133Sharti		HRDBG("no refresh needed");
325154133Sharti		return;
326154133Sharti	}
327154133Sharti
328154133Sharti	/* mark each entry as missing */
329154133Sharti	TAILQ_FOREACH(entry, &printer_tbl, link)
330154133Sharti		entry->flags &= ~HR_PRINTER_FOUND;
331154133Sharti
332154133Sharti	hrPrinter_get_OS_entries();
333154133Sharti
334154133Sharti	/*
335154133Sharti	 * Purge items that disappeared
336154133Sharti	 */
337154133Sharti	entry = TAILQ_FIRST(&printer_tbl);
338154133Sharti	while (entry != NULL) {
339154133Sharti		entry_tmp = TAILQ_NEXT(entry, link);
340154133Sharti		if (!(entry->flags & HR_PRINTER_FOUND))
341154133Sharti			printer_entry_delete(entry);
342154133Sharti		entry = entry_tmp;
343154133Sharti	}
344154133Sharti
345154133Sharti	printer_tick = this_tick;
346154133Sharti
347154133Sharti	HRDBG("refresh DONE ");
348154133Sharti}
349154133Sharti
350154133Shartiint
351154133Shartiop_hrPrinterTable(struct snmp_context *ctx __unused, struct snmp_value *value,
352154133Sharti    u_int sub, u_int iidx __unused, enum snmp_op curr_op)
353154133Sharti{
354154133Sharti	struct printer_entry *entry;
355154133Sharti
356154133Sharti	refresh_printer_tbl();
357154133Sharti
358154133Sharti	switch (curr_op) {
359154133Sharti
360154133Sharti	case SNMP_OP_GETNEXT:
361154133Sharti		if ((entry = NEXT_OBJECT_INT(&printer_tbl, &value->var,
362154133Sharti		    sub)) == NULL)
363154133Sharti			return (SNMP_ERR_NOSUCHNAME);
364154133Sharti		value->var.len = sub + 1;
365154133Sharti		value->var.subs[sub] = entry->index;
366154133Sharti		goto get;
367154133Sharti
368154133Sharti	case SNMP_OP_GET:
369154133Sharti		if ((entry = FIND_OBJECT_INT(&printer_tbl, &value->var,
370154133Sharti		    sub)) == NULL)
371154133Sharti			return (SNMP_ERR_NOSUCHNAME);
372154133Sharti		goto get;
373154133Sharti
374154133Sharti	case SNMP_OP_SET:
375154133Sharti		if ((entry = FIND_OBJECT_INT(&printer_tbl, &value->var,
376154133Sharti		    sub)) == NULL)
377154133Sharti			return (SNMP_ERR_NO_CREATION);
378154133Sharti		return (SNMP_ERR_NOT_WRITEABLE);
379154133Sharti
380154133Sharti	case SNMP_OP_ROLLBACK:
381154133Sharti	case SNMP_OP_COMMIT:
382154133Sharti		abort();
383154133Sharti	}
384154133Sharti	abort();
385154133Sharti
386154133Sharti  get:
387154133Sharti	switch (value->var.subs[sub - 1]) {
388154133Sharti
389154133Sharti	case LEAF_hrPrinterStatus:
390154133Sharti		value->v.integer = entry->status;
391154133Sharti		return (SNMP_ERR_NOERROR);
392154133Sharti
393154133Sharti	case LEAF_hrPrinterDetectedErrorState:
394154133Sharti		return (string_get(value, entry->detectedErrorState,
395154133Sharti		    sizeof(entry->detectedErrorState)));
396154133Sharti	}
397154133Sharti	abort();
398154133Sharti}
399