4 * SNMP supplies functions for the Common UNIX Printing System (CUPS).
6 * Copyright 2008 by Apple Inc.
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/".
14 * This file is subject to the Apple OS-Developed Software exception.
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...
24 * Include necessary headers.
27 #include "backend-private.h"
28 #include <cups/array.h>
35 #define CUPS_MAX_SUPPLIES 32 /* Maximum number of supplies for a printer */
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 */
57 static http_addr_t current_addr
; /* Current address */
58 static int num_supplies
= 0;
59 /* Number of supplies found */
60 static backend_supplies_t supplies
[CUPS_MAX_SUPPLIES
];
61 /* Supply information */
63 static const int hrDeviceDescr
[] =
64 { CUPS_OID_hrDeviceDescr
, 1, -1 };
65 /* Device description OID */
66 static const int hrPrinterStatus
[] =
67 { CUPS_OID_hrPrinterStatus
, 1, -1 };
68 /* Current state OID */
69 static const int hrPrinterDetectedErrorState
[] =
70 { CUPS_OID_hrPrinterDetectedErrorState
, 1, -1 };
71 /* Current printer state bits OID */
72 static const int prtMarkerColorantValue
[] =
73 { CUPS_OID_prtMarkerColorantValue
, -1 },
75 prtMarkerColorantValueOffset
=
76 (sizeof(prtMarkerColorantValue
) /
77 sizeof(prtMarkerColorantValue
[0]));
78 /* Offset to colorant index */
79 static const int prtMarkerLifeCount
[] =
80 { CUPS_OID_prtMarkerLifeCount
, 1, 1, -1 };
81 /* Page counter OID */
82 static const int prtMarkerSuppliesEntry
[] =
83 { CUPS_OID_prtMarkerSuppliesEntry
, -1 };
85 static const int prtMarkerSuppliesColorantIndex
[] =
86 { CUPS_OID_prtMarkerSuppliesColorantIndex
, -1 },
87 /* Colorant index OID */
88 prtMarkerSuppliesColorantIndexOffset
=
89 (sizeof(prtMarkerSuppliesColorantIndex
) /
90 sizeof(prtMarkerSuppliesColorantIndex
[0]));
91 /* Offset to supply index */
92 static const int prtMarkerSuppliesDescription
[] =
93 { CUPS_OID_prtMarkerSuppliesDescription
, -1 },
95 prtMarkerSuppliesDescriptionOffset
=
96 (sizeof(prtMarkerSuppliesDescription
) /
97 sizeof(prtMarkerSuppliesDescription
[0]));
98 /* Offset to supply index */
99 static const int prtMarkerSuppliesLevel
[] =
100 { CUPS_OID_prtMarkerSuppliesLevel
, -1 },
102 prtMarkerSuppliesLevelOffset
=
103 (sizeof(prtMarkerSuppliesLevel
) /
104 sizeof(prtMarkerSuppliesLevel
[0]));
105 /* Offset to supply index */
106 static const int prtMarkerSuppliesMaxCapacity
[] =
107 { CUPS_OID_prtMarkerSuppliesMaxCapacity
, -1 },
108 /* Max capacity OID */
109 prtMarkerSuppliesMaxCapacityOffset
=
110 (sizeof(prtMarkerSuppliesMaxCapacity
) /
111 sizeof(prtMarkerSuppliesMaxCapacity
[0]));
112 /* Offset to supply index */
113 static const int prtMarkerSuppliesType
[] =
114 { CUPS_OID_prtMarkerSuppliesType
, -1 },
116 prtMarkerSuppliesTypeOffset
=
117 (sizeof(prtMarkerSuppliesType
) /
118 sizeof(prtMarkerSuppliesType
[0]));
119 /* Offset to supply index */
126 static void backend_init_supplies(int snmp_fd
, http_addr_t
*addr
);
127 static void backend_walk_cb(cups_snmp_t
*packet
, void *data
);
131 * 'backendSNMPSupplies()' - Get the current supplies for a device.
134 int /* O - 0 on success, -1 on error */
136 int snmp_fd
, /* I - SNMP socket */
137 http_addr_t
*addr
, /* I - Printer address */
138 int *page_count
, /* O - Page count */
139 int *printer_state
) /* O - Printer state */
141 if (!httpAddrEqual(addr
, ¤t_addr
))
142 backend_init_supplies(snmp_fd
, addr
);
143 else if (num_supplies
> 0)
144 _cupsSNMPWalk(snmp_fd
, ¤t_addr
, CUPS_SNMP_VERSION_1
,
145 _cupsSNMPDefaultCommunity(), prtMarkerSuppliesLevel
, 0.5,
146 backend_walk_cb
, NULL
);
154 if (num_supplies
> 0)
156 int i
; /* Looping var */
157 char value
[CUPS_MAX_SUPPLIES
* 4],
158 /* marker-levels value string */
159 *ptr
; /* Pointer into value string */
160 cups_snmp_t packet
; /* SNMP response packet */
164 * Generate the marker-levels value string...
167 for (i
= 0, ptr
= value
; i
< num_supplies
; i
++, ptr
+= strlen(ptr
))
172 if (supplies
[i
].max_capacity
> 0)
173 sprintf(ptr
, "%d", 100 * supplies
[i
].level
/ supplies
[i
].max_capacity
);
178 fprintf(stderr
, "ATTR: marker-levels=%s\n", value
);
181 * Get the current printer status bits...
184 if (!_cupsSNMPWrite(snmp_fd
, addr
, CUPS_SNMP_VERSION_1
,
185 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST
, 1,
186 hrPrinterDetectedErrorState
))
189 if (!_cupsSNMPRead(snmp_fd
, &packet
, 0.5) ||
190 packet
.object_type
!= CUPS_ASN1_OCTET_STRING
)
193 i
= ((packet
.object_value
.string
[0] & 255) << 8) |
194 (packet
.object_value
.string
[1] & 255);
196 if (i
& CUPS_TC_lowPaper
)
197 fputs("STATE: +media-low-report\n", stderr
);
199 fputs("STATE: -media-low-report\n", stderr
);
201 if (i
& (CUPS_TC_noPaper
| CUPS_TC_inputTrayEmpty
))
202 fputs("STATE: +media-empty-warning\n", stderr
);
204 fputs("STATE: -media-empty-warning\n", stderr
);
206 if (i
& CUPS_TC_lowToner
)
207 fputs("STATE: +toner-low-report\n", stderr
);
209 fputs("STATE: -toner-low-report\n", stderr
);
211 if (i
& CUPS_TC_noToner
)
212 fputs("STATE: +toner-empty-warning\n", stderr
);
214 fputs("STATE: -toner-empty-warning\n", stderr
);
216 if (i
& CUPS_TC_doorOpen
)
217 fputs("STATE: +door-open-report\n", stderr
);
219 fputs("STATE: -door-open-report\n", stderr
);
221 if (i
& CUPS_TC_jammed
)
222 fputs("STATE: +media-jam-warning\n", stderr
);
224 fputs("STATE: -media-jam-warning\n", stderr
);
226 if (i
& CUPS_TC_offline
)
227 fputs("STATE: +offline-report\n", stderr
);
229 fputs("STATE: -offline-report\n", stderr
);
231 if (i
& (CUPS_TC_serviceRequested
| CUPS_TC_overduePreventMaint
))
232 fputs("STATE: +service-needed-warning\n", stderr
);
234 fputs("STATE: -service-needed-warning\n", stderr
);
236 if (i
& CUPS_TC_inputTrayMissing
)
237 fputs("STATE: +input-tray-missing-warning\n", stderr
);
239 fputs("STATE: -input-tray-missing-warning\n", stderr
);
241 if (i
& CUPS_TC_outputTrayMissing
)
242 fputs("STATE: +output-tray-missing-warning\n", stderr
);
244 fputs("STATE: -output-tray-missing-warning\n", stderr
);
246 if (i
& CUPS_TC_markerSupplyMissing
)
247 fputs("STATE: +marker-supply-missing-warning\n", stderr
);
249 fputs("STATE: -marker-supply-missing-warning\n", stderr
);
251 if (i
& CUPS_TC_outputNearFull
)
252 fputs("STATE: +output-area-almost-full-report\n", stderr
);
254 fputs("STATE: -output-area-almost-full-report\n", stderr
);
256 if (i
& CUPS_TC_outputFull
)
257 fputs("STATE: +output-area-full-warning\n", stderr
);
259 fputs("STATE: -output-area-full-warning\n", stderr
);
262 * Get the current printer state...
267 if (!_cupsSNMPWrite(snmp_fd
, addr
, CUPS_SNMP_VERSION_1
,
268 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST
, 1,
272 if (!_cupsSNMPRead(snmp_fd
, &packet
, 0.5) ||
273 packet
.object_type
!= CUPS_ASN1_INTEGER
)
276 *printer_state
= packet
.object_value
.integer
;
280 * Get the current page count...
285 if (!_cupsSNMPWrite(snmp_fd
, addr
, CUPS_SNMP_VERSION_1
,
286 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST
, 1,
290 if (!_cupsSNMPRead(snmp_fd
, &packet
, 0.5) ||
291 packet
.object_type
!= CUPS_ASN1_COUNTER
)
294 *page_count
= packet
.object_value
.counter
;
305 * 'backend_init_supplies()' - Initialize the supplies list.
309 backend_init_supplies(
310 int snmp_fd
, /* I - SNMP socket */
311 http_addr_t
*addr
) /* I - Printer address */
313 int i
, /* Looping var */
314 type
; /* Current marker type */
315 cups_file_t
*cachefile
; /* Cache file */
316 const char *cachedir
; /* CUPS_CACHEDIR value */
317 char addrstr
[1024], /* Address string */
318 cachefilename
[1024], /* Cache filename */
319 description
[CUPS_SNMP_MAX_STRING
],
320 /* Device description string */
321 value
[CUPS_MAX_SUPPLIES
* (CUPS_SNMP_MAX_STRING
* 2 + 3)],
323 *ptr
, /* Pointer into value string */
324 *name_ptr
; /* Pointer into name string */
325 cups_snmp_t packet
; /* SNMP response packet */
326 ppd_file_t
*ppd
; /* PPD file for this queue */
327 ppd_attr_t
*ppdattr
; /* cupsSNMPSupplies attribute */
328 static const char * const types
[] = /* Supply types */
368 * Reset state information...
371 current_addr
= *addr
;
374 memset(supplies
, 0, sizeof(supplies
));
377 * See if we should be getting supply levels via SNMP...
380 if ((ppd
= ppdOpenFile(getenv("PPD"))) != NULL
&&
381 (ppdattr
= ppdFindAttr(ppd
, "cupsSNMPSupplies", NULL
)) != NULL
&&
382 ppdattr
->value
&& strcasecmp(ppdattr
->value
, "true"))
391 * Get the device description...
394 if (!_cupsSNMPWrite(snmp_fd
, addr
, CUPS_SNMP_VERSION_1
,
395 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST
, 1,
399 if (!_cupsSNMPRead(snmp_fd
, &packet
, 0.5) ||
400 packet
.object_type
!= CUPS_ASN1_OCTET_STRING
)
402 strlcpy(description
, "Unknown", sizeof(description
));
406 strlcpy(description
, packet
.object_value
.string
, sizeof(description
));
409 * See if we have already queried this device...
412 httpAddrString(addr
, addrstr
, sizeof(addrstr
));
414 if ((cachedir
= getenv("CUPS_CACHEDIR")) == NULL
)
415 cachedir
= CUPS_CACHEDIR
;
417 snprintf(cachefilename
, sizeof(cachefilename
), "%s/%s.snmp", cachedir
,
420 if ((cachefile
= cupsFileOpen(cachefilename
, "r")) != NULL
)
423 * Yes, read the cache file:
427 * supply structures...
430 if (cupsFileGets(cachefile
, value
, sizeof(value
)))
432 if (sscanf(value
, "1 %d", &num_supplies
) == 1 &&
433 num_supplies
<= CUPS_MAX_SUPPLIES
&&
434 cupsFileGets(cachefile
, value
, sizeof(value
)))
436 if ((ptr
= value
+ strlen(value
) - 1) >= value
&& *ptr
== '\n')
439 if (!strcmp(description
, value
))
440 cupsFileRead(cachefile
, (char *)supplies
,
441 num_supplies
* sizeof(backend_supplies_t
));
449 cupsFileClose(cachefile
);
453 * If the cache information isn't correct, scan for supplies...
456 if (num_supplies
< 0)
459 * Walk the printer configuration information...
462 _cupsSNMPWalk(snmp_fd
, ¤t_addr
, CUPS_SNMP_VERSION_1
,
463 _cupsSNMPDefaultCommunity(), prtMarkerSuppliesEntry
, 0.5,
464 backend_walk_cb
, NULL
);
468 * Save the cached information...
471 if (num_supplies
< 0)
474 if ((cachefile
= cupsFileOpen(cachefilename
, "w")) != NULL
)
476 cupsFilePrintf(cachefile
, "1 %d\n", num_supplies
);
477 cupsFilePrintf(cachefile
, "%s\n", description
);
479 if (num_supplies
> 0)
480 cupsFileWrite(cachefile
, (char *)supplies
,
481 num_supplies
* sizeof(backend_supplies_t
));
483 cupsFileClose(cachefile
);
486 if (num_supplies
<= 0)
493 for (i
= 0; i
< num_supplies
; i
++)
494 strcpy(supplies
[i
].color
, "none");
496 _cupsSNMPWalk(snmp_fd
, ¤t_addr
, CUPS_SNMP_VERSION_1
,
497 _cupsSNMPDefaultCommunity(), prtMarkerColorantValue
, 0.5,
498 backend_walk_cb
, NULL
);
501 * Output the marker-colors attribute...
504 for (i
= 0, ptr
= value
; i
< num_supplies
; i
++, ptr
+= strlen(ptr
))
509 strcpy(ptr
, supplies
[i
].color
);
512 fprintf(stderr
, "ATTR: marker-colors=%s\n", value
);
515 * Output the marker-names attribute...
518 for (i
= 0, ptr
= value
; i
< num_supplies
; i
++)
524 for (name_ptr
= supplies
[i
].name
; *name_ptr
;)
526 if (*name_ptr
== '\\' || *name_ptr
== '\"')
529 *ptr
++ = *name_ptr
++;
536 fprintf(stderr
, "ATTR: marker-names=%s\n", value
);
539 * Output the marker-types attribute...
542 for (i
= 0, ptr
= value
; i
< num_supplies
; i
++, ptr
+= strlen(ptr
))
547 type
= supplies
[i
].type
;
549 if (type
< CUPS_TC_other
|| type
> CUPS_TC_covers
)
550 strcpy(ptr
, "unknown");
552 strcpy(ptr
, types
[type
- CUPS_TC_other
]);
555 fprintf(stderr
, "ATTR: marker-types=%s\n", value
);
560 * 'backend_walk_cb()' - Interpret the supply value responses...
564 backend_walk_cb(cups_snmp_t
*packet
, /* I - SNMP packet */
565 void *data
) /* I - User data (unused) */
567 int i
, j
, k
; /* Looping vars */
568 static const char * const colors
[8][2] =
569 { /* Standard color names */
570 { "black", "#000000" },
571 { "blue", "#0000FF" },
572 { "cyan", "#00FFFF" },
573 { "green", "#00FF00" },
574 { "magenta", "#FF00FF" },
575 { "red", "#FF0000" },
576 { "white", "#FFFFFF" },
577 { "yellow", "#FFFF00" }
583 if (_cupsSNMPIsOIDPrefixed(packet
, prtMarkerColorantValue
) &&
584 packet
->object_type
== CUPS_ASN1_OCTET_STRING
)
590 i
= packet
->object_name
[prtMarkerColorantValueOffset
];
592 fprintf(stderr
, "DEBUG2: prtMarkerColorantValue.1.%d = \"%s\"\n", i
,
593 packet
->object_value
.string
);
595 for (j
= 0; j
< num_supplies
; j
++)
596 if (supplies
[j
].colorant
== i
)
598 for (k
= 0; k
< (int)(sizeof(colors
) / sizeof(colors
[0])); k
++)
599 if (!strcmp(colors
[k
][0], packet
->object_value
.string
))
601 strcpy(supplies
[j
].color
, colors
[k
][1]);
606 else if (_cupsSNMPIsOIDPrefixed(packet
, prtMarkerSuppliesColorantIndex
))
609 * Get colorant index...
612 i
= packet
->object_name
[prtMarkerSuppliesColorantIndexOffset
];
613 if (i
< 1 || i
> CUPS_MAX_SUPPLIES
||
614 packet
->object_type
!= CUPS_ASN1_INTEGER
)
617 fprintf(stderr
, "DEBUG2: prtMarkerSuppliesColorantIndex.1.%d = %d\n", i
,
618 packet
->object_value
.integer
);
620 if (i
> num_supplies
)
623 supplies
[i
- 1].colorant
= packet
->object_value
.integer
;
625 else if (_cupsSNMPIsOIDPrefixed(packet
, prtMarkerSuppliesDescription
))
628 * Get supply name/description...
631 i
= packet
->object_name
[prtMarkerSuppliesDescriptionOffset
];
632 if (i
< 1 || i
> CUPS_MAX_SUPPLIES
||
633 packet
->object_type
!= CUPS_ASN1_OCTET_STRING
)
636 fprintf(stderr
, "DEBUG2: prtMarkerSuppliesDescription.1.%d = \"%s\"\n", i
,
637 packet
->object_value
.string
);
639 if (i
> num_supplies
)
642 strlcpy(supplies
[i
- 1].name
, packet
->object_value
.string
,
643 sizeof(supplies
[0].name
));
645 else if (_cupsSNMPIsOIDPrefixed(packet
, prtMarkerSuppliesLevel
))
651 i
= packet
->object_name
[prtMarkerSuppliesLevelOffset
];
652 if (i
< 1 || i
> CUPS_MAX_SUPPLIES
||
653 packet
->object_type
!= CUPS_ASN1_INTEGER
)
656 fprintf(stderr
, "DEBUG2: prtMarkerSuppliesLevel.1.%d = %d\n", i
,
657 packet
->object_value
.integer
);
659 if (i
> num_supplies
)
662 supplies
[i
- 1].level
= packet
->object_value
.integer
;
664 else if (_cupsSNMPIsOIDPrefixed(packet
, prtMarkerSuppliesMaxCapacity
))
667 * Get max capacity...
670 i
= packet
->object_name
[prtMarkerSuppliesMaxCapacityOffset
];
671 if (i
< 1 || i
> CUPS_MAX_SUPPLIES
||
672 packet
->object_type
!= CUPS_ASN1_INTEGER
)
675 fprintf(stderr
, "DEBUG2: prtMarkerSuppliesMaxCapacity.1.%d = %d\n", i
,
676 packet
->object_value
.integer
);
678 if (i
> num_supplies
)
681 supplies
[i
- 1].max_capacity
= packet
->object_value
.integer
;
683 else if (_cupsSNMPIsOIDPrefixed(packet
, prtMarkerSuppliesType
))
689 i
= packet
->object_name
[prtMarkerSuppliesTypeOffset
];
690 if (i
< 1 || i
> CUPS_MAX_SUPPLIES
||
691 packet
->object_type
!= CUPS_ASN1_INTEGER
)
694 fprintf(stderr
, "DEBUG2: prtMarkerSuppliesType.1.%d = %d\n", i
,
695 packet
->object_value
.integer
);
697 if (i
> num_supplies
)
700 supplies
[i
- 1].type
= packet
->object_value
.integer
;