]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/snmp-supplies.c
Merge changes from CUPS 1.4svn-r8329.
[thirdparty/cups.git] / backend / snmp-supplies.c
1 /*
2 * "$Id$"
3 *
4 * SNMP supplies functions for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2008-2009 by Apple Inc.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * "LICENSE" which should have been included with this file. If this
12 * file is missing or damaged, see the license at "http://www.cups.org/".
13 *
14 * This file is subject to the Apple OS-Developed Software exception.
15 *
16 * Contents:
17 *
18 * backendSNMPSupplies() - Get the current supplies for a device.
19 * backend_init_supplies() - Initialize the supplies list.
20 * backend_walk_cb() - Interpret the supply value responses...
21 */
22
23 /*
24 * Include necessary headers.
25 */
26
27 #include "backend-private.h"
28 #include <cups/array.h>
29
30
31 /*
32 * Local constants...
33 */
34
35 #define CUPS_MAX_SUPPLIES 32 /* Maximum number of supplies for a printer */
36
37
38 /*
39 * Local structures...
40 */
41
42 typedef struct /**** Printer supply data ****/
43 {
44 char name[CUPS_SNMP_MAX_STRING], /* Name of supply */
45 color[8]; /* Color: "#RRGGBB" or "none" */
46 int colorant, /* Colorant index */
47 type, /* Supply type */
48 max_capacity, /* Maximum capacity */
49 level; /* Current level value */
50 } backend_supplies_t;
51
52 typedef struct /**** Printer state table ****/
53 {
54 int bit; /* State bit */
55 const char *keyword; /* IPP printer-state-reasons keyword */
56 } backend_state_t;
57
58
59 /*
60 * Local globals...
61 */
62
63 static http_addr_t current_addr; /* Current address */
64 static int current_state = -1;
65 /* Current device state bits */
66 static int num_supplies = 0;
67 /* Number of supplies found */
68 static backend_supplies_t supplies[CUPS_MAX_SUPPLIES];
69 /* Supply information */
70
71 static const int hrDeviceDescr[] =
72 { CUPS_OID_hrDeviceDescr, 1, -1 };
73 /* Device description OID */
74 static const int hrPrinterStatus[] =
75 { CUPS_OID_hrPrinterStatus, 1, -1 };
76 /* Current state OID */
77 static const int hrPrinterDetectedErrorState[] =
78 { CUPS_OID_hrPrinterDetectedErrorState, 1, -1 };
79 /* Current printer state bits OID */
80 static const int prtMarkerColorantValue[] =
81 { CUPS_OID_prtMarkerColorantValue, -1 },
82 /* Colorant OID */
83 prtMarkerColorantValueOffset =
84 (sizeof(prtMarkerColorantValue) /
85 sizeof(prtMarkerColorantValue[0]));
86 /* Offset to colorant index */
87 static const int prtMarkerLifeCount[] =
88 { CUPS_OID_prtMarkerLifeCount, 1, 1, -1 };
89 /* Page counter OID */
90 static const int prtMarkerSuppliesEntry[] =
91 { CUPS_OID_prtMarkerSuppliesEntry, -1 };
92 /* Supplies OID */
93 static const int prtMarkerSuppliesColorantIndex[] =
94 { CUPS_OID_prtMarkerSuppliesColorantIndex, -1 },
95 /* Colorant index OID */
96 prtMarkerSuppliesColorantIndexOffset =
97 (sizeof(prtMarkerSuppliesColorantIndex) /
98 sizeof(prtMarkerSuppliesColorantIndex[0]));
99 /* Offset to supply index */
100 static const int prtMarkerSuppliesDescription[] =
101 { CUPS_OID_prtMarkerSuppliesDescription, -1 },
102 /* Description OID */
103 prtMarkerSuppliesDescriptionOffset =
104 (sizeof(prtMarkerSuppliesDescription) /
105 sizeof(prtMarkerSuppliesDescription[0]));
106 /* Offset to supply index */
107 static const int prtMarkerSuppliesLevel[] =
108 { CUPS_OID_prtMarkerSuppliesLevel, -1 },
109 /* Level OID */
110 prtMarkerSuppliesLevelOffset =
111 (sizeof(prtMarkerSuppliesLevel) /
112 sizeof(prtMarkerSuppliesLevel[0]));
113 /* Offset to supply index */
114 static const int prtMarkerSuppliesMaxCapacity[] =
115 { CUPS_OID_prtMarkerSuppliesMaxCapacity, -1 },
116 /* Max capacity OID */
117 prtMarkerSuppliesMaxCapacityOffset =
118 (sizeof(prtMarkerSuppliesMaxCapacity) /
119 sizeof(prtMarkerSuppliesMaxCapacity[0]));
120 /* Offset to supply index */
121 static const int prtMarkerSuppliesType[] =
122 { CUPS_OID_prtMarkerSuppliesType, -1 },
123 /* Type OID */
124 prtMarkerSuppliesTypeOffset =
125 (sizeof(prtMarkerSuppliesType) /
126 sizeof(prtMarkerSuppliesType[0]));
127 /* Offset to supply index */
128
129 static const backend_state_t const printer_states[] =
130 {
131 { CUPS_TC_lowPaper, "media-low-report" },
132 { CUPS_TC_noPaper | CUPS_TC_inputTrayEmpty, "media-empty-warning" },
133 { CUPS_TC_lowToner, "toner-low-report" },
134 { CUPS_TC_noToner, "toner-empty-warning" },
135 { CUPS_TC_doorOpen, "door-open-report" },
136 { CUPS_TC_jammed, "media-jam-warning" },
137 /* { CUPS_TC_offline, "offline-report" }, */ /* unreliable */
138 { CUPS_TC_serviceRequested | CUPS_TC_overduePreventMaint, "service-needed-warning" },
139 { CUPS_TC_inputTrayMissing, "input-tray-missing-warning" },
140 { CUPS_TC_outputTrayMissing, "output-tray-missing-warning" },
141 { CUPS_TC_markerSupplyMissing, "marker-supply-missing-warning" },
142 { CUPS_TC_outputNearFull, "output-area-almost-full-report" },
143 { CUPS_TC_outputFull, "output-area-full-warning" }
144 };
145
146
147 /*
148 * Local functions...
149 */
150
151 static void backend_init_supplies(int snmp_fd, http_addr_t *addr);
152 static void backend_walk_cb(cups_snmp_t *packet, void *data);
153
154
155 /*
156 * 'backendSNMPSupplies()' - Get the current supplies for a device.
157 */
158
159 int /* O - 0 on success, -1 on error */
160 backendSNMPSupplies(
161 int snmp_fd, /* I - SNMP socket */
162 http_addr_t *addr, /* I - Printer address */
163 int *page_count, /* O - Page count */
164 int *printer_state) /* O - Printer state */
165 {
166 if (!httpAddrEqual(addr, &current_addr))
167 backend_init_supplies(snmp_fd, addr);
168 else if (num_supplies > 0)
169 _cupsSNMPWalk(snmp_fd, &current_addr, CUPS_SNMP_VERSION_1,
170 _cupsSNMPDefaultCommunity(), prtMarkerSuppliesLevel, 0.5,
171 backend_walk_cb, NULL);
172
173 if (page_count)
174 *page_count = -1;
175
176 if (printer_state)
177 *printer_state = -1;
178
179 if (num_supplies > 0)
180 {
181 int i, /* Looping var */
182 new_state, /* New state value */
183 change_state; /* State change */
184 char value[CUPS_MAX_SUPPLIES * 4],
185 /* marker-levels value string */
186 *ptr; /* Pointer into value string */
187 cups_snmp_t packet; /* SNMP response packet */
188
189
190 /*
191 * Generate the marker-levels value string...
192 */
193
194 for (i = 0, ptr = value; i < num_supplies; i ++, ptr += strlen(ptr))
195 {
196 if (i)
197 *ptr++ = ',';
198
199 if (supplies[i].max_capacity > 0)
200 sprintf(ptr, "%d", 100 * supplies[i].level / supplies[i].max_capacity);
201 else
202 strcpy(ptr, "-1");
203 }
204
205 fprintf(stderr, "ATTR: marker-levels=%s\n", value);
206
207 /*
208 * Get the current printer status bits...
209 */
210
211 if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
212 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
213 hrPrinterDetectedErrorState))
214 return (-1);
215
216 if (!_cupsSNMPRead(snmp_fd, &packet, 0.5) ||
217 packet.object_type != CUPS_ASN1_OCTET_STRING)
218 return (-1);
219
220 new_state = (packet.object_value.string.bytes[0] << 8) |
221 packet.object_value.string.bytes[1];
222
223 if (current_state < 0)
224 change_state = 0xffff;
225 else
226 change_state = current_state ^ new_state;
227
228 for (i = 0;
229 i < (int)(sizeof(printer_states) / sizeof(printer_states[0]));
230 i ++)
231 if (change_state & printer_states[i].bit)
232 fprintf(stderr, "STATE: %c%s\n",
233 (new_state & printer_states[i].bit) ? '+' : '-',
234 printer_states[i].keyword);
235
236 current_state = new_state;
237
238 /*
239 * Get the current printer state...
240 */
241
242 if (printer_state)
243 {
244 if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
245 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
246 hrPrinterStatus))
247 return (-1);
248
249 if (!_cupsSNMPRead(snmp_fd, &packet, 0.5) ||
250 packet.object_type != CUPS_ASN1_INTEGER)
251 return (-1);
252
253 *printer_state = packet.object_value.integer;
254 }
255
256 /*
257 * Get the current page count...
258 */
259
260 if (page_count)
261 {
262 if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
263 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
264 prtMarkerLifeCount))
265 return (-1);
266
267 if (!_cupsSNMPRead(snmp_fd, &packet, 0.5) ||
268 packet.object_type != CUPS_ASN1_COUNTER)
269 return (-1);
270
271 *page_count = packet.object_value.counter;
272 }
273
274 return (0);
275 }
276 else
277 return (-1);
278 }
279
280
281 /*
282 * 'backend_init_supplies()' - Initialize the supplies list.
283 */
284
285 static void
286 backend_init_supplies(
287 int snmp_fd, /* I - SNMP socket */
288 http_addr_t *addr) /* I - Printer address */
289 {
290 int i, /* Looping var */
291 type; /* Current marker type */
292 cups_file_t *cachefile; /* Cache file */
293 const char *cachedir; /* CUPS_CACHEDIR value */
294 char addrstr[1024], /* Address string */
295 cachefilename[1024], /* Cache filename */
296 description[CUPS_SNMP_MAX_STRING],
297 /* Device description string */
298 value[CUPS_MAX_SUPPLIES * (CUPS_SNMP_MAX_STRING * 2 + 3)],
299 /* Value string */
300 *ptr, /* Pointer into value string */
301 *name_ptr; /* Pointer into name string */
302 cups_snmp_t packet; /* SNMP response packet */
303 ppd_file_t *ppd; /* PPD file for this queue */
304 ppd_attr_t *ppdattr; /* cupsSNMPSupplies attribute */
305 static const char * const types[] = /* Supply types */
306 {
307 "other",
308 "unknown",
309 "toner",
310 "wasteToner",
311 "ink",
312 "inkCartridge",
313 "inkRibbon",
314 "wasteInk",
315 "opc",
316 "developer",
317 "fuserOil",
318 "solidWax",
319 "ribbonWax",
320 "wasteWax",
321 "fuser",
322 "coronaWire",
323 "fuserOilWick",
324 "cleanerUnit",
325 "fuserCleaningPad",
326 "transferUnit",
327 "tonerCartridge",
328 "fuserOiler",
329 "water",
330 "wasteWater",
331 "glueWaterAdditive",
332 "wastePaper",
333 "bindingSupply",
334 "bandingSupply",
335 "stitchingWire",
336 "shrinkWrap",
337 "paperWrap",
338 "staples",
339 "inserts",
340 "covers"
341 };
342
343
344 /*
345 * Reset state information...
346 */
347
348 current_addr = *addr;
349 current_state = -1;
350 num_supplies = -1;
351
352 memset(supplies, 0, sizeof(supplies));
353
354 /*
355 * See if we should be getting supply levels via SNMP...
356 */
357
358 if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL &&
359 (ppdattr = ppdFindAttr(ppd, "cupsSNMPSupplies", NULL)) != NULL &&
360 ppdattr->value && strcasecmp(ppdattr->value, "true"))
361 {
362 ppdClose(ppd);
363 return;
364 }
365
366 ppdClose(ppd);
367
368 /*
369 * Get the device description...
370 */
371
372 if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
373 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
374 hrDeviceDescr))
375 return;
376
377 if (!_cupsSNMPRead(snmp_fd, &packet, 0.5) ||
378 packet.object_type != CUPS_ASN1_OCTET_STRING)
379 {
380 strlcpy(description, "Unknown", sizeof(description));
381 num_supplies = 0;
382 }
383 else
384 strlcpy(description, (char *)packet.object_value.string.bytes,
385 sizeof(description));
386
387 /*
388 * See if we have already queried this device...
389 */
390
391 httpAddrString(addr, addrstr, sizeof(addrstr));
392
393 if ((cachedir = getenv("CUPS_CACHEDIR")) == NULL)
394 cachedir = CUPS_CACHEDIR;
395
396 snprintf(cachefilename, sizeof(cachefilename), "%s/%s.snmp", cachedir,
397 addrstr);
398
399 if ((cachefile = cupsFileOpen(cachefilename, "r")) != NULL)
400 {
401 /*
402 * Yes, read the cache file:
403 *
404 * 1 num_supplies
405 * device description
406 * supply structures...
407 */
408
409 if (cupsFileGets(cachefile, value, sizeof(value)))
410 {
411 if (sscanf(value, "1 %d", &num_supplies) == 1 &&
412 num_supplies <= CUPS_MAX_SUPPLIES &&
413 cupsFileGets(cachefile, value, sizeof(value)))
414 {
415 if ((ptr = value + strlen(value) - 1) >= value && *ptr == '\n')
416 *ptr = '\n';
417
418 if (!strcmp(description, value))
419 cupsFileRead(cachefile, (char *)supplies,
420 num_supplies * sizeof(backend_supplies_t));
421 else
422 num_supplies = -1;
423 }
424 else
425 num_supplies = -1;
426 }
427
428 cupsFileClose(cachefile);
429 }
430
431 /*
432 * If the cache information isn't correct, scan for supplies...
433 */
434
435 if (num_supplies < 0)
436 {
437 /*
438 * Walk the printer configuration information...
439 */
440
441 _cupsSNMPWalk(snmp_fd, &current_addr, CUPS_SNMP_VERSION_1,
442 _cupsSNMPDefaultCommunity(), prtMarkerSuppliesEntry, 0.5,
443 backend_walk_cb, NULL);
444 }
445
446 /*
447 * Save the cached information...
448 */
449
450 if (num_supplies < 0)
451 num_supplies = 0;
452
453 if ((cachefile = cupsFileOpen(cachefilename, "w")) != NULL)
454 {
455 cupsFilePrintf(cachefile, "1 %d\n", num_supplies);
456 cupsFilePrintf(cachefile, "%s\n", description);
457
458 if (num_supplies > 0)
459 cupsFileWrite(cachefile, (char *)supplies,
460 num_supplies * sizeof(backend_supplies_t));
461
462 cupsFileClose(cachefile);
463 }
464
465 if (num_supplies <= 0)
466 return;
467
468 /*
469 * Get the colors...
470 */
471
472 for (i = 0; i < num_supplies; i ++)
473 strcpy(supplies[i].color, "none");
474
475 _cupsSNMPWalk(snmp_fd, &current_addr, CUPS_SNMP_VERSION_1,
476 _cupsSNMPDefaultCommunity(), prtMarkerColorantValue, 0.5,
477 backend_walk_cb, NULL);
478
479 /*
480 * Output the marker-colors attribute...
481 */
482
483 for (i = 0, ptr = value; i < num_supplies; i ++, ptr += strlen(ptr))
484 {
485 if (i)
486 *ptr++ = ',';
487
488 strcpy(ptr, supplies[i].color);
489 }
490
491 fprintf(stderr, "ATTR: marker-colors=%s\n", value);
492
493 /*
494 * Output the marker-names attribute...
495 */
496
497 for (i = 0, ptr = value; i < num_supplies; i ++)
498 {
499 if (i)
500 *ptr++ = ',';
501
502 *ptr++ = '\"';
503 for (name_ptr = supplies[i].name; *name_ptr;)
504 {
505 if (*name_ptr == '\\' || *name_ptr == '\"')
506 *ptr++ = '\\';
507
508 *ptr++ = *name_ptr++;
509 }
510 *ptr++ = '\"';
511 }
512
513 *ptr = '\0';
514
515 fprintf(stderr, "ATTR: marker-names=%s\n", value);
516
517 /*
518 * Output the marker-types attribute...
519 */
520
521 for (i = 0, ptr = value; i < num_supplies; i ++, ptr += strlen(ptr))
522 {
523 if (i)
524 *ptr++ = ',';
525
526 type = supplies[i].type;
527
528 if (type < CUPS_TC_other || type > CUPS_TC_covers)
529 strcpy(ptr, "unknown");
530 else
531 strcpy(ptr, types[type - CUPS_TC_other]);
532 }
533
534 fprintf(stderr, "ATTR: marker-types=%s\n", value);
535 }
536
537
538 /*
539 * 'backend_walk_cb()' - Interpret the supply value responses...
540 */
541
542 static void
543 backend_walk_cb(cups_snmp_t *packet, /* I - SNMP packet */
544 void *data) /* I - User data (unused) */
545 {
546 int i, j, k; /* Looping vars */
547 static const char * const colors[8][2] =
548 { /* Standard color names */
549 { "black", "#000000" },
550 { "blue", "#0000FF" },
551 { "cyan", "#00FFFF" },
552 { "green", "#00FF00" },
553 { "magenta", "#FF00FF" },
554 { "red", "#FF0000" },
555 { "white", "#FFFFFF" },
556 { "yellow", "#FFFF00" }
557 };
558
559
560 (void)data;
561
562 if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerColorantValue) &&
563 packet->object_type == CUPS_ASN1_OCTET_STRING)
564 {
565 /*
566 * Get colorant...
567 */
568
569 i = packet->object_name[prtMarkerColorantValueOffset];
570
571 fprintf(stderr, "DEBUG2: prtMarkerColorantValue.1.%d = \"%s\"\n", i,
572 (char *)packet->object_value.string.bytes);
573
574 for (j = 0; j < num_supplies; j ++)
575 if (supplies[j].colorant == i)
576 {
577 for (k = 0; k < (int)(sizeof(colors) / sizeof(colors[0])); k ++)
578 if (!strcmp(colors[k][0], (char *)packet->object_value.string.bytes))
579 {
580 strcpy(supplies[j].color, colors[k][1]);
581 break;
582 }
583 }
584 }
585 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesColorantIndex))
586 {
587 /*
588 * Get colorant index...
589 */
590
591 i = packet->object_name[prtMarkerSuppliesColorantIndexOffset];
592 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
593 packet->object_type != CUPS_ASN1_INTEGER)
594 return;
595
596 fprintf(stderr, "DEBUG2: prtMarkerSuppliesColorantIndex.1.%d = %d\n", i,
597 packet->object_value.integer);
598
599 if (i > num_supplies)
600 num_supplies = i;
601
602 supplies[i - 1].colorant = packet->object_value.integer;
603 }
604 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesDescription))
605 {
606 /*
607 * Get supply name/description...
608 */
609
610 i = packet->object_name[prtMarkerSuppliesDescriptionOffset];
611 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
612 packet->object_type != CUPS_ASN1_OCTET_STRING)
613 return;
614
615 fprintf(stderr, "DEBUG2: prtMarkerSuppliesDescription.1.%d = \"%s\"\n", i,
616 (char *)packet->object_value.string.bytes);
617
618 if (i > num_supplies)
619 num_supplies = i;
620
621 strlcpy(supplies[i - 1].name, (char *)packet->object_value.string.bytes,
622 sizeof(supplies[0].name));
623 }
624 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesLevel))
625 {
626 /*
627 * Get level...
628 */
629
630 i = packet->object_name[prtMarkerSuppliesLevelOffset];
631 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
632 packet->object_type != CUPS_ASN1_INTEGER)
633 return;
634
635 fprintf(stderr, "DEBUG2: prtMarkerSuppliesLevel.1.%d = %d\n", i,
636 packet->object_value.integer);
637
638 if (i > num_supplies)
639 num_supplies = i;
640
641 supplies[i - 1].level = packet->object_value.integer;
642 }
643 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesMaxCapacity))
644 {
645 /*
646 * Get max capacity...
647 */
648
649 i = packet->object_name[prtMarkerSuppliesMaxCapacityOffset];
650 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
651 packet->object_type != CUPS_ASN1_INTEGER)
652 return;
653
654 fprintf(stderr, "DEBUG2: prtMarkerSuppliesMaxCapacity.1.%d = %d\n", i,
655 packet->object_value.integer);
656
657 if (i > num_supplies)
658 num_supplies = i;
659
660 supplies[i - 1].max_capacity = packet->object_value.integer;
661 }
662 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesType))
663 {
664 /*
665 * Get marker type...
666 */
667
668 i = packet->object_name[prtMarkerSuppliesTypeOffset];
669 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
670 packet->object_type != CUPS_ASN1_INTEGER)
671 return;
672
673 fprintf(stderr, "DEBUG2: prtMarkerSuppliesType.1.%d = %d\n", i,
674 packet->object_value.integer);
675
676 if (i > num_supplies)
677 num_supplies = i;
678
679 supplies[i - 1].type = packet->object_value.integer;
680 }
681 }
682
683
684 /*
685 * End of "$Id$".
686 */