4 * SNMP supplies functions for the Common UNIX Printing System (CUPS).
6 * Copyright 2008-2009 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 */
42 typedef struct /**** Printer supply data ****/
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 */
52 typedef struct /**** Printer state table ****/
54 int bit
; /* State bit */
55 const char *keyword
; /* IPP printer-state-reasons keyword */
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 */
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 },
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 };
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 },
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 },
124 prtMarkerSuppliesTypeOffset
=
125 (sizeof(prtMarkerSuppliesType
) /
126 sizeof(prtMarkerSuppliesType
[0]));
127 /* Offset to supply index */
129 static const backend_state_t
const printer_states
[] =
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" }
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
);
156 * 'backendSNMPSupplies()' - Get the current supplies for a device.
159 int /* O - 0 on success, -1 on error */
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 */
166 if (!httpAddrEqual(addr
, ¤t_addr
))
167 backend_init_supplies(snmp_fd
, addr
);
168 else if (num_supplies
> 0)
169 _cupsSNMPWalk(snmp_fd
, ¤t_addr
, CUPS_SNMP_VERSION_1
,
170 _cupsSNMPDefaultCommunity(), prtMarkerSuppliesLevel
, 0.5,
171 backend_walk_cb
, NULL
);
179 if (num_supplies
> 0)
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 */
191 * Generate the marker-levels value string...
194 for (i
= 0, ptr
= value
; i
< num_supplies
; i
++, ptr
+= strlen(ptr
))
199 if (supplies
[i
].max_capacity
> 0)
200 sprintf(ptr
, "%d", 100 * supplies
[i
].level
/ supplies
[i
].max_capacity
);
205 fprintf(stderr
, "ATTR: marker-levels=%s\n", value
);
208 * Get the current printer status bits...
211 if (!_cupsSNMPWrite(snmp_fd
, addr
, CUPS_SNMP_VERSION_1
,
212 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST
, 1,
213 hrPrinterDetectedErrorState
))
216 if (!_cupsSNMPRead(snmp_fd
, &packet
, 0.5) ||
217 packet
.object_type
!= CUPS_ASN1_OCTET_STRING
)
220 new_state
= (packet
.object_value
.string
.bytes
[0] << 8) |
221 packet
.object_value
.string
.bytes
[1];
223 if (current_state
< 0)
224 change_state
= 0xffff;
226 change_state
= current_state
^ new_state
;
229 i
< (int)(sizeof(printer_states
) / sizeof(printer_states
[0]));
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
);
236 current_state
= new_state
;
239 * Get the current printer state...
244 if (!_cupsSNMPWrite(snmp_fd
, addr
, CUPS_SNMP_VERSION_1
,
245 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST
, 1,
249 if (!_cupsSNMPRead(snmp_fd
, &packet
, 0.5) ||
250 packet
.object_type
!= CUPS_ASN1_INTEGER
)
253 *printer_state
= packet
.object_value
.integer
;
257 * Get the current page count...
262 if (!_cupsSNMPWrite(snmp_fd
, addr
, CUPS_SNMP_VERSION_1
,
263 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST
, 1,
267 if (!_cupsSNMPRead(snmp_fd
, &packet
, 0.5) ||
268 packet
.object_type
!= CUPS_ASN1_COUNTER
)
271 *page_count
= packet
.object_value
.counter
;
282 * 'backend_init_supplies()' - Initialize the supplies list.
286 backend_init_supplies(
287 int snmp_fd
, /* I - SNMP socket */
288 http_addr_t
*addr
) /* I - Printer address */
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)],
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 */
345 * Reset state information...
348 current_addr
= *addr
;
352 memset(supplies
, 0, sizeof(supplies
));
355 * See if we should be getting supply levels via SNMP...
358 if ((ppd
= ppdOpenFile(getenv("PPD"))) != NULL
&&
359 (ppdattr
= ppdFindAttr(ppd
, "cupsSNMPSupplies", NULL
)) != NULL
&&
360 ppdattr
->value
&& strcasecmp(ppdattr
->value
, "true"))
369 * Get the device description...
372 if (!_cupsSNMPWrite(snmp_fd
, addr
, CUPS_SNMP_VERSION_1
,
373 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST
, 1,
377 if (!_cupsSNMPRead(snmp_fd
, &packet
, 0.5) ||
378 packet
.object_type
!= CUPS_ASN1_OCTET_STRING
)
380 strlcpy(description
, "Unknown", sizeof(description
));
384 strlcpy(description
, (char *)packet
.object_value
.string
.bytes
,
385 sizeof(description
));
388 * See if we have already queried this device...
391 httpAddrString(addr
, addrstr
, sizeof(addrstr
));
393 if ((cachedir
= getenv("CUPS_CACHEDIR")) == NULL
)
394 cachedir
= CUPS_CACHEDIR
;
396 snprintf(cachefilename
, sizeof(cachefilename
), "%s/%s.snmp", cachedir
,
399 if ((cachefile
= cupsFileOpen(cachefilename
, "r")) != NULL
)
402 * Yes, read the cache file:
406 * supply structures...
409 if (cupsFileGets(cachefile
, value
, sizeof(value
)))
411 if (sscanf(value
, "1 %d", &num_supplies
) == 1 &&
412 num_supplies
<= CUPS_MAX_SUPPLIES
&&
413 cupsFileGets(cachefile
, value
, sizeof(value
)))
415 if ((ptr
= value
+ strlen(value
) - 1) >= value
&& *ptr
== '\n')
418 if (!strcmp(description
, value
))
419 cupsFileRead(cachefile
, (char *)supplies
,
420 num_supplies
* sizeof(backend_supplies_t
));
428 cupsFileClose(cachefile
);
432 * If the cache information isn't correct, scan for supplies...
435 if (num_supplies
< 0)
438 * Walk the printer configuration information...
441 _cupsSNMPWalk(snmp_fd
, ¤t_addr
, CUPS_SNMP_VERSION_1
,
442 _cupsSNMPDefaultCommunity(), prtMarkerSuppliesEntry
, 0.5,
443 backend_walk_cb
, NULL
);
447 * Save the cached information...
450 if (num_supplies
< 0)
453 if ((cachefile
= cupsFileOpen(cachefilename
, "w")) != NULL
)
455 cupsFilePrintf(cachefile
, "1 %d\n", num_supplies
);
456 cupsFilePrintf(cachefile
, "%s\n", description
);
458 if (num_supplies
> 0)
459 cupsFileWrite(cachefile
, (char *)supplies
,
460 num_supplies
* sizeof(backend_supplies_t
));
462 cupsFileClose(cachefile
);
465 if (num_supplies
<= 0)
472 for (i
= 0; i
< num_supplies
; i
++)
473 strcpy(supplies
[i
].color
, "none");
475 _cupsSNMPWalk(snmp_fd
, ¤t_addr
, CUPS_SNMP_VERSION_1
,
476 _cupsSNMPDefaultCommunity(), prtMarkerColorantValue
, 0.5,
477 backend_walk_cb
, NULL
);
480 * Output the marker-colors attribute...
483 for (i
= 0, ptr
= value
; i
< num_supplies
; i
++, ptr
+= strlen(ptr
))
488 strcpy(ptr
, supplies
[i
].color
);
491 fprintf(stderr
, "ATTR: marker-colors=%s\n", value
);
494 * Output the marker-names attribute...
497 for (i
= 0, ptr
= value
; i
< num_supplies
; i
++)
503 for (name_ptr
= supplies
[i
].name
; *name_ptr
;)
505 if (*name_ptr
== '\\' || *name_ptr
== '\"')
508 *ptr
++ = *name_ptr
++;
515 fprintf(stderr
, "ATTR: marker-names=%s\n", value
);
518 * Output the marker-types attribute...
521 for (i
= 0, ptr
= value
; i
< num_supplies
; i
++, ptr
+= strlen(ptr
))
526 type
= supplies
[i
].type
;
528 if (type
< CUPS_TC_other
|| type
> CUPS_TC_covers
)
529 strcpy(ptr
, "unknown");
531 strcpy(ptr
, types
[type
- CUPS_TC_other
]);
534 fprintf(stderr
, "ATTR: marker-types=%s\n", value
);
539 * 'backend_walk_cb()' - Interpret the supply value responses...
543 backend_walk_cb(cups_snmp_t
*packet
, /* I - SNMP packet */
544 void *data
) /* I - User data (unused) */
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" }
562 if (_cupsSNMPIsOIDPrefixed(packet
, prtMarkerColorantValue
) &&
563 packet
->object_type
== CUPS_ASN1_OCTET_STRING
)
569 i
= packet
->object_name
[prtMarkerColorantValueOffset
];
571 fprintf(stderr
, "DEBUG2: prtMarkerColorantValue.1.%d = \"%s\"\n", i
,
572 (char *)packet
->object_value
.string
.bytes
);
574 for (j
= 0; j
< num_supplies
; j
++)
575 if (supplies
[j
].colorant
== i
)
577 for (k
= 0; k
< (int)(sizeof(colors
) / sizeof(colors
[0])); k
++)
578 if (!strcmp(colors
[k
][0], (char *)packet
->object_value
.string
.bytes
))
580 strcpy(supplies
[j
].color
, colors
[k
][1]);
585 else if (_cupsSNMPIsOIDPrefixed(packet
, prtMarkerSuppliesColorantIndex
))
588 * Get colorant index...
591 i
= packet
->object_name
[prtMarkerSuppliesColorantIndexOffset
];
592 if (i
< 1 || i
> CUPS_MAX_SUPPLIES
||
593 packet
->object_type
!= CUPS_ASN1_INTEGER
)
596 fprintf(stderr
, "DEBUG2: prtMarkerSuppliesColorantIndex.1.%d = %d\n", i
,
597 packet
->object_value
.integer
);
599 if (i
> num_supplies
)
602 supplies
[i
- 1].colorant
= packet
->object_value
.integer
;
604 else if (_cupsSNMPIsOIDPrefixed(packet
, prtMarkerSuppliesDescription
))
607 * Get supply name/description...
610 i
= packet
->object_name
[prtMarkerSuppliesDescriptionOffset
];
611 if (i
< 1 || i
> CUPS_MAX_SUPPLIES
||
612 packet
->object_type
!= CUPS_ASN1_OCTET_STRING
)
615 fprintf(stderr
, "DEBUG2: prtMarkerSuppliesDescription.1.%d = \"%s\"\n", i
,
616 (char *)packet
->object_value
.string
.bytes
);
618 if (i
> num_supplies
)
621 strlcpy(supplies
[i
- 1].name
, (char *)packet
->object_value
.string
.bytes
,
622 sizeof(supplies
[0].name
));
624 else if (_cupsSNMPIsOIDPrefixed(packet
, prtMarkerSuppliesLevel
))
630 i
= packet
->object_name
[prtMarkerSuppliesLevelOffset
];
631 if (i
< 1 || i
> CUPS_MAX_SUPPLIES
||
632 packet
->object_type
!= CUPS_ASN1_INTEGER
)
635 fprintf(stderr
, "DEBUG2: prtMarkerSuppliesLevel.1.%d = %d\n", i
,
636 packet
->object_value
.integer
);
638 if (i
> num_supplies
)
641 supplies
[i
- 1].level
= packet
->object_value
.integer
;
643 else if (_cupsSNMPIsOIDPrefixed(packet
, prtMarkerSuppliesMaxCapacity
))
646 * Get max capacity...
649 i
= packet
->object_name
[prtMarkerSuppliesMaxCapacityOffset
];
650 if (i
< 1 || i
> CUPS_MAX_SUPPLIES
||
651 packet
->object_type
!= CUPS_ASN1_INTEGER
)
654 fprintf(stderr
, "DEBUG2: prtMarkerSuppliesMaxCapacity.1.%d = %d\n", i
,
655 packet
->object_value
.integer
);
657 if (i
> num_supplies
)
660 supplies
[i
- 1].max_capacity
= packet
->object_value
.integer
;
662 else if (_cupsSNMPIsOIDPrefixed(packet
, prtMarkerSuppliesType
))
668 i
= packet
->object_name
[prtMarkerSuppliesTypeOffset
];
669 if (i
< 1 || i
> CUPS_MAX_SUPPLIES
||
670 packet
->object_type
!= CUPS_ASN1_INTEGER
)
673 fprintf(stderr
, "DEBUG2: prtMarkerSuppliesType.1.%d = %d\n", i
,
674 packet
->object_value
.integer
);
676 if (i
> num_supplies
)
679 supplies
[i
- 1].type
= packet
->object_value
.integer
;