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