1 /*-
2 * Copyright (c) 2005-2006 The FreeBSD Project
3 * All rights reserved.
4 *
5 * Author: Victor Cruceru <soc-victor@freebsd.org>
6 *
7 * Redistribution of this software and documentation and use in source and
8 * binary forms, with or without modification, are permitted provided that
9 * the following conditions are met:
10 *
11 * 1. Redistributions of source code or documentation must retain the above
12 * copyright notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 /*
31 * Host Resources MIB implementation for SNMPd: instrumentation for
32 * hrPrinterTable
33 */
34
35 #include <sys/param.h>
36 #include <sys/stat.h>
37
38 #include <assert.h>
39 #include <err.h>
40 #include <errno.h>
41 #include <paths.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <syslog.h>
45 #include <unistd.h>
46
47 #include "hostres_snmp.h"
48 #include "hostres_oid.h"
49 #include "hostres_tree.h"
50
51 #include <sys/dirent.h>
52 #include "lp.h"
53
54 /* Constants */
55 static const struct asn_oid OIDX_hrDevicePrinter_c = OIDX_hrDevicePrinter;
56
57 enum PrinterStatus {
58 PS_OTHER = 1,
59 PS_UNKNOWN = 2,
60 PS_IDLE = 3,
61 PS_PRINTING = 4,
62 PS_WARMUP = 5
63 };
64
65 /*
66 * This structure is used to hold a SNMP table entry
67 * for HOST-RESOURCES-MIB's hrPrinterTable.
68 */
69 struct printer_entry {
70 int32_t index;
71 int32_t status; /* values from PrinterStatus enum above */
72 u_char detectedErrorState[2];
73 TAILQ_ENTRY(printer_entry) link;
74 #define HR_PRINTER_FOUND 0x001
75 uint32_t flags;
76
77 };
78 TAILQ_HEAD(printer_tbl, printer_entry);
79
80 /* the hrPrinterTable */
81 static struct printer_tbl printer_tbl = TAILQ_HEAD_INITIALIZER(printer_tbl);
82
83 /* last (agent) tick when hrPrinterTable was updated */
84 static uint64_t printer_tick;
85
86 /**
87 * Create entry into the printer table.
88 */
89 static struct printer_entry *
printer_entry_create(const struct device_entry * devEntry)90 printer_entry_create(const struct device_entry *devEntry)
91 {
92 struct printer_entry *entry = NULL;
93
94 assert(devEntry != NULL);
95 if (devEntry == NULL)
96 return (NULL);
97
98 if ((entry = malloc(sizeof(*entry))) == NULL) {
99 syslog(LOG_WARNING, "hrPrinterTable: %s: %m", __func__);
100 return (NULL);
101 }
102 memset(entry, 0, sizeof(*entry));
103 entry->index = devEntry->index;
104 INSERT_OBJECT_INT(entry, &printer_tbl);
105 return (entry);
106 }
107
108 /**
109 * Delete entry from the printer table.
110 */
111 static void
printer_entry_delete(struct printer_entry * entry)112 printer_entry_delete(struct printer_entry *entry)
113 {
114
115 assert(entry != NULL);
116 if (entry == NULL)
117 return;
118
119 TAILQ_REMOVE(&printer_tbl, entry, link);
120 free(entry);
121 }
122
123 /**
124 * Find a printer by its index
125 */
126 static struct printer_entry *
printer_find_by_index(int32_t idx)127 printer_find_by_index(int32_t idx)
128 {
129 struct printer_entry *entry;
130
131 TAILQ_FOREACH(entry, &printer_tbl, link)
132 if (entry->index == idx)
133 return (entry);
134
135 return (NULL);
136 }
137
138 /**
139 * Get the status of a printer
140 */
141 static enum PrinterStatus
get_printer_status(const struct printer * pp)142 get_printer_status(const struct printer *pp)
143 {
144 char statfile[MAXPATHLEN];
145 char lockfile[MAXPATHLEN];
146 char fline[128];
147 int fd;
148 FILE *f = NULL;
149 enum PrinterStatus ps = PS_UNKNOWN;
150
151 if (pp->lock_file[0] == '/')
152 strlcpy(lockfile, pp->lock_file, sizeof(lockfile));
153 else
154 snprintf(lockfile, sizeof(lockfile), "%s/%s",
155 pp->spool_dir, pp->lock_file);
156
157 fd = open(lockfile, O_RDONLY);
158 if (fd < 0 || flock(fd, LOCK_SH | LOCK_NB) == 0) {
159 ps = PS_IDLE;
160 goto LABEL_DONE;
161 }
162
163 if (pp->status_file[0] == '/')
164 strlcpy(statfile, pp->status_file, sizeof(statfile));
165 else
166 snprintf(statfile, sizeof(statfile), "%s/%s",
167 pp->spool_dir, pp->status_file);
168
169 f = fopen(statfile, "r");
170 if (f == NULL) {
171 syslog(LOG_ERR, "cannot open status file: %s", strerror(errno));
172 ps = PS_UNKNOWN;
173 goto LABEL_DONE;
174 }
175
176 memset(&fline[0], '\0', sizeof(fline));
177 if (fgets(fline, sizeof(fline) -1, f) == NULL) {
178 ps = PS_UNKNOWN;
179 goto LABEL_DONE;
180 }
181
182 if (strstr(fline, "is ready and printing") != NULL) {
183 ps = PS_PRINTING;
184 goto LABEL_DONE;
185 }
186
187 if (strstr(fline, "to become ready (offline?)") != NULL) {
188 ps = PS_OTHER;
189 goto LABEL_DONE;
190 }
191
192 LABEL_DONE:
193 if (fd >= 0)
194 (void)close(fd); /* unlocks as well */
195
196 if (f != NULL)
197 (void)fclose(f);
198
199 return (ps);
200 }
201
202 /**
203 * Called for each printer found in /etc/printcap.
204 */
205 static void
handle_printer(struct printer * pp)206 handle_printer(struct printer *pp)
207 {
208 struct device_entry *dev_entry;
209 struct printer_entry *printer_entry;
210 char dev_only[128];
211 struct stat sb;
212
213 if (pp->remote_host != NULL) {
214 HRDBG("skipped %s -- remote", pp->printer);
215 return;
216 }
217
218 if (strncmp(pp->lp, _PATH_DEV, strlen(_PATH_DEV)) != 0) {
219 HRDBG("skipped %s [device %s] -- remote", pp->printer, pp->lp);
220 return;
221 }
222
223 memset(dev_only, '\0', sizeof(dev_only));
224 snprintf(dev_only, sizeof(dev_only), "%s", pp->lp + strlen(_PATH_DEV));
225
226 HRDBG("printer %s has device %s", pp->printer, dev_only);
227
228 if (stat(pp->lp, &sb) < 0) {
229 if (errno == ENOENT) {
230 HRDBG("skipped %s -- device %s missing",
231 pp->printer, pp->lp);
232 return;
233 }
234 }
235
236 if ((dev_entry = device_find_by_name(dev_only)) == NULL) {
237 HRDBG("%s not in hrDeviceTable", pp->lp);
238 return;
239 }
240 HRDBG("%s found in hrDeviceTable", pp->lp);
241 dev_entry->type = &OIDX_hrDevicePrinter_c;
242
243 dev_entry->flags |= HR_DEVICE_IMMUTABLE;
244
245 /* Then check hrPrinterTable for this device */
246 if ((printer_entry = printer_find_by_index(dev_entry->index)) == NULL &&
247 (printer_entry = printer_entry_create(dev_entry)) == NULL)
248 return;
249
250 printer_entry->flags |= HR_PRINTER_FOUND;
251 printer_entry->status = get_printer_status(pp);
252 memset(printer_entry->detectedErrorState, 0,
253 sizeof(printer_entry->detectedErrorState));
254 }
255
256 static void
hrPrinter_get_OS_entries(void)257 hrPrinter_get_OS_entries(void)
258 {
259 int status, more;
260 struct printer myprinter, *pp = &myprinter;
261
262 init_printer(pp);
263 HRDBG("---->Getting printers .....");
264 more = firstprinter(pp, &status);
265 if (status)
266 goto errloop;
267
268 while (more) {
269 do {
270 HRDBG("---->Got printer %s", pp->printer);
271
272 handle_printer(pp);
273 more = nextprinter(pp, &status);
274 errloop:
275 if (status)
276 syslog(LOG_WARNING,
277 "hrPrinterTable: printcap entry for %s "
278 "has errors, skipping",
279 pp->printer ? pp->printer : "<noname?>");
280 } while (more && status);
281 }
282
283 lastprinter();
284 printer_tick = this_tick;
285 }
286
287 /**
288 * Init the things for hrPrinterTable
289 */
290 void
init_printer_tbl(void)291 init_printer_tbl(void)
292 {
293
294 hrPrinter_get_OS_entries();
295 }
296
297 /**
298 * Finalization routine for hrPrinterTable
299 * It destroys the lists and frees any allocated heap memory
300 */
301 void
fini_printer_tbl(void)302 fini_printer_tbl(void)
303 {
304 struct printer_entry *n1;
305
306 while ((n1 = TAILQ_FIRST(&printer_tbl)) != NULL) {
307 TAILQ_REMOVE(&printer_tbl, n1, link);
308 free(n1);
309 }
310 }
311
312 /**
313 * Refresh the printer table if needed.
314 */
315 void
refresh_printer_tbl(void)316 refresh_printer_tbl(void)
317 {
318 struct printer_entry *entry;
319 struct printer_entry *entry_tmp;
320
321 if (this_tick <= printer_tick) {
322 HRDBG("no refresh needed");
323 return;
324 }
325
326 /* mark each entry as missing */
327 TAILQ_FOREACH(entry, &printer_tbl, link)
328 entry->flags &= ~HR_PRINTER_FOUND;
329
330 hrPrinter_get_OS_entries();
331
332 /*
333 * Purge items that disappeared
334 */
335 entry = TAILQ_FIRST(&printer_tbl);
336 while (entry != NULL) {
337 entry_tmp = TAILQ_NEXT(entry, link);
338 if (!(entry->flags & HR_PRINTER_FOUND))
339 printer_entry_delete(entry);
340 entry = entry_tmp;
341 }
342
343 printer_tick = this_tick;
344
345 HRDBG("refresh DONE ");
346 }
347
348 int
op_hrPrinterTable(struct snmp_context * ctx __unused,struct snmp_value * value,u_int sub,u_int iidx __unused,enum snmp_op curr_op)349 op_hrPrinterTable(struct snmp_context *ctx __unused, struct snmp_value *value,
350 u_int sub, u_int iidx __unused, enum snmp_op curr_op)
351 {
352 struct printer_entry *entry;
353
354 refresh_printer_tbl();
355
356 switch (curr_op) {
357
358 case SNMP_OP_GETNEXT:
359 if ((entry = NEXT_OBJECT_INT(&printer_tbl, &value->var,
360 sub)) == NULL)
361 return (SNMP_ERR_NOSUCHNAME);
362 value->var.len = sub + 1;
363 value->var.subs[sub] = entry->index;
364 goto get;
365
366 case SNMP_OP_GET:
367 if ((entry = FIND_OBJECT_INT(&printer_tbl, &value->var,
368 sub)) == NULL)
369 return (SNMP_ERR_NOSUCHNAME);
370 goto get;
371
372 case SNMP_OP_SET:
373 if ((entry = FIND_OBJECT_INT(&printer_tbl, &value->var,
374 sub)) == NULL)
375 return (SNMP_ERR_NO_CREATION);
376 return (SNMP_ERR_NOT_WRITEABLE);
377
378 case SNMP_OP_ROLLBACK:
379 case SNMP_OP_COMMIT:
380 abort();
381 }
382 abort();
383
384 get:
385 switch (value->var.subs[sub - 1]) {
386
387 case LEAF_hrPrinterStatus:
388 value->v.integer = entry->status;
389 return (SNMP_ERR_NOERROR);
390
391 case LEAF_hrPrinterDetectedErrorState:
392 return (string_get(value, entry->detectedErrorState,
393 sizeof(entry->detectedErrorState)));
394 }
395 abort();
396 }
397