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 } 399