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 * 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 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 * 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 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 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 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 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 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 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 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