1 /*
2 Copyright (C) 2016-2021, Dirk Krause
3 SPDX-License-Identifier: BSD-3-Clause
4 */
5
6 /*
7 WARNING: This file was generated by the dkct program (see
8 http://dktools.sourceforge.net/ for details).
9 Changes you make here will be lost if dkct is run again!
10 You should modify the original source and run dkct on it.
11 Original source: pjsnmp.ctr
12 */
13
14 /** @file pjsnmp.c The pjsnmp module.
15 */
16
17
18 #include "dk4conf.h"
19 #include <libdk4base/dk4types.h>
20
21 #include <stdio.h>
22
23 #if DK4_HAVE_SYS_TYPES_H
24 #include <sys/types.h>
25 #endif
26
27 #if DK4_HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30
31
32 #if DK4_HAVE_LIBNETSNMP && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET))
33
34 #include <net-snmp/net-snmp-config.h>
35 #include <net-snmp/net-snmp-includes.h>
36 #include <net-snmp/utilities.h>
37 #include <net-snmp/library/snmp_logging.h>
38
39 #include <libdk4base/dk4types.h>
40 #include <libdk4c/dk4inst.h>
41 #include <libdk4ma/dk4maasz.h>
42 #include <libdk4ma/dk4maadu.h>
43 #include <libdk4base/dk4mem.h>
44 #include <libdk4c/dk4sto.h>
45 #include <libdk4base/dk4str8.h>
46 #include <libdk4c/dk4fopc8.h>
47 #include <libdk4c/dk4stat.h>
48 #include <libdk4c/dk4stat8.h>
49 #include <libdk4maio8d/dk4mai8dus.h>
50 #include <libdk4maio8h/dk4mai8huc.h>
51 #include <libdk4maio8d/dk4mai8dii.h>
52 #include <libdk4maio8d/dk4mai8ddu.h>
53 #include <libdk4maio8d/dk4mao8d.h>
54 #include <libdk4maio8h/dk4mao8h.h>
55 #include <libdk4c/dk4time.h>
56 #include <libdk4c/dk4time08.h>
57 #include <libdk4sock/dk4sock.h>
58 #include <libdk4c/dk4isadm.h>
59 #include <libdk4c/dk4lprng.h>
60 #include <libdk4base/dk4unused.h>
61
62
63
64
65
66 #endif
67 /* if DK4_HAVE_LIBNETSNMP && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) */
68
69
70
71 /** One status text entry from SNMP model.
72 */
73 typedef struct {
74 unsigned char *text; /**< Text pointer, not 0-terminated. */
75 size_t size; /**< Text size. */
76 int state; /**< Summary state if text was found. */
77 int start; /**< Flag: Text here is start text only. */
78 } status_text_t;
79
80
81 #if DK4_HAVE_LIBNETSNMP && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET))
82
83
84 /** SNMP device status.
85 */
86 enum {
87 SNMP_DEVST_UNKNOWN = 1 , /**< Status is unknown. */
88
89 SNMP_DEVST_RUNNING = 2 , /**< Running. */
90
91 SNMP_DEVST_WARNING = 3 , /**< Warning condition active. */
92
93 SNMP_DEVST_TESTING = 4 , /**< In self test. */
94
95 SNMP_DEVST_DOWN = 5 /**< Down due to error condition. */
96 };
97
98
99 /** SNMP printer status.
100 */
101 enum {
102 SNMP_PRST_OTHER = 1 , /**< Any state not mentioned below. */
103
104 SNMP_PRST_UNKNOWN = 2 , /**< Unknown. */
105
106 SNMP_PRST_IDLE = 3 , /**< Ready, waiting for next job. */
107
108 SNMP_PRST_PRINTING = 4 , /**< Printing a job. */
109
110 SNMP_PRST_WARMUP = 5 /**< Warming up. */
111 };
112
113
114 /** State summary.
115 For accounting we would only need to distinguish between
116 standby/idle (secure to retrieve page counter) and anything
117 else.
118 But we want to print human readable state summaries to status
119 file which are shown by lpq -L.
120 */
121 enum {
122 PJSNMP_ST_UNKNOWN = 0 , /**< Failed to retrieve printer state. */
123
124 PJSNMP_ST_UNREACHABLE = 1 , /**< Printer is unreachable. */
125
126 PJSNMP_ST_ERROR = 2 , /**< Error state on printer. */
127
128 PJSNMP_ST_BUSY = 3 , /**< Printer is printing. */
129
130 PJSNMP_ST_IDLE = 4 , /**< Printer idle, can retrieve page counter. */
131
132 PJSNMP_ST_WARMUP = 5 , /**< Warming up. */
133
134 PJSNMP_ST_STANDBY = 6 , /**< Standby. */
135 };
136
137
138 /** @defgroup pdes Printer detected error states.
139 The first byte in the octet string is the most significant byte
140 in the unsigned long value.
141 */
142 /**@{*/
143 /** Warning: Paper low.
144 */
145 #define PDES_WARNING_PAPER_LOW 0x80000000UL
146
147 /** Error: Out of paper.
148 */
149 #define PDES_ERROR_NO_PAPER 0x40000000UL
150
151 /** Warning: Toner low.
152 */
153 #define PDES_WARNING_TONER_LOW 0x20000000UL
154
155 /** Error: Out of toner.
156 */
157 #define PDES_ERROR_NO_TONER 0x10000000UL
158
159 /** Error: Door open.
160 */
161 #define PDES_ERROR_DOOR_OPEN 0x08000000UL
162
163 /** Error: Paper jam.
164 */
165 #define PDES_ERROR_PAPER_JAM 0x04000000UL
166
167 /** Error: Printer offline.
168 */
169 #define PDES_ERROR_OFFLINE 0x02000000UL
170
171 /** Warning: Service requested.
172 */
173 #define PDES_WARNING_SERVICE 0x01000000UL
174
175 /** Input tray removed.
176 */
177 #define PDES_ERROR_INPUT_TRAY_MISSING 0x00800000UL
178
179 /** Output tray removed.
180 */
181 #define PDES_ERROR_OUTPUT_TRAY_MISSING 0x00400000UL
182
183 /** Marker supply missing.
184 */
185 #define PDES_ERROR_MARKER_SUPPLY_MISSING 0x00200000UL
186
187 /** Output tray nearly full.
188 */
189 #define PDES_WARNING_OUTPUT_NEAR_FULL 0x00100000UL
190
191 /** Output tray full.
192 */
193 #define PDES_ERROR_OUTPUT_FULL 0x00080000UL
194
195 /** At least one input tray empty.
196 */
197 #define PDES_WARNING_INPUT_TRAY_EMPTY 0x00040000UL
198
199 /** Overdue preventive maintenance.
200 */
201 #define PDES_WARNING_OVERDUE_PREVENT_MAINT 0x00020000UL
202
203
204 /** Bitmask to check if there is any error condition.
205 */
206 #define PDES_ANY_ERROR \
207 ( PDES_ERROR_NO_PAPER \
208 | PDES_ERROR_NO_TONER \
209 | PDES_ERROR_DOOR_OPEN \
210 | PDES_ERROR_PAPER_JAM \
211 | PDES_ERROR_OFFLINE \
212 | PDES_ERROR_INPUT_TRAY_MISSING \
213 | PDES_ERROR_OUTPUT_TRAY_MISSING \
214 | PDES_ERROR_MARKER_SUPPLY_MISSING \
215 | PDES_ERROR_OUTPUT_FULL)
216
217
218 /** Bitmask to check if there is any warning condition.
219 */
220 #define PDES_ANY_WARNING \
221 ( PDES_WARNING_PAPER_LOW \
222 | PDES_WARNING_TONER_LOW \
223 | PDES_WARNING_SERVICE \
224 | PDES_WARNING_OUTPUT_NEAR_FULL \
225 | PDES_WARNING_OVERDUE_PREVENT_MAINT \
226 | PDES_WARNING_INPUT_TRAY_EMPTY)
227
228
229 /** Bitmask to check whether any error or warning condition is set.
230 */
231 #define PDES_ANY_CONDITION ((PDES_ANY_WARNING) | (PDES_ANY_ERROR))
232
233 /**@}*/
234
235
236 /** Buffer for sending.
237 Should be large to minimize number of I/O operations.
238 */
239 #if DK4_SIZEOF_SIZE_T > 2
240 static char pjsnmp_buf[1048576];
241 #else
242 static char pjsnmp_buf[16384];
243 #endif
244
245
246 /** Current status text, also used in accounting.
247 */
248 static char stat_text[2048];
249
250
251 /** Buffer for responses from printer.
252 */
253 static char dummy_text[2048];
254
255
256 /** Previous status text.
257 */
258 static char ostat_text[sizeof(stat_text)];
259
260
261 /** Buffer for automatically constructed job name.
262 */
263 static char job_name_buf[32+(8*sizeof(dk4_um_t))];
264
265
266 /** Status text oid.
267 */
268 static oid oid_st[MAX_OID_LEN];
269
270
271 /** OID for device status.
272 */
273 static const oid oid_ds[] = {
274 (oid)1, (oid)3, (oid)6, (oid)1, (oid)2, (oid)1,
275 (oid)25, (oid)3, (oid)2, (oid)1, (oid)5, (oid)1
276 };
277
278
279 /** OID for printer status.
280 */
281 static const oid oid_ps[] = {
282 (oid)1, (oid)3, (oid)6, (oid)1, (oid)2, (oid)1,
283 (oid)25, (oid)3, (oid)5, (oid)1, (oid)1, (oid)1
284 };
285
286
287 /** OID for pagecount value.
288 */
289 static const oid oid_pc[] = {
290 (oid)1, (oid)3, (oid)6, (oid)1, (oid)2, (oid)1,
291 (oid)43, (oid)10, (oid)2, (oid)1, (oid)4, (oid)1, (oid)1
292 };
293
294
295 /** OID for printer detected error state.
296 */
297 static const oid oid_pe[] = {
298 (oid)1, (oid)3, (oid)6, (oid)1, (oid)2, (oid)1,
299 (oid)25, (oid)3, (oid)5, (oid)1, (oid)2, (oid)1
300 };
301
302
303 /** Default OID for status text.
304 */
305 static const oid oid_dst[] = {
306 (oid)1, (oid)3, (oid)6, (oid)1, (oid)2, (oid)1,
307 (oid)43, (oid)16, (oid)5, (oid)1, (oid)2, (oid)1, (oid)1
308 };
309
310
311 /** Constant texts, not localized.
312 */
313 static const char * const pjsnmp_kw[] = {
314 /* 0 */
315 "\n",
316
317 /* 1 */
318 " ",
319
320 /* 2 */
321 "PRINTCAP_ENTRY",
322
323 /* 3 */
324 "*",
325
326 /* 4 */
327 "pjsnmp",
328
329 /* 5 */
330 "public",
331
332 /* 6 */
333 "acct-check",
334
335 /* 7 */
336 "Device and printer status: ",
337
338 /* 8 */
339 "/",
340
341 /* 9 */
342 "Printer detected error state: ",
343
344 /* 10 */
345 "Current printer state: ",
346
347 /* 11 */
348 "not found",
349
350 /* 12 */
351 "unknown",
352
353 /* 13 */
354 "running",
355
356 /* 14 */
357 "warning",
358
359 /* 15 */
360 "testing",
361
362 /* 16 */
363 "down",
364
365 /* 17 */
366 "not found",
367
368 /* 18 */
369 "other",
370
371 /* 19 */
372 "unknown",
373
374 /* 20 */
375 "idle",
376
377 /* 21 */
378 "printing",
379
380 /* 22 */
381 "warmup",
382
383 /* 23 */
384 "UNKNOWN",
385
386 /* 24 */
387 "UNREACHABLE",
388
389 /* 25 */
390 "ERROR",
391
392 /* 26 */
393 "BUSY",
394
395 /* 27 */
396 "IDLE",
397
398 /* 28 */
399 "WARMUP",
400
401 /* 29 */
402 "STANDBY",
403
404 /* 30 */
405 "\"",
406
407 /* 31 */
408 "\"\n",
409
410 /* 32 */
411 "ERROR: ",
412
413 /* 33 */
414 "Warning: ",
415
416 /* 34 */
417 "Paper low (printer will run out of paper soon).\n",
418
419 /* 35 */
420 "Out of paper!\n",
421
422 /* 36 */
423 "Toner low (printer will run out of toner soon).\n",
424
425 /* 37 */
426 "Out of toner!\n",
427
428 /* 38 */
429 "Door open!\n",
430
431 /* 39 */
432 "Paper jam!\n",
433
434 /* 40 */
435 "Offline!\n",
436
437 /* 41 */
438 "Service requested.\n",
439
440 /* 42 */
441 "Input tray missing!\n",
442
443 /* 43 */
444 "Output tray missing!\n",
445
446 /* 44 */
447 "Marker supply missing!\n",
448
449 /* 45 */
450 "Output tray nearly full (will be full soon).\n",
451
452 /* 46 */
453 "Output tray full!\n",
454
455 /* 47 */
456 "At least one input tray is empty.\n",
457
458 /* 48 */
459 "Overdue preventive maintenance.\n",
460
461 /* 49 */
462 "ERROR ON PRINTER, MANUAL INTERVENTION REQUIRED!\n",
463
464 /* 50 */
465 "PRINTER PROBABLY REQUIRES MANUAL INTERVENTION!\n",
466
467 /* 51 */
468 "acct-start",
469
470 /* 52 */
471 "acct-end",
472
473 /* 53 */
474 "acct-charge",
475
476 /* 54 */
477 "print-job",
478
479 /* 55 */
480 "Print job finished.\n",
481
482 /* 56 */
483 "Job size: ",
484
485 /* 57 */
486 "Data transfer: ",
487
488 /* 58 */
489 "Processing: ",
490
491 /* 59 */
492 "Pages: ",
493
494 /* 60 */
495 " bytes\n",
496
497 /* 61 */
498 " seconds\n",
499
500 /* 62 */
501 "# ",
502
503 /* 63 */
504 "Configuration problem (command line arguments)!\n",
505
506 /* 64 */
507 "Configuration problem (printcap file)!\n",
508
509 /* 65 */
510 "Configuration problem (SNMP model file)!\n",
511
512 /* 66 */
513 "ERROR: Missing option name!\n",
514
515 /* 67 */
516 "ERROR: Missing option argument!\n",
517
518 /* 68 */
519 "ERROR: Non-option command line argument found!\n",
520
521 /* 69 */
522 "ERROR: No user name specified!\n",
523
524 /* 70 */
525 "ERROR: No print queue name specified!\n",
526
527 /* 71 */
528 "ERROR: No print job name specified!\n",
529
530 /* 72 */
531 "Warning: Redefinition of \"",
532
533 /* 73 */
534 "\" to \"",
535
536 /* 74 */
537 "\"!\n",
538
539 /* 75 */
540 "ERROR: Empty text for option \"",
541
542 /* 76 */
543 "\"!\n",
544
545 /* 77 */
546 "ERROR: Missing text for \"",
547
548 /* 78 */
549 "\"!\n",
550
551 /* 79 */
552 "ERROR: Option \"",
553
554 /* 80 */
555 "\" requires unsigned short number!\nIllegal text: \"",
556
557 /* 81 */
558 "\"!",
559
560 /* 82 */
561 "ERROR: Illegal SNMP version: \"",
562
563 /* 83 */
564 "\"!\nAllowed: \"1\", \"2c\", \"3\"!\n",
565
566 /* 84 */
567 "ERROR: Option \"",
568
569 /* 85 */
570 "\" requires an integer value!\nIllegal text: \"",
571
572 /* 86 */
573 "\"!\n",
574
575 /* 87 */
576 "ERROR: Missing \"]\"!\n",
577
578 /* 88 */
579 "ERROR: Missing model name!\n",
580
581 /* 89 */
582 "ERROR: Illegal summary state: \"",
583
584 /* 90 */
585 "\"!\n",
586
587 /* 91 */
588 "ERROR: Illegal printer status name: \"",
589
590 /* 92 */
591 "\"!\n",
592
593 /* 93 */
594 "ERROR: Illegal device status name: \"",
595
596 /* 94 */
597 "\"!",
598
599 /* 95 */
600 "ERROR: Missing printer status name!\n",
601
602 /* 96 */
603 "ERROR: Missing summary state name!\n",
604
605 /* 97 */
606 "ERROR: Illegal condition name: \"",
607
608 /* 98 */
609 "\"!\n",
610
611 /* 99 */
612 "ERROR: OID too long!\n",
613
614 /* 100 */
615 "ERROR: Numeric overflow for sub id: \"",
616
617 /* 101 */
618 "\"!\n",
619
620 /* 102 */
621 "ERROR: Not a number: \"",
622
623 /* 103 */
624 "\"!\n",
625
626 /* 104 */
627 "ERROR: Empty OID text!\n",
628
629 /* 105 */
630 "ERROR: Memory allocation failed!\n",
631
632 /* 106 */
633 "ERROR: Empty string!\n",
634
635 /* 107 */
636 "ERROR: Incorrect string specification!\n",
637
638 /* 108 */
639 "ERROR: Not a hexadecimal byte: \"",
640
641 /* 109 */
642 "\"!\n",
643
644 /* 110 */
645 "ERROR: Value required!\n",
646
647 /* 111 */
648 "ERROR: Not a boolean: \"",
649
650 /* 112 */
651 "\"!\n",
652
653 /* 113 */
654 "ERROR: Illegal key: \"",
655
656 /* 114 */
657 "\"!\n",
658
659 /* 115 */
660 "ERROR: Model not found: \"",
661
662 /* 116 */
663 "\"!\n",
664
665 /* 117 */
666 "ERROR: Failed to open model file!\n\"",
667
668 /* 118 */
669 "\"!\n",
670
671 /* 119 */
672 "ERROR: Missing host name for printer!\n",
673
674 /* 120 */
675 "ERROR: PRINTCAP_ENTRY not found in environment!\n",
676
677 /* 121 */
678 "ERROR: Failed to create SNMP session!\n",
679
680 /* 122 */
681 "ERROR: Failed to send data to accounting system!\n",
682
683 /* 123 */
684 "ERROR: Partial success when sending data to accounting system!\n",
685
686 /* 124 */
687 "ERROR: Failed to shut down accounting socket!\n",
688
689 /* 125 */
690 "ERROR: Failed to connect to accounting system!\n",
691
692 /* 126 */
693 "ERROR: Failed to start accounting program!\n",
694
695 /* 127 */
696 "ERROR: Missing command name for accounting program!\n",
697
698 /* 128 */
699 "ERROR: Failed to open accounting file!\n",
700
701 /* 129 */
702 "ERROR: Can not receive response from pipe!\n",
703
704 /* 130 */
705 "ERROR: Can not receive response from file!\n",
706
707 /* 131 */
708 "Checking permission: user=\"",
709
710 /* 132 */
711 "\" printer=\"",
712
713 /* 133 */
714 "\".\n",
715
716 /* 134 */
717 "Printing allowed.\n",
718
719 /* 135 */
720 "ERROR: PRINTING DENIED BY ACCOUNTING/QUOTA SYSTEM!\n",
721
722 /* 136 */
723 "ERROR: Printer unreachable too long!\n",
724
725 /* 137 */
726 "ERROR: Network connection not writeable (timeout)!\n",
727
728 /* 138 */
729 "ERROR: Failed to send data!\n",
730
731 /* 139 */
732 "ERROR: Failed to read print data from standard input!\n",
733
734 /* 140 */
735 "ERROR: Failed to connect to printer!\n",
736
737 /* 141 */
738 "ERROR: Failed to set up signal handlers!\n",
739
740 /* 142 */
741 "ERROR: Failed to restore signal handlers!\n",
742
743 /* 143 */
744 ":",
745
746 /* 144 */
747 "",
748
749 /* 145 */
750 "Retrieving page counter at start.\n",
751
752 /* 146 */
753 "Retrieving page counter at end.\n",
754
755 /* 147 */
756 "Page counter at start: ",
757
758 /* 148 */
759 "Page counter at end: ",
760
761 /* 149 */
762 "Failed to retrieve page counter value!\n",
763
764 /* 150 */
765 "---\n",
766
767 /* 151 */
768 "Starting print data transfer.\n",
769
770 /* 152 */
771 "Finished print data transfer.\n",
772
773 /* 153 */
774 "Print data transfer aborted.\n",
775
776 /* 154 */
777 "Transmitting accounting data via network socket.\n",
778
779 /* 155 */
780 "Writing accounting data pipe.\n",
781
782 /* 156 */
783 "Transmitting accounting data via local socket.\n",
784
785 /* 157 */
786 "Writing accounting data to file.\n",
787
788 /* 158 */
789 "Transmitting accounting data to fd 3.\n",
790
791 /* 159 */
792 "Accounting data transmitted successfully.\n",
793
794 /* 160 */
795 "ERROR: Failed to transmit accounting data!\n",
796
797 /* 161 */
798 "\" requires an unsigned integer value!\nIllegal text: \"",
799
800 /* 162 */
801 "Warning: Unusual change to idle/standby state.\nWaiting ",
802
803 /* 163 */
804 " seconds ",
805
806 /* 164 */
807 "for printer to resume printing.\n",
808
809 /* 165 */
810 "ERROR: No accounting destination configured!\n",
811
812 /* 166 */
813 "Response from accounting system: \"",
814
815 /* 167 */
816 "\".\n",
817
818 /* 168 */
819 "Printer in error state too long!\n",
820
821 /* 169 */
822 "Command line arguments:\n",
823
824 /* 170 */
825 "ERROR: Failed to initialize socket subsystem!\n",
826
827 /* 171 */
828 "pjsnmp@localhost+",
829
830 NULL
831
832 };
833
834
835
836 /** Responses from accounting system.
837 */
838 static const char * const accounting_responses[] = {
839 /* 0 */
840 "ACCEPT",
841
842 /* 1 */
843 "HOLD",
844
845 /* 2 */
846 "REMOVE",
847
848 NULL
849
850 };
851
852
853
854 /** Names for configuration items in the printcap entry.
855 */
856 static const char * const printcap_entry_names[] = {
857 /* 0 */
858 "pjsnmp-host",
859
860 /* 1 */
861 "pjsnmp-port",
862
863 /* 2 */
864 "pjsnmp-ordrel",
865
866 /* 3 */
867 "pjsnmp-accounting-enable",
868
869 /* 4 */
870 "pjsnmp-accounting-check",
871
872 /* 5 */
873 "pjsnmp-accounting-file",
874
875 /* 6 */
876 "pjsnmp-accounting-host",
877
878 /* 7 */
879 "pjsnmp-accounting-port",
880
881 /* 8 */
882 "pjsnmp-require-printing",
883
884 /* 9 */
885 "pjsnmp-version",
886
887 /* 10 */
888 "pjsnmp-community",
889
890 /* 11 */
891 "pjsnmp-during-transfer",
892
893 /* 12 */
894 "pjsnmp-model-file",
895
896 /* 13 */
897 "pjsnmp-model-name",
898
899 /* 14 */
900 "pjsnmp-ignore-failed-session",
901
902 /* 15 */
903 "pjsnmp-allow-printing-if-accounting-connection-failed",
904
905 /* 16 */
906 "pjsnmp-pdes-always",
907
908 /* 17 */
909 "pjsnmp-unknown-busy",
910
911 /* 18 */
912 "pjsnmp-unreachable-timeout-start",
913
914 /* 19 */
915 "pjsnmp-unreachable-timeout-end",
916
917 /* 20 */
918 "pjsnmp-unreachable-timeout-printing",
919
920 /* 21 */
921 "pjsnmp-local-port",
922
923 /* 22 */
924 "pjsnmp-unwriteable-timeout-printing",
925
926 /* 23 */
927 "pjsnmp-read-response",
928
929 /* 24 */
930 "pjsnmp-force-printable-status-text",
931
932 /* 25 */
933 "pjsnmp-error-timeout-start",
934
935 /* 26 */
936 "pjsnmp-error-timeout-end",
937
938 /* 27 */
939 "pjsnmp-error-timeout-printing",
940
941 /* 28 */
942 "pjsnmp-verbose",
943
944 /* 29 */
945 "pjsnmp-non-ascii-job-title",
946
947 /* 30 */
948 "pjsnmp-accounting-start-end",
949
950 NULL
951
952 };
953
954
955 /** SNMP version names, 2 and 2c are the same.
956 */
957 static const char * const snmp_version_names[] = {
958 /* 0 */
959 "1",
960
961 /* 1 */
962 "2",
963
964 /* 2 */
965 "2c",
966
967 /* 3 */
968 "3",
969
970 NULL
971
972 };
973
974
975 /** Keywords used in the SNMP models file.
976 */
977 static const char * const model_keys[] = {
978 /* 0 */
979 "map",
980
981 /* 1 */
982 "error condition",
983
984 /* 2 */
985 "add error condition",
986
987 /* 3 */
988 "status text oid",
989
990 /* 4 */
991 "text standby",
992
993 /* 5 */
994 "start standby",
995
996 /* 6 */
997 "text idle",
998
999 /* 7 */
1000 "start idle",
1001
1002 /* 8 */
1003 "always use status text",
1004
1005 /* 9 */
1006 "text busy",
1007
1008 /* 10 */
1009 "start busy",
1010
1011 /* 11 */
1012 "text unknown",
1013
1014 /* 12 */
1015 "start unknown",
1016
1017 /* 13 */
1018 "text error",
1019
1020 /* 14 */
1021 "start error",
1022
1023 /* 15 */
1024 "text warmup",
1025
1026 /* 16 */
1027 "start warmup",
1028
1029 NULL
1030
1031 };
1032
1033
1034 /** Summary state keywords used in the models file, can be abbreviated.
1035 */
1036 static const char * const summary_state_names[] = {
1037 /* 0 */
1038 "u$nknown",
1039
1040 /* 1 */
1041 "e$rror",
1042
1043 /* 2 */
1044 "b$usy",
1045
1046 /* 3 */
1047 "i$dle",
1048
1049 /* 4 */
1050 "w$armup",
1051
1052 /* 5 */
1053 "s$tandby",
1054
1055 NULL
1056
1057 };
1058
1059
1060 /** Device status names, can be abbreviated.
1061 */
1062 static const char * const device_status_names[] = {
1063 /* 0 */
1064 "u$nknown",
1065
1066 /* 1 */
1067 "r$unning",
1068
1069 /* 2 */
1070 "w$arning",
1071
1072 /* 3 */
1073 "t$esting",
1074
1075 /* 4 */
1076 "d$own",
1077
1078 NULL
1079
1080 };
1081
1082
1083 /** Printer status names, can be abbreviated.
1084 */
1085 static const char * const printer_status_names[] = {
1086 /* 0 */
1087 "o$ther",
1088
1089 /* 1 */
1090 "u$nknown",
1091
1092 /* 2 */
1093 "i$dle",
1094
1095 /* 3 */
1096 "p$rinting",
1097
1098 /* 4 */
1099 "w$armup",
1100
1101 NULL
1102
1103 };
1104
1105
1106 /** Names for the condition bits in the SNMP models file.
1107 */
1108 static const char * const condition_names[] = {
1109 /* 0 */
1110 "low-paper",
1111
1112 /* 1 */
1113 "no-paper",
1114
1115 /* 2 */
1116 "low-toner",
1117
1118 /* 3 */
1119 "no-toner",
1120
1121 /* 4 */
1122 "door-open",
1123
1124 /* 5 */
1125 "paper-jam",
1126
1127 /* 6 */
1128 "offline",
1129
1130 /* 7 */
1131 "service-requested",
1132
1133 /* 8 */
1134 "input-tray-missing",
1135
1136 /* 9 */
1137 "output-tray-missing",
1138
1139 /* 10 */
1140 "marker-supply-missing",
1141
1142 /* 11 */
1143 "output-near-full",
1144
1145 /* 12 */
1146 "output-full",
1147
1148 /* 13 */
1149 "input-tray-empty",
1150
1151 /* 14 */
1152 "overdue-preventive-maintenance",
1153
1154 NULL
1155
1156 };
1157
1158
1159 /** Default configuration file for SNMP models.
1160 */
1161 static const char def_model_file[] = {
1162 DK4_INST_DIR_SHARE "/dktools/print-snmp.conf"
1163 };
1164
1165
1166 /** Option values for uppercase options.
1167 */
1168 static char *upper[] = {
1169 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1170 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
1171 };
1172
1173
1174 /** Option values for lowercase options.
1175 */
1176 static char *lower[] = {
1177 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1178 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
1179 };
1180
1181
1182 /** Mapping from device/printer status combinations to short summaries.
1183 */
1184 static int summary_states[] = {
1185 /* unknown-other */ PJSNMP_ST_UNKNOWN ,
1186 /* unknown-unknown */ PJSNMP_ST_UNKNOWN ,
1187 /* unknown-idle */ PJSNMP_ST_UNKNOWN ,
1188 /* unknown-printing */ PJSNMP_ST_UNKNOWN ,
1189 /* unknown-warmup */ PJSNMP_ST_WARMUP ,
1190
1191 /* running-other */ PJSNMP_ST_STANDBY ,
1192 /* running-unknown */ PJSNMP_ST_UNKNOWN ,
1193 /* running-idle */ PJSNMP_ST_IDLE ,
1194 /* running-printing */ PJSNMP_ST_BUSY ,
1195 /* running-warmup */ PJSNMP_ST_WARMUP ,
1196
1197 /* warning-other */ PJSNMP_ST_STANDBY ,
1198 /* warning-unknown */ PJSNMP_ST_UNKNOWN ,
1199 /* warning-idle */ PJSNMP_ST_IDLE ,
1200 /* warning-printing */ PJSNMP_ST_BUSY ,
1201 /* warning-warmup */ PJSNMP_ST_WARMUP ,
1202
1203 /* testing-other */ PJSNMP_ST_UNKNOWN ,
1204 /* testing-unknown */ PJSNMP_ST_UNKNOWN ,
1205 /* testing-idle */ PJSNMP_ST_UNKNOWN ,
1206 /* testing-printing */ PJSNMP_ST_UNKNOWN ,
1207 /* testing-warmup */ PJSNMP_ST_WARMUP ,
1208
1209 /* down-other */ PJSNMP_ST_ERROR ,
1210 /* down-unknown */ PJSNMP_ST_ERROR ,
1211 /* down-idle */ PJSNMP_ST_ERROR ,
1212 /* down-printing */ PJSNMP_ST_ERROR ,
1213 /* down-warmup */ PJSNMP_ST_WARMUP
1214 };
1215
1216
1217
1218 /** Error report for number of bytes transferred.
1219 */
1220 static dk4_er_t er_bytes;
1221
1222
1223 /** Copy of the printcap entry from PRINTCAP_ENTRY environment variable
1224 (allocated, freed during cleanup before exit).
1225 */
1226 static char *printcap_entry = NULL;
1227
1228
1229 /** Printer host name (from printcap entry).
1230 */
1231 static char *host_name = NULL;
1232
1233
1234 /** User name to be charged for the job (from -n or -L option).
1235 */
1236 static char *user_name = NULL;
1237
1238
1239 /** Print queue name (from -P or -Q option).
1240 */
1241 static char *queue_name = NULL;
1242
1243
1244 /** Job title (from -J -f or -N option).
1245 */
1246 static char *job_title = NULL;
1247
1248
1249 /** Job name from -A -j -t or -D option
1250 (only needed for old accounting system).
1251 */
1252 static char *job_name = NULL;
1253
1254
1255 /** File, socket, pipe or host name for accounting
1256 (from pjsnmp-accounting-file in printcap entry).
1257 */
1258 static char *acct_name = NULL;
1259
1260
1261 /** Host to connect to for accounting
1262 (from pjsnmp-accounting-host in printcap entry).
1263 */
1264 static char *acct_host = NULL;
1265
1266
1267 /** SNMP community name
1268 (from pjsnmp-community in printcap entry).
1269 */
1270 static char *snmp_comm = NULL;
1271
1272
1273 /** File name containing SNMP models
1274 (from pjsnmp-model-file in printcap entry).
1275 */
1276 static char *model_file = NULL;
1277
1278
1279 /** SNMP model name (from pjsnmp-model-name in printcap entry).
1280 */
1281 static char *model_name = NULL;
1282
1283
1284 /** Status file for log messages (name obtained from -s cmd arg).
1285 */
1286 static FILE *stt_file = NULL;
1287
1288
1289 /** Output file to use for log messages.
1290 This is either stt_file or stderr, but never NULL.
1291 */
1292 static FILE *out_file = NULL;
1293
1294
1295 /** SNMP session.
1296 */
1297 static struct snmp_session *snmp_sess = NULL;
1298
1299
1300 /** Storage for status text nodes.
1301 */
1302 static dk4_sto_t *s_stn = NULL;
1303
1304
1305 /** Iterator through storage above.
1306 */
1307 static dk4_sto_it_t *i_stn = NULL;
1308
1309
1310 /** Number of bytes transferred.
1311 */
1312 static dk4_um_t bytes_trans = (dk4_um_t)0UL;
1313
1314
1315 /** Timeout if printer not seen printing
1316 (from pjsnmp-require-printing in printcap entry).
1317 If the first page contains a complicated graphics it
1318 might take a longer time to start printing.
1319 */
1320 static dk4_um_t print_timeout = (dk4_um_t)600UL;
1321
1322
1323 /** Timeout if printer is unreachable at start of print job
1324 (from pjsnmp-unreachable-timeout-start in printcap entry).
1325 */
1326 static dk4_um_t unto_start = (dk4_um_t)900UL;
1327
1328
1329 /** Timeout if printer is unreachable during data transfer
1330 (from pjsnmp-unreachable-timeout-printing in printcap entry).
1331 */
1332 static dk4_um_t unto_print = (dk4_um_t)120UL;
1333
1334 /** Timeout if printer socket is unwriteable during data transfer
1335 (from pjsnmp-unwriteable-timeout-printing in printcap entry).
1336 */
1337 static dk4_um_t unwt_print = (dk4_um_t)900UL;
1338
1339 /** Timeout if printer is unreachable at end of print job
1340 (from pjsnmp-unreachable-timeout-end in printcap entry).
1341 */
1342 static dk4_um_t unto_end = (dk4_um_t)120UL;
1343
1344 /** Timeout for error condition before printing to skip job.
1345 */
1346 static dk4_um_t erto_start = (dk4_um_t)900UL;
1347
1348 /** Timeout if printer is in error state during printing.
1349 */
1350 static dk4_um_t erto_print = (dk4_um_t)86400UL;
1351
1352 /** Timeout if printer is in error state after job.
1353 */
1354 static dk4_um_t erto_end = (dk4_um_t)3600UL;
1355
1356 /** Start time printer gone unreachable.
1357 */
1358 static dk4_time_t start_unreach = (dk4_time_t)0UL;
1359
1360 /** Start time printer gone error.
1361 */
1362 static dk4_time_t start_error = (dk4_time_t)0UL;
1363
1364
1365 /** Size of device status OID (number of elements).
1366 */
1367 static const size_t sz_oid_ds = sizeof(oid_ds)/sizeof(oid);
1368
1369
1370 /** Size of printer status OID (number of elements).
1371 */
1372 static const size_t sz_oid_ps = sizeof(oid_ps)/sizeof(oid);
1373
1374
1375 /** Size of page counter OID (number of elements).
1376 */
1377 static const size_t sz_oid_pc = sizeof(oid_pc)/sizeof(oid);
1378
1379
1380 /** Size of printer error OID (number of elements).
1381 */
1382 static const size_t sz_oid_pe = sizeof(oid_pe)/sizeof(oid);
1383
1384
1385 /** Size of printer error OID (number of elements).
1386 */
1387 static const size_t sz_oid_dst = sizeof(oid_dst)/sizeof(oid);
1388
1389
1390 /** Size of printer error OID (number of bytes).
1391 */
1392 static const size_t by_oid_dst = sizeof(oid_dst);
1393
1394
1395 /** Number of items in status text OID.
1396 */
1397 static size_t sz_oid_st = sizeof(oid_dst)/sizeof(oid);
1398
1399
1400 /** Number of bytes in status text OID.
1401 */
1402 static size_t by_oid_st = sizeof(oid_dst);
1403
1404
1405 /** Number of bytes currently stored in stat_text.
1406 */
1407 static size_t sz_stat_text = 0;
1408
1409
1410 /** Number of bytes stored in ostat_text.
1411 */
1412 static size_t sz_ostat_text = 0;
1413
1414
1415 /** Start of program.
1416 */
1417 static dk4_time_t t1 = (dk4_time_t)0UL;
1418
1419
1420 /** Start of data transfer.
1421 */
1422 static dk4_time_t t2 = (dk4_time_t)0UL;
1423
1424
1425 /** End of data transfer.
1426 */
1427 static dk4_time_t t3 = (dk4_time_t)0UL;
1428
1429
1430 /** End of program.
1431 */
1432 static dk4_time_t t4 = (dk4_time_t)0UL;
1433
1434
1435 /** SNMP version number (from pjsnmp-version in printcap entry).
1436 */
1437 static long snmp_version = SNMP_VERSION_1;
1438
1439
1440 /** Bit mask to retrieve real error conditions from
1441 hrPrinterDetectedErrorStates
1442 (modified by error condition and add error condition in model).
1443 */
1444 static unsigned long pdes_error = PDES_ANY_ERROR ;
1445
1446
1447 /** Previous printer detected error state conditions.
1448 */
1449 static unsigned long opdes_cond = 0UL;
1450
1451
1452 /** Current printer detected error state conditions.
1453 */
1454 static unsigned long pdes_cond = 0UL;
1455
1456
1457 /** Page counter at start of job.
1458 */
1459 static unsigned long pc_start = 0UL;
1460
1461
1462 /** Unusable page counter value (start value or at last error).
1463 */
1464 static unsigned long pc_error = 0UL;
1465
1466
1467 /** Page counter at end of job.
1468 */
1469 static unsigned long pc_end = 0UL;
1470
1471
1472 /** Current page counter.
1473 */
1474 static unsigned long pcnt = 0UL;
1475
1476
1477 #endif
1478 /* if DK4_HAVE_LIBNETSNMP && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) */
1479
1480
1481
1482 /** Exit status code, must be one from the enum.
1483 */
1484 static int exval = LPRNG_EXIT_REMOVE;
1485
1486
1487 #if DK4_HAVE_LIBNETSNMP && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET))
1488
1489
1490 /** Flag: Verbose
1491 */
1492 static int verbose = 0;
1493
1494
1495 /** Flag: Allow non-ASCII characters in job title for accounting.
1496 */
1497 static int allow_non_ascii = 0;
1498
1499
1500 /** Always use status text (default: only if summary is "unknown").
1501 */
1502 static int stat_always = 0;
1503
1504
1505 /** Flag: Always use printer detected error state
1506 (from pjsnmp-pdes-always in printcap entry), default: on.
1507 */
1508 static int pdes_always = 1;
1509
1510
1511 /** Flag: Reset of pdes_error was done.
1512 */
1513 static int pdes_reset = 0;
1514
1515
1516 /** Flag: Report unknown states as busy
1517 (from pjsnmp-unknown-busy in printcap entry), default: off.
1518 */
1519 static int unkn_busy = 0;
1520
1521
1522 /** Flag: Do orderly release (from pjsnmp-ordrel in printcap entry).
1523 */
1524 static int host_ordr = 0;
1525
1526
1527 /** Flag: Accounting enabled
1528 (from pjsnmp-accounting-enable in printcap entry).
1529 */
1530 static int acct_enab = 0;
1531
1532
1533 /** Flag: Quota check enabled
1534 (from pjsnmp-accounting-check in printcap entry).
1535 */
1536 static int acct_achk = 0;
1537
1538
1539 /** Flag: Use separated start/end lines for accounting.
1540 Default is to use just one charge line at end.
1541 */
1542 static int acct_start_end = 0;
1543
1544
1545 /** Flag: Read printer responses
1546 (from pjsnmp-read-response in printcap entry).
1547 */
1548 static int read_responses = 0;
1549
1550
1551 /** Flag: Force printable status text
1552 (from pjsnmp-force-printable-status-text in printcap entry).
1553 */
1554 static int force_printable_status_text = 1;
1555
1556 /** Flag: SNMP check during transfer
1557 (from pjsnmp-during-transfer in printcap entry).
1558 */
1559 static int check_trans = 1;
1560
1561
1562 /** Flag: EOF found on standard input.
1563 */
1564 static int have_eof = 0;
1565
1566
1567 /** Ignore failed SNMP session
1568 (from pjsnmp-ignore-failed-session in printcap entry).
1569 */
1570 static int snmp_igfs = 0;
1571
1572
1573 /** Allow printing if accounting connection failed.
1574 (from pjsnmp-allow-printing-if-accounting-connection-failed).
1575 */
1576 static int acct_apcf = 0;
1577
1578
1579 /** Flag: Have page counter at start.
1580 */
1581 static int have_pcs = 0;
1582
1583
1584 /** Flag: Have page counter at end.
1585 */
1586 static int have_pce = 0;
1587
1588
1589 /** Previous device status.
1590 */
1591 static int odevst = 0;
1592
1593
1594 /** Current device status.
1595 */
1596 static int devst = 0;
1597
1598
1599 /** Previous printer status.
1600 */
1601 static int oprist = 0;
1602
1603
1604 /** Current printer status.
1605 */
1606 static int prist = 0;
1607
1608
1609 /** Previous summary status.
1610 */
1611 static int ostsum = PJSNMP_ST_UNKNOWN;
1612
1613
1614 /** Current summary status.
1615 */
1616 static int stsum = PJSNMP_ST_UNKNOWN;
1617
1618
1619 /** Flag: The printer was seen in printing state.
1620 This flag is cleared if the printer was seen in error state,
1621 as some printers go from error to printing via idle.
1622 */
1623 static int seen_printing = 0;
1624
1625
1626 /** Last header used for log messages.
1627 */
1628 static int last_header = 0;
1629
1630
1631 /** Decision from permission check
1632 0=unknown
1633 1=accept
1634 2=hold
1635 3=remove
1636 */
1637 static int decision = 0;
1638
1639
1640 /** Port number to send data to in host representation
1641 (from pjsnmp-port in printcap entry).
1642 */
1643 static unsigned short host_port = 9100;
1644
1645
1646 /** Local port number.
1647 (from pjsnmp-local-port in printcap entry).
1648 */
1649 static unsigned short local_port = 0;
1650
1651
1652 /** Accounting port number in host representation
1653 (from pjsnmp-accounting-port in printcap entry).
1654 */
1655 static unsigned short acct_port = 9100;
1656
1657
1658 #ifdef SIGPIPE
1659 /** Indicator: SIGPIPE signal received.
1660 */
1661 static
1662 DK4_VOLATILE
1663 dk4_sig_atomic_t sig_had_pipe = 0;
1664 #endif
1665
1666
1667 /** Indicator: SIGINT signal received.
1668 */
1669 static
1670 DK4_VOLATILE
1671 dk4_sig_atomic_t sig_had_int = 0;
1672
1673
1674 /** Indicator: SIGTERM signal received.
1675 */
1676 static
1677 DK4_VOLATILE
1678 dk4_sig_atomic_t sig_had_term = 0;
1679
1680
1681 /** Pass volatile pointer through to caller.
1682 The CERT C coding standard recommends to set signal indicator
1683 variables through pointers to avoid
1684 compilers from optimizing too hard (they might attempt
1685 to keep the value in a register although properly marked
1686 as volatile).
1687 @param ptr Address of volatile indicator variable.
1688 @return Same pointer as ptr.
1689 */
1690 static
1691 DK4_VOLATILE
1692 dk4_sig_atomic_t *
sig_pass_pointer(DK4_VOLATILE dk4_sig_atomic_t * ptr)1693 sig_pass_pointer(DK4_VOLATILE dk4_sig_atomic_t *ptr)
1694 {
1695 return ptr;
1696 }
1697
1698
1699 #ifdef SIGPIPE
1700 /** Handler for SIGPIPE signal.
1701 @param signo Signal number (always SIGPIPE, ignored).
1702 */
1703 static
1704 void
sig_handler_pipe(int DK4_ARG_UNUSED (signo))1705 sig_handler_pipe(int DK4_ARG_UNUSED(signo) )
1706 {
1707 DK4_UNUSED_ARG(signo)
1708 *sig_pass_pointer(&sig_had_pipe) = 1;
1709 }
1710 #endif
1711
1712
1713 /** Handler for SIGINT signal.
1714 @param signo Signal number (always SIGINT, ignored).
1715 */
1716 static
1717 void
sig_handler_int(int DK4_ARG_UNUSED (signo))1718 sig_handler_int(int DK4_ARG_UNUSED(signo) )
1719 {
1720 DK4_UNUSED_ARG(signo)
1721 *sig_pass_pointer(&sig_had_int) = 1;
1722 }
1723
1724
1725 /** Handler for SIGTERM signal.
1726 @param signo Signal number (always SIGTERM, ignored).
1727 */
1728 static
1729 void
sig_handler_term(int DK4_ARG_UNUSED (signo))1730 sig_handler_term(int DK4_ARG_UNUSED(signo) )
1731 {
1732 DK4_UNUSED_ARG(signo)
1733 *sig_pass_pointer(&sig_had_term) = 1;
1734 }
1735
1736
1737 /** Read value from volatile atomic type.
1738 This function is necessary as some compilers mis-optimize
1739 direct access to volatile variables (at least if you believe
1740 one of the coding standards).
1741 @param ap Pointer to volatile atomic variable.
1742 @return Contents of the variable.
1743 */
1744 static
1745 dk4_sig_atomic_t
sig_read_atomic(DK4_VOLATILE dk4_sig_atomic_t * ap)1746 sig_read_atomic(DK4_VOLATILE dk4_sig_atomic_t *ap)
1747 {
1748 return (*ap);
1749 }
1750
1751
1752 /** Check whether we can continue or must abort due to signal.
1753 @param dopipe Flag: Check for SIGPIPE too.
1754 @return 1 if we can continue, 0 otherwise.
1755 */
1756 static
1757 int
can_continue(int dopipe)1758 can_continue(int dopipe)
1759 {
1760 int back = 1;
1761 if (0 != sig_read_atomic(&sig_had_term)) { back = 0; }
1762 if (0 != sig_read_atomic(&sig_had_int)) { back = 0; }
1763 #ifdef SIGPIPE
1764 if (0 != dopipe) {
1765 if (0 != sig_read_atomic(&sig_had_pipe)) { back = 0; }
1766 }
1767 #endif
1768 return back;
1769 }
1770
1771
1772 /** Log a timestamp.
1773 @return 1 if time changed since previous log, 0 otherwise.
1774 */
1775 static
1776 int
log_timestamp(void)1777 log_timestamp(void)
1778 {
1779 char buf[64];
1780 static dk4_time_t previous_log = (dk4_time_t)0UL;
1781 dk4_time_t current;
1782 int back = 0;
1783
1784 dk4time_get(¤t);
1785 if (current != previous_log) {
1786 if (dk4time_as_text_c8(buf, sizeof(buf), ¤t, NULL)) {
1787 back = 1;
1788 fputs(pjsnmp_kw[62], out_file);
1789 fputs(buf, out_file);
1790 fputs(pjsnmp_kw[0], out_file);
1791 previous_log = current;
1792 }
1793 }
1794 return back;
1795 }
1796
1797
1798
1799 /** Log a 1 component message with header line.
1800 @param h Header line chooser (1, 2 or 3).
1801 @param i Index of message text in pjsnmp_kw.
1802 */
1803 static
1804 void
log_1_with_header(int h,size_t i)1805 log_1_with_header(int h, size_t i)
1806 {
1807 (void)log_timestamp();
1808 if (last_header != h) {
1809 fputs(pjsnmp_kw[62 + h], out_file);
1810 last_header = h;
1811 }
1812 fputs(pjsnmp_kw[i], out_file);
1813 fflush(out_file);
1814 }
1815
1816
1817
1818 /** Log a 3 components message with header line.
1819 @param h Header line chooser (1, 2 or 3).
1820 @param i1 Index of first text component in pjsnmp_kw.
1821 @param i2 Index of third text component in pjsnmp_kw.
1822 @param s Second text component (variable text).
1823 */
1824 static
1825 void
log_3_with_header(int n,size_t i1,size_t i2,const char * s)1826 log_3_with_header(int n, size_t i1, size_t i2, const char *s)
1827 {
1828 (void)log_timestamp();
1829 if (last_header != n) {
1830 fputs(pjsnmp_kw[62 + n], out_file);
1831 last_header = n;
1832 }
1833 fputs(pjsnmp_kw[i1], out_file);
1834 if (NULL != s) { fputs(s, out_file); }
1835 fputs(pjsnmp_kw[i2], out_file);
1836 fflush(out_file);
1837 }
1838
1839
1840
1841 /** Log a 5 components message with header line.
1842 @param h Header line chooser (1, 2 or 3).
1843 @param i1 Index of first text component in pjsnmp_kw.
1844 @param i2 Index of third text component in pjsnmp_kw.
1845 @param i3 Index of fifth text component in pjsnmp_kw.
1846 @param s1 Second text component (variable text).
1847 @param s2 Fourth text component (variable text).
1848 */
1849 static
1850 void
log_5_with_header(int n,size_t i1,size_t i2,size_t i3,const char * s1,const char * s2)1851 log_5_with_header(
1852 int n,
1853 size_t i1,
1854 size_t i2,
1855 size_t i3,
1856 const char *s1,
1857 const char *s2
1858 )
1859 {
1860 (void)log_timestamp();
1861 if (last_header != n) {
1862 fputs(pjsnmp_kw[62 + n], out_file);
1863 last_header = n;
1864 }
1865 fputs(pjsnmp_kw[i1], out_file);
1866 if (NULL != s1) { fputs(s1, out_file); }
1867 fputs(pjsnmp_kw[i2], out_file);
1868 if (NULL != s2) { fputs(s2, out_file); }
1869 fputs(pjsnmp_kw[i3], out_file);
1870 fflush(out_file);
1871 }
1872
1873
1874
1875 /** Log a 1 component message without header line.
1876 @param i Index of text component in pjsnmp_kw.
1877 */
1878 static
1879 void
log_1_no_header(size_t i)1880 log_1_no_header(size_t i)
1881 {
1882
1883 log_timestamp();
1884 fputs(pjsnmp_kw[i], out_file);
1885 fflush(out_file);
1886
1887 }
1888
1889
1890
1891 /** Log a 3 components message without header line.
1892 @param i1 Index of first text component in pjsnmp_kw.
1893 @param i2 Index of third text component in pjsnmp_kw.
1894 @param s Second text component (variable text).
1895 */
1896 static
1897 void
log_3_no_header(size_t i1,size_t i2,const char * s)1898 log_3_no_header(size_t i1, size_t i2, const char *s)
1899 {
1900
1901 log_timestamp();
1902 fputs(pjsnmp_kw[i1], out_file);
1903 if (NULL != s) { fputs(s, out_file); }
1904 fputs(pjsnmp_kw[i2], out_file);
1905 fflush(out_file);
1906
1907 }
1908
1909
1910
1911 /** Log a 5 components message without header line.
1912 @param i1 Index of first text component in pjsnmp_kw.
1913 @param i2 Index of third text component in pjsnmp_kw.
1914 @param i3 Index of fifth text component in pjsnmp_kw.
1915 @param s1 Second text component (variable text).
1916 @param s2 Fourth text component (variable text).
1917 */
1918 static
1919 void
log_5_no_header(size_t i1,size_t i2,size_t i3,const char * s1,const char * s2)1920 log_5_no_header(
1921 size_t i1,
1922 size_t i2,
1923 size_t i3,
1924 const char *s1,
1925 const char *s2
1926 )
1927 {
1928 log_timestamp();
1929 fputs(pjsnmp_kw[i1], out_file);
1930 if (NULL != s1) { fputs(s1, out_file); }
1931 fputs(pjsnmp_kw[i2], out_file);
1932 if (NULL != s2) { fputs(s2, out_file); }
1933 fputs(pjsnmp_kw[i3], out_file);
1934 fflush(out_file);
1935 }
1936
1937
1938
1939 /** Log a 1 component message related to model file.
1940 @param lineno Line number in model file to complain about.
1941 @param i Index of first text component in pjsnmp_kw.
1942 */
1943 static
1944 void
log_model_1(dk4_um_t lineno,size_t i)1945 log_model_1(dk4_um_t lineno, size_t i)
1946 {
1947 char buf[8*sizeof(dk4_um_t)];
1948 const char *fn;
1949 const char *ptr;
1950 int res;
1951
1952 (void)log_timestamp();
1953 if (3 != last_header) {
1954 last_header = 3;
1955 fputs(pjsnmp_kw[65], out_file);
1956 }
1957 if ((dk4_um_t)0UL < lineno) {
1958 res = dk4ma_write_c8_decimal_unsigned(buf, sizeof(buf), lineno, 0, NULL);
1959 if (0 != res) {
1960 fn = ((NULL != model_file) ? model_file : def_model_file);
1961 ptr = dk4str8_rchr(fn, '/');
1962 if (NULL != ptr) {
1963 ptr++;
1964 } else {
1965 ptr = fn;
1966 }
1967 fputs(ptr, out_file);
1968 fputs(pjsnmp_kw[143], out_file);
1969 fputs(buf, out_file);
1970 fputs(pjsnmp_kw[143], out_file);
1971 fputs(pjsnmp_kw[1], out_file);
1972 }
1973 }
1974 fputs(pjsnmp_kw[i], out_file);
1975 fflush(out_file);
1976 }
1977
1978
1979
1980 /** Log a 3 components message related to model file.
1981 @param lineno Line number in model file to complain about.
1982 @param i1 Index of first text component in pjsnmp_kw.
1983 @param i2 Index of third text component in pjsnmp_kw.
1984 @param s Second text component (variable text).
1985 */
1986 static
1987 void
log_model_3(dk4_um_t lineno,size_t i1,size_t i2,const char * s)1988 log_model_3(
1989 dk4_um_t lineno,
1990 size_t i1,
1991 size_t i2,
1992 const char *s
1993 )
1994 {
1995 char buf[8*sizeof(dk4_um_t)];
1996 const char *fn;
1997 const char *ptr;
1998 int res;
1999
2000 (void)log_timestamp();
2001 if (3 != last_header) {
2002 last_header = 3;
2003 fputs(pjsnmp_kw[65], out_file);
2004 }
2005 if ((dk4_um_t)0UL < lineno) {
2006 res = dk4ma_write_c8_decimal_unsigned(buf, sizeof(buf), lineno, 0, NULL);
2007 if (0 != res) {
2008 fn = ((NULL != model_file) ? model_file : def_model_file);
2009 ptr = dk4str8_rchr(fn, '/');
2010 if (NULL != ptr) {
2011 ptr++;
2012 } else {
2013 ptr = fn;
2014 }
2015 fputs(ptr, out_file);
2016 fputs(pjsnmp_kw[143], out_file);
2017 fputs(buf, out_file);
2018 fputs(pjsnmp_kw[143], out_file);
2019 fputs(pjsnmp_kw[1], out_file);
2020 }
2021 }
2022 fputs(pjsnmp_kw[i1], out_file);
2023 if (NULL != s) { fputs(s, out_file); }
2024 fputs(pjsnmp_kw[i2], out_file);
2025 fflush(out_file);
2026 }
2027
2028
2029
2030 /** Create new status text node.
2031 @param t Text, not 0-terminated.
2032 @param tsz Text size.
2033 @param st Summary state if text is found.
2034 @param fl Flag: This is the start text only.
2035 @return Valid pointer on success, NULL on error.
2036 */
2037 static
2038 status_text_t *
stt_new(const unsigned char * t,size_t tsz,int st,int fl)2039 stt_new(
2040 const unsigned char *t,
2041 size_t tsz,
2042 int st,
2043 int fl
2044 )
2045 {
2046 status_text_t *back = NULL;
2047
2048 if ((NULL != t) && (0 < tsz)) {
2049 back = dk4mem_new(status_text_t,1,NULL);
2050 if (NULL != back) {
2051 DK4_MEMRES(back, sizeof(status_text_t));
2052 back->start = fl;
2053 back->state = st;
2054 back->size = tsz;
2055 back->text = dk4mem_new(unsigned char,tsz,NULL);
2056 if (NULL != back->text) {
2057 DK4_MEMCPY(back->text, t, tsz);
2058 } else {
2059 dk4mem_free(back);
2060 back = NULL;
2061 }
2062 }
2063 #if TRACE_DEBUG
2064 else {
2065 }
2066 #endif
2067 }
2068 #if TRACE_DEBUG
2069 else {
2070 }
2071 #endif
2072
2073 return back;
2074 }
2075
2076
2077
2078 /** Destroy status text node, release memory.
2079 @param ptr Node to destroy.
2080 */
2081 static
2082 void
stt_delete(status_text_t * ptr)2083 stt_delete(status_text_t *ptr)
2084 {
2085
2086 if (NULL != ptr) {
2087 dk4mem_release(ptr->text);
2088 dk4mem_free(ptr);
2089 }
2090
2091 }
2092
2093
2094
2095 /** Compare two status text nodes.
2096 While building the storage the start flag is ignored.
2097 When searching a text for a status text received via SNMP
2098 we have to consider the flag.
2099 @param l Left object.
2100 @param r Right object.
2101 @param cr Comparison criteria (0=build storage, 1=search).
2102 @return Comparison result.
2103 */
2104 static
2105 int
stt_compare(const void * l,const void * r,int cr)2106 stt_compare(const void *l, const void *r, int cr)
2107 {
2108 const status_text_t *pl; /* Left operand */
2109 const status_text_t *pr; /* Right operand */
2110 size_t min; /* Minimum of both text sizes */
2111 int back = 0;
2112
2113 if (NULL != l) {
2114 if (NULL != r) {
2115 pl = (const status_text_t *)l;
2116 pr = (const status_text_t *)r;
2117 min = pl->size;
2118 if (min > pr->size) { min = pr->size; }
2119 if (0 < min) {
2120 back = dk4mem_cmp(pl->text, pr->text, min, NULL);
2121 }
2122 if (0 == back) {
2123 if ((0 == cr) || (0 == pl->start)) {
2124 if (pl->size > pr->size) {
2125 back = 1;
2126 } else {
2127 if (pl->size < pr->size) {
2128 back = -1;
2129 }
2130 }
2131 }
2132 }
2133 } else {
2134 back = 1;
2135 }
2136 } else {
2137 if (NULL != r) {
2138 back = -1;
2139 }
2140 }
2141
2142 return back;
2143 }
2144
2145
2146
2147 /** Set one command line option.
2148 @param Option character.
2149 @param Option argument.
2150 */
2151 static
2152 void
set_option(char c,char * val)2153 set_option(char c, char *val)
2154 {
2155 size_t sz = 0; /* Index of option in arguments array */
2156
2157 if (NULL != val) {
2158 if (('a' <= c) && ('z' >= c)) {
2159 sz = (size_t)(c - 'a');
2160 if (DK4_SIZEOF(lower,DK4_PCHAR) > sz) {
2161 lower[sz] = val;
2162 }
2163 } else {
2164 if (('A' <= c) && ('Z' >= c)) {
2165 sz = (size_t)(c - 'A');
2166 if (DK4_SIZEOF(upper,DK4_PCHAR) > sz) {
2167 upper[sz] = val;
2168 }
2169 }
2170 }
2171 }
2172 }
2173
2174
2175
2176 /** Retrieve argument for one option.
2177 @param c Option character (option name).
2178 @return The value provided as option argument, NULL otherwise.
2179 */
2180 static
2181 char *
get_option(char c)2182 get_option(char c)
2183 {
2184 char *back = NULL;
2185 size_t sz = 0; /* Index of option in arguments array */
2186
2187 if (('a' <= c) && ('z' >= c)) {
2188 sz = (size_t)(c - 'a');
2189 if (DK4_SIZEOF(lower,DK4_PCHAR) > sz) {
2190 back = lower[sz];
2191 }
2192 } else {
2193 if (('A' <= c) && ('Z' >= c)) {
2194 sz = (size_t)(c - 'A');
2195 if (DK4_SIZEOF(upper,DK4_PCHAR) > sz) {
2196 back = upper[sz];
2197 }
2198 }
2199 }
2200 return back;
2201 }
2202
2203
2204
2205 /** Process command line arguments.
2206 @param argc Number of command line arguments.
2207 @param argv Command line arguments array.
2208 @return 1 on success, 0 on error.
2209 */
2210 static
2211 int
process_command_line_arguments(int argc,char * argv[])2212 process_command_line_arguments(int argc, char *argv[])
2213 {
2214 char *curarg; /* Current argument to process */
2215 char *s_name; /* Status file name */
2216 size_t szjnb; /* Size of job name buffer */
2217 int back = 1;
2218 int i; /* Index of current argument */
2219 int res; /* Result for number to text conversion */
2220 char c; /* Option name */
2221
2222 for (i = 1; i < argc; i++) {
2223 curarg = argv[i];
2224 if ('-' == *curarg) {
2225 curarg++;
2226 if ('\0' != *curarg) {
2227 c = *(curarg++);
2228 if ('\0' != *curarg) {
2229 set_option(c, curarg);
2230 } else {
2231 #if VERSION_BEFORE_20160208
2232 /* -c may appear without an argument.
2233 */
2234 back = 0;
2235 /* ERROR: Missing option argument */
2236 log_1_with_header(1, 67);
2237 #else
2238 set_option(c, (char *)(pjsnmp_kw[144]));
2239 #endif
2240 }
2241 } else {
2242 back = 0;
2243 /* ERROR: Only minus */
2244 log_1_with_header(1, 66);
2245 }
2246 } else {
2247 #if 0
2248 /* 2016-02-15 Seeing non-option arguments is not an error,
2249 the accounting file name is passed once
2250 as argument to -a and once as file name.
2251 */
2252 back = 0;
2253 /* ERROR: Non-option argument found */
2254 log_1_with_header(1, 68);
2255 #endif
2256 }
2257 }
2258 if (0 != back) {
2259 s_name = get_option('s');
2260 if (NULL != s_name) {
2261 stt_file = fopen(s_name, "w");
2262 if (NULL != stt_file) {
2263 out_file = stt_file;
2264 }
2265 #if TRACE_DEBUG
2266 else {
2267 }
2268 #endif
2269 }
2270 #if TRACE_DEBUG
2271 else {
2272 }
2273 #endif
2274 user_name = get_option('n');
2275 if (NULL == user_name) user_name = get_option('L');
2276 if (NULL != user_name) {
2277 user_name = dk4str8_start(user_name, NULL);
2278 }
2279 if (NULL != user_name) {
2280 (void)dk4str8_next(user_name, NULL);
2281 if (0 == strlen(user_name)) { user_name = NULL; }
2282 }
2283 if (NULL == user_name) {
2284 /* ERROR: No user name */
2285 log_1_with_header(1, 69);
2286 back = 0;
2287 }
2288 #if TRACE_DEBUG
2289 else {
2290 }
2291 #endif
2292 if (0 != back) {
2293 queue_name = get_option('P');
2294 if (NULL == queue_name) queue_name = get_option('Q');
2295 if (NULL != queue_name) {
2296 queue_name = dk4str8_start(queue_name, NULL);
2297 }
2298 if (NULL != queue_name) {
2299 (void)dk4str8_next(queue_name, NULL);
2300 if (0 == strlen(queue_name)) { queue_name = NULL; }
2301 }
2302 if (NULL == queue_name) {
2303 /* ERROR: No queue name */
2304 log_1_with_header(1, 70);
2305 back = 0;
2306 }
2307 #if TRACE_DEBUG
2308 else {
2309 }
2310 #endif
2311 }
2312 if (0 != back) {
2313 job_name = get_option('A');
2314 if (NULL == job_name) job_name = get_option('j');
2315 if (NULL == job_name) job_name = get_option('t');
2316 if (NULL == job_name) job_name = get_option('D');
2317 if (NULL != job_name) {
2318 job_name = dk4str8_start(job_name, NULL);
2319 }
2320 if (NULL != job_name) {
2321 (void)dk4str8_next(job_name, NULL);
2322 if (0 == strlen(job_name)) { job_name = NULL; }
2323 }
2324 if (NULL == job_name) {
2325 szjnb = sizeof(job_name_buf);
2326 if (0 != dk4str8_cpy_s(job_name_buf, szjnb, pjsnmp_kw[171], NULL)) {
2327 res = dk4ma_write_c8_decimal_unsigned(
2328 &(job_name_buf[strlen(job_name_buf)]),
2329 (szjnb - strlen(job_name_buf)), (dk4_um_t)getpid(), 0, NULL
2330 );
2331 if (0 != res) {
2332 job_name = job_name_buf;
2333 }
2334 }
2335 }
2336 if (NULL == job_name) {
2337 /* ERROR: No job name */
2338 log_1_with_header(1, 71);
2339 back = 0;
2340 }
2341 #if TRACE_DEBUG
2342 else {
2343 }
2344 #endif
2345 }
2346 if (0 != back) {
2347 job_title = get_option('J');
2348 if (NULL == job_title) job_title = get_option('f');
2349 if (NULL == job_title) job_title = get_option('N');
2350 if (NULL != job_title) {
2351 job_title = dk4str8_start(job_title, NULL);
2352 }
2353 if (NULL != job_title) {
2354 dk4str8_normalize(job_title, NULL);
2355 }
2356 #if 0
2357 /* 20160206
2358 Allow job_title to be NULL, pjsnmp_kw[54] is used instead.
2359 */
2360 if (NULL == job_title) {
2361 back = 0;
2362 }
2363 #endif
2364 #if TRACE_DEBUG
2365 else {
2366 }
2367 #endif
2368 }
2369 }
2370
2371 return back;
2372 }
2373
2374
2375
2376 /** Set string pointer.
2377 @param dptr Address of pointer to set.
2378 @param src Value.
2379 @param name Configuration entry name.
2380 @return 1 on success, 0 on error.
2381 */
2382 static
2383 int
set_string(char ** dptr,char * src,const char * name)2384 set_string(char **dptr, char *src, const char *name)
2385 {
2386 int back = 0;
2387
2388 if (NULL != src) {
2389 src = dk4str8_start(src, NULL);
2390 if (NULL != src) {
2391 if (NULL != *dptr) {
2392 /* WARNING: Redefinition */
2393 log_5_with_header(2, 72, 73, 74, name, src);
2394 }
2395 *dptr = src;
2396 back = 1;
2397 } else {
2398 /* ERROR: Empty text */
2399 log_3_with_header(2, 75, 76, name);
2400 }
2401 } else {
2402 /* ERROR: Text string required */
2403 log_3_with_header(2, 77, 78, name);
2404 }
2405 return back;
2406 }
2407
2408
2409
2410 /** Set unsigned short variable.
2411 @param dptr Address of variable to set.
2412 @param src Text containing the value.
2413 @return 1 on success, 0 on error.
2414 */
2415 static
2416 int
set_unsigned_short(unsigned short * dptr,char * src,const char * name)2417 set_unsigned_short(unsigned short *dptr, char *src, const char *name)
2418 {
2419 const char *ep = NULL; /* First unprocessabe char */
2420 int back = 0;
2421 int res; /* Operation result */
2422 unsigned short us;
2423
2424 if (NULL != src) {
2425 src = dk4str8_start(src, NULL);
2426 if (NULL != src) {
2427 res = dk4ma_input_c8_dec_ushort(&us, src, &ep, 1, NULL);
2428 if (0 != res) {
2429 *dptr = us;
2430 back = 1;
2431 } else {
2432 /* ERROR: Not a number! */
2433 log_5_with_header(2, 79, 80, 81, name, src);
2434 }
2435 } else {
2436 /* ERROR: Empty text */
2437 log_3_with_header(2, 75, 76, name);
2438 }
2439 } else {
2440 /* ERROR: Numeric value required */
2441 log_3_with_header(2, 77, 78, name);
2442 }
2443 return back;
2444 }
2445
2446
2447
2448 /** Set SNMP version.
2449 @param dptr Address of variable to set.
2450 @param src Text containing the SNMP version.
2451 @return 1 on success, 0 on error.
2452 */
2453 static
2454 int
set_snmp_version(long * dptr,char * src)2455 set_snmp_version(long *dptr, char *src)
2456 {
2457 int back = 0;
2458
2459 if (NULL != src) {
2460 src = dk4str8_start(src, NULL);
2461 if (NULL != src) {
2462 switch (dk4str8_array_index(snmp_version_names, src, 0)) {
2463 case 0: {
2464 *dptr = SNMP_VERSION_1;
2465 back = 1;
2466 } break;
2467 case 1: case 2: {
2468 *dptr = SNMP_VERSION_2c;
2469 back = 1;
2470 } break;
2471 case 3: {
2472 *dptr = SNMP_VERSION_3;
2473 back = 1;
2474 } break;
2475 default: {
2476 /* ERROR: Illegal SNMP version */
2477 log_3_with_header(2, 82, 83, src);
2478 } break;
2479 }
2480 } else {
2481 /* ERROR: Empty text */
2482 log_3_with_header(2, 75, 76, printcap_entry_names[9]);
2483 }
2484 } else {
2485 /* ERROR: Value required */
2486 log_3_with_header(2, 77, 78, printcap_entry_names[9]);
2487 }
2488
2489 return back;
2490 }
2491
2492
2493
2494 /** Set maximum unsigned integer variable from text.
2495 @param dptr Address of destination variable.
2496 @param src Text containing the value.
2497 @param name Setting name for diagnostics.
2498 @return 1 on success, 0 on error.
2499 */
2500 static
2501 int
set_umax(dk4_um_t * dptr,char * src,const char * name)2502 set_umax(dk4_um_t *dptr, char *src, const char *name)
2503 {
2504 const char *ep = NULL; /* First unprocessable character */
2505 dk4_um_t i = 0UL; /* Result value */
2506 int back = 0;
2507 int res; /* Operation result */
2508
2509 if (NULL != src) {
2510 res = dk4ma_input_c8_dec_dk4_um_t(&i, src, &ep, 1, NULL);
2511 if (0 != res) {
2512 *dptr = i;
2513 back = 1;
2514 } else {
2515 /* ERROR: Not a numeric value */
2516 log_5_with_header(2, 84, 161, 86, name, src);
2517 }
2518 } else {
2519 /* ERROR: Value required */
2520 log_3_with_header(2, 77, 78, name);
2521 }
2522 return back;
2523 }
2524
2525
2526
2527 #if 0
2528
2529 /** Set integer variable from text.
2530 @param dptr Address of destination variable.
2531 @param src Text containing the value.
2532 @param name Setting name for diagnostics.
2533 @return 1 on success, 0 on error.
2534 */
2535 static
2536 int
2537 set_integer(int *dptr, char *src, const char *name)
2538 {
2539 const char *ep = NULL; /* First unprocessable character */
2540 int i = 0; /* Result value */
2541 int back = 0;
2542 int res; /* Operation result */
2543
2544 if (NULL != src) {
2545 res = dk4ma_input_c8_dec_int(&i, src, &ep, 1, NULL);
2546 if (0 != res) {
2547 *dptr = i;
2548 back = 1;
2549 } else {
2550 /* ERROR: Not a numeric value */
2551 log_5_with_header(2, 84, 85, 86, name, src);
2552 }
2553 } else {
2554 /* ERROR: Value required */
2555 log_3_with_header(2, 77, 78, name);
2556 }
2557 return back;
2558 }
2559
2560 #endif
2561
2562
2563
2564 /** Process one configuration item from a printcap entry.
2565 @param ci Item to process.
2566 @return 1 on success, 0 on error.
2567 */
2568 static
2569 int
process_one_config_item(char * ci)2570 process_one_config_item(char *ci)
2571 {
2572 char *value = NULL; /* Value for config item */
2573 int btrue = 1; /* Condition */
2574 int back = 1;
2575
2576 value = dk4str8_chr(ci, '=');
2577 if (NULL != value) {
2578 *(value++) = '\0';
2579 value = dk4str8_start(value, NULL);
2580 } else {
2581 value = dk4str8_chr(ci, '@');
2582 if (NULL != value) {
2583 *value = '\0';
2584 btrue = 0;
2585 }
2586 #if TRACE_DEBUG
2587 else {
2588 }
2589 #endif
2590 }
2591 switch (dk4str8_array_index(printcap_entry_names, ci, 0)) {
2592 case 0: {
2593 if (0 == set_string(&host_name, value, printcap_entry_names[0])) {
2594 back = 0;
2595 }
2596 } break;
2597 case 1: {
2598 if (0 == set_unsigned_short(&host_port, value, printcap_entry_names[1])) {
2599 back = 0;
2600 }
2601 } break;
2602 case 2: {
2603 host_ordr = btrue;
2604 } break;
2605 case 3: {
2606 acct_enab = btrue;
2607 } break;
2608 case 4: {
2609 acct_achk = btrue;
2610 } break;
2611 case 5: {
2612 if (0 == set_string(&acct_name, value, printcap_entry_names[5])) {
2613 back = 0;
2614 }
2615 } break;
2616 case 6: {
2617 if (0 == set_string(&acct_host, value, printcap_entry_names[6])) {
2618 back = 0;
2619 }
2620 } break;
2621 case 7: {
2622 if (0 == set_unsigned_short(&acct_port, value, printcap_entry_names[7])) {
2623 back = 0;
2624 }
2625 } break;
2626 case 8: {
2627 if (NULL != value) {
2628 if (0 == set_umax(&print_timeout, value, printcap_entry_names[8])) {
2629 back = 0;
2630 }
2631 } else {
2632 if (0 == btrue) { print_timeout = (dk4_um_t)0UL; }
2633 else { back = 0; }
2634 }
2635 } break;
2636 case 9: {
2637 if (0 == set_snmp_version(&snmp_version, value)) {
2638 back = 0;
2639 }
2640 } break;
2641 case 10: {
2642 if (0 == set_string(&snmp_comm, value, printcap_entry_names[10])) {
2643 back = 0;
2644 }
2645 } break;
2646 case 11: {
2647 check_trans = btrue;
2648 } break;
2649 case 12: {
2650 if (0 == set_string(&model_file, value, printcap_entry_names[12])) {
2651 back = 0;
2652 }
2653 } break;
2654 case 13: {
2655 if (0 == set_string(&model_name, value, printcap_entry_names[13])) {
2656 back = 0;
2657 }
2658 } break;
2659 case 14: {
2660 snmp_igfs = btrue;
2661 } break;
2662 case 15: {
2663 acct_apcf = btrue;
2664 } break;
2665 case 16: {
2666 pdes_always = btrue;
2667 } break;
2668 case 17: {
2669 unkn_busy = btrue;
2670 } break;
2671 case 18: {
2672 if (NULL != value) {
2673 if (0 == set_umax(&unto_start, value, printcap_entry_names[18])) {
2674 back = 0;
2675 }
2676 } else {
2677 if (0 == btrue) { unto_start = (dk4_um_t)0UL; }
2678 else { back = 0; }
2679 }
2680 } break;
2681 case 19: {
2682 if (NULL != value) {
2683 if (0 == set_umax(&unto_end, value, printcap_entry_names[19])) {
2684 back = 0;
2685 }
2686 } else {
2687 if (0 == btrue) { unto_end = (dk4_um_t)0UL; }
2688 else { back = 0; }
2689 }
2690 } break;
2691 case 20: {
2692 if (NULL != value) {
2693 if (0 == set_umax(&unto_print, value, printcap_entry_names[20])) {
2694 back = 0;
2695 }
2696 } else {
2697 if (0 == btrue) { unto_print = (dk4_um_t)0UL; }
2698 else { back = 0; }
2699 }
2700 } break;
2701 case 21: {
2702 if (0 == set_unsigned_short(&local_port, value, printcap_entry_names[21]))
2703 {
2704 back = 0;
2705 }
2706 } break;
2707 case 22: {
2708 if (NULL != value) {
2709 if (0 == set_umax(&unwt_print, value, printcap_entry_names[22])) {
2710 back = 0;
2711 }
2712 } else {
2713 if (0 == btrue) { unwt_print = (dk4_um_t)0UL; }
2714 else {
2715 back = 0;
2716 }
2717 }
2718 } break;
2719 case 23: {
2720 read_responses = btrue;
2721 } break;
2722 case 24: {
2723 force_printable_status_text = btrue;
2724 } break;
2725 case 25: {
2726 if (NULL != value) {
2727 if (0 == set_umax(&erto_start, value, printcap_entry_names[22])) {
2728 back = 0;
2729 }
2730 } else {
2731 if (0 == btrue) { erto_start = (dk4_um_t)0UL; }
2732 else {
2733 back = 0;
2734 }
2735 }
2736 } break;
2737 case 26: {
2738 if (NULL != value) {
2739 if (0 == set_umax(&erto_end, value, printcap_entry_names[22])) {
2740 back = 0;
2741 }
2742 } else {
2743 if (0 == btrue) { erto_end = (dk4_um_t)0UL; }
2744 else {
2745 back = 0;
2746 }
2747 }
2748 } break;
2749 case 27: {
2750 if (NULL != value) {
2751 if (0 == set_umax(&erto_print, value, printcap_entry_names[22])) {
2752 back = 0;
2753 }
2754 } else {
2755 if (0 == btrue) { erto_print = (dk4_um_t)0UL; }
2756 else {
2757 back = 0;
2758 }
2759 }
2760 } break;
2761 case 28: {
2762 verbose = btrue;
2763 } break;
2764 case 29: {
2765 allow_non_ascii = btrue;
2766 } break;
2767 case 30: {
2768 acct_start_end = btrue;
2769 } break;
2770 }
2771 return back;
2772 }
2773
2774
2775
2776 /** Check whether the line opens the model we are searching for.
2777 @param str Text to check.
2778 @return 1 on success, 0 on error.
2779 */
2780 static
2781 int
check_in_model(char * str,dk4_um_t lineno)2782 check_in_model(char *str, dk4_um_t lineno)
2783 {
2784 char *ptr; /* Closing square bracket */
2785 int back = 0;
2786
2787 str = dk4str8_start(str, NULL);
2788 if (NULL != str) {
2789 ptr = dk4str8_chr(str, ']');
2790 if (NULL != ptr) {
2791 *ptr = '\0';
2792 dk4str8_normalize(str, NULL);
2793 if (0 == strcmp(str, model_name)) {
2794 back = 1;
2795 }
2796 #if TRACE_DEBUG
2797 else {
2798 }
2799 #endif
2800 } else {
2801 /* ERROR: Syntax */
2802 log_model_1(lineno, 87);
2803 }
2804 } else {
2805 /* ERROR: Syntax */
2806 log_model_1(lineno, 88);
2807 }
2808 return back;
2809 }
2810
2811
2812
2813 /** Overwrite one mapping from device/printer status to summary state.
2814 @param devst Device status.
2815 @param prst Printer status.
2816 @param sujmst Summary state resulting from device and printer status.
2817 */
2818 static
2819 void
set_state_mapping(int devst,int prst,int sumst)2820 set_state_mapping(int devst, int prst, int sumst)
2821 {
2822
2823 if ((0 < devst) && (6 > devst)) {
2824 if ((0 < prst) && (6 > prst)) {
2825 summary_states[5 * devst + prst - 6] = sumst;
2826 }
2827 #if TRACE_DEBUG
2828 else {
2829 }
2830 #endif
2831 }
2832 #if TRACE_DEBUG
2833 else {
2834 }
2835 #endif
2836
2837 }
2838
2839
2840
2841 /** Map device and printer status to summary state.
2842 @param devst Device status.
2843 @param prst Printer status.
2844 @return Summary state.
2845 */
2846 static
2847 int
get_state_mapping(int devst,int prst)2848 get_state_mapping(int devst, int prst)
2849 {
2850 int back = PJSNMP_ST_UNKNOWN;
2851
2852 if ((0 < devst) && (6 > devst)) {
2853 if ((0 < prst) && (6 > prst)) {
2854 back = summary_states[5 * devst + prst - 6];
2855
2856 }
2857 #if TRACE_DEBUG
2858 else {
2859 }
2860 #endif
2861 }
2862 #if TRACE_DEBUG
2863 else {
2864 }
2865 #endif
2866
2867 return back;
2868 }
2869
2870
2871
2872 /** Add a state mapping.
2873 @param str Mapping text.
2874 @param lineno Line number (for diagnostics).
2875 @return 1 on success, 0 on error.
2876 */
2877 static
2878 int
model_conf_map(char * str,dk4_um_t lineno)2879 model_conf_map(char *str, dk4_um_t lineno)
2880 {
2881 char *psummary; /* Summary state name */
2882 char *ppr; /* Printer status */
2883 int stsum; /* Summary state */
2884 int stdev; /* Device status */
2885 int stpri; /* Printer status */
2886 int back = 0;
2887
2888 psummary = dk4str8_next(str, NULL);
2889 if (NULL != psummary) {
2890 ppr = dk4str8_chr(str, '/');
2891 if (NULL != ppr) {
2892 *(ppr++) = '\0';
2893 ppr = dk4str8_start(ppr, NULL);
2894 if (NULL != ppr) {
2895 back = 1;
2896 switch (dk4str8_abbr_index(summary_state_names, '$', psummary, 0)) {
2897 case 0: {
2898 stsum = PJSNMP_ST_UNKNOWN;
2899 } break;
2900 case 1: {
2901 stsum = PJSNMP_ST_ERROR;
2902 } break;
2903 case 2: {
2904 stsum = PJSNMP_ST_BUSY;
2905 } break;
2906 case 3: {
2907 stsum = PJSNMP_ST_IDLE;
2908 } break;
2909 case 4: {
2910 stsum = PJSNMP_ST_WARMUP;
2911 } break;
2912 case 5: {
2913 stsum = PJSNMP_ST_STANDBY;
2914 } break;
2915 default: {
2916 back = 0;
2917 /* ERROR: Illegal summary state */
2918 log_model_3(lineno, 89, 90, psummary);
2919 } break;
2920 }
2921 if (0 != back) {
2922 if (0 == strcmp(str, pjsnmp_kw[3])) {
2923 if (0 == strcmp(ppr, pjsnmp_kw[3])) {
2924 for (stdev = 1; stdev < 6; stdev++) {
2925 for (stpri = 1; stpri < 6; stpri++) {
2926 set_state_mapping(stdev, stpri, stsum);
2927 }
2928 }
2929 } else {
2930 stpri = dk4str8_abbr_index(printer_status_names, '$', ppr, 0);
2931 if (-1 < stpri) {
2932 stpri++;
2933 for (stdev = 1; stdev < 6; stdev++) {
2934 set_state_mapping(stdev, stpri, stsum);
2935 }
2936 } else {
2937 back = 0;
2938 /* ERROR: Syntax, illegal printer status name */
2939 log_model_3(lineno, 91, 92, ppr);
2940 }
2941 }
2942 } else {
2943 stdev = dk4str8_abbr_index(device_status_names, '$', str, 0);
2944 if (-1 < stdev) {
2945 stdev++;
2946 if (0 == strcmp(ppr, pjsnmp_kw[3])) {
2947 for (stpri = 1; stpri < 6; stpri++) {
2948 set_state_mapping(stdev, stpri, stsum);
2949 }
2950 } else {
2951 stpri = dk4str8_abbr_index(printer_status_names, '$', ppr, 0);
2952 if (-1 < stpri) {
2953 stpri++;
2954 set_state_mapping(stdev, stpri, stsum);
2955 } else {
2956 back = 0;
2957 /* ERROR: Syntax, illegal printer status name */
2958 log_model_3(lineno, 91, 92, ppr);
2959 }
2960 }
2961 } else {
2962 back = 0;
2963 /* ERROR: Syntax, illegal device status name */
2964 log_model_3(lineno, 93, 94, ppr);
2965 }
2966 }
2967 } else {
2968 }
2969 } else {
2970 /* ERROR: Syntax, printer status missing */
2971 log_model_1(lineno, 95);
2972 }
2973 } else {
2974 /* ERROR: Syntax, printer status missing */
2975 log_model_1(lineno, 95);
2976 }
2977 } else {
2978 /* ERROR: Syntax, summary state missing */
2979 log_model_1(lineno, 96);
2980 }
2981 return back;
2982 }
2983
2984
2985
2986 /** Add a condition to the error conditions list.
2987 @param str Text for condition.
2988 @param addcond Flag: Add to existing list, do not reset.
2989 @param lineno Line number (for diagnostics).
2990 @return 1 on success, 0 on error.
2991 */
2992 static
2993 int
model_conf_error_condition(char * str,int addcond,dk4_um_t lineno)2994 model_conf_error_condition(char *str, int addcond, dk4_um_t lineno)
2995 {
2996 unsigned long cond; /* Used to build condition bit */
2997 int i; /* Index of keyword in condition_names */
2998 int back = 0;
2999
3000 if ((0 == addcond) && (0 == pdes_reset)) {
3001 pdes_error = 0UL;
3002 }
3003 pdes_reset = 1;
3004 dk4str8_normalize(str, NULL);
3005 i = dk4str8_array_index(condition_names, str, 0);
3006 if (-1 < i) {
3007 back = 1;
3008 cond = 0x80000000UL;
3009 while (0 < i--) { cond = cond / 2UL; }
3010 pdes_error |= cond;
3011 } else {
3012 /* ERROR: Syntax, illegal condition name */
3013 log_model_3(lineno, 97, 98, str);
3014 }
3015 return back;
3016 }
3017
3018
3019
3020 /** Set status text OID.
3021 @param str Text containing the OID.
3022 @param lineno Line number (for diagnostics).
3023 @return 1 on success, 0 on error.
3024 */
3025 static
3026 int
model_conf_status_oid(char * str,dk4_um_t lineno)3027 model_conf_status_oid(char *str, dk4_um_t lineno)
3028 {
3029 const char *ep; /* First unprocessable character */
3030 char *np; /* Next part */
3031 dk4_um_t num; /* Numeric value for sub-id */
3032 size_t used = 0; /* Number of used sub-ids */
3033 int back = 1;
3034 int res; /* Operation result */
3035
3036 dk4str8_normalize(str, NULL);
3037 if ('.' == *str) {
3038 str++;
3039 }
3040 while ((NULL != str) && (1 == back)) {
3041 np = dk4str8_chr(str, '.');
3042 if (NULL != np) {
3043 *(np++) = '\0';
3044 }
3045 ep = NULL;
3046 res = dk4ma_input_c8_dec_dk4_um_t(&num, str, &ep, 1, NULL);
3047 if (0 != res) {
3048 if ((dk4_um_t)(MAX_SUBID) >= num) {
3049 if (used < DK4_SIZEOF(oid_st,oid)) {
3050 oid_st[used++] = (oid)num;
3051 } else {
3052 /* ERROR: Syntax, OID too long */
3053 log_model_1(lineno, 99);
3054 }
3055 } else {
3056 back = 0;
3057 /* ERROR: Syntax, overflow */
3058 log_model_3(lineno, 100, 101, str);
3059 }
3060 } else {
3061 back = 0;
3062 /* ERROR: Syntax, not a number */
3063 log_model_3(lineno, 102, 103, str);
3064 }
3065 str = np;
3066 }
3067 if (0 == used) {
3068 back = 0;
3069 /* ERROR: Syntax, empty OID */
3070 log_model_1(lineno, 104);
3071 }
3072 if (0 != back) {
3073 sz_oid_st = used;
3074 } else {
3075 sz_oid_st = 0;
3076 }
3077 by_oid_st = sz_oid_st * sizeof(oid);
3078
3079 return back;
3080 }
3081
3082
3083
3084 /** Remove final double quote from string.
3085 @param str String to modify.
3086 @return 1 on success, 0 on error.
3087 */
3088 static
3089 int
remove_final_double_quote(char * str)3090 remove_final_double_quote(char *str)
3091 {
3092 char *ptr; /* Position of final double quote */
3093 int back = 0;
3094
3095 ptr = dk4str8_rchr(str, '"');
3096 if (NULL != ptr) {
3097 *(ptr++) = '\0';
3098 back = 1;
3099 while ('\0' != *ptr) {
3100 if (' ' != *ptr) {
3101 if ('\t' != *ptr) {
3102 if ('\r' != *ptr) {
3103 if ('\n' != *ptr) {
3104 back = 0;
3105 }
3106 }
3107 }
3108 }
3109 ptr++;
3110 }
3111 }
3112 #if TRACE_DEBUG
3113 else {
3114 }
3115 #endif
3116
3117 return back;
3118 }
3119
3120
3121
3122 /** Add a text indicating a state to the storage.
3123 @param str Indicator text.
3124 @param state State number indicated by the text.
3125 @param start Flag: This text is just the start of the real text.
3126 @param lineno Line number (for diagnostics).
3127 @return 1 on success, 0 on error.
3128 */
3129 static
3130 int
model_conf_state_text(char * str,int state,int start,dk4_um_t lineno)3131 model_conf_state_text(char *str, int state, int start, dk4_um_t lineno)
3132 {
3133 status_text_t *stptr; /* New configuration node to add */
3134 const char *ep; /* First unprocessable char when reading number */
3135 char *cptr; /* Current word to process */
3136 char *nptr; /* Next word (remaining text) to process */
3137 size_t sl; /* String length */
3138 int res; /* Operation result */
3139 int back = 0;
3140 unsigned char uc; /* Current byte found */
3141
3142 if (NULL == s_stn) {
3143 s_stn = dk4sto_open(NULL);
3144 if (NULL != s_stn) {
3145 dk4sto_set_comp(s_stn, stt_compare, 0);
3146 }
3147 }
3148 if ((NULL != s_stn) && (NULL == i_stn)) {
3149 i_stn = dk4sto_it_open(s_stn, NULL);
3150 }
3151 if ((NULL != s_stn) && (NULL != i_stn)) {
3152 if (NULL != str) {
3153 if ('"' == *str) {
3154 str++;
3155 if (0 != remove_final_double_quote(str)) {
3156 sl = strlen(str);
3157 if (0 < sl) {
3158 stptr = stt_new((unsigned char *)str, sl, state, start);
3159 if (NULL != stptr) {
3160 if (0 != dk4sto_add(s_stn, stptr, NULL)) {
3161 back = 1;
3162 } else {
3163 /* ERROR: Allocation failed */
3164 log_model_1(lineno, 105);
3165 stt_delete(stptr);
3166 }
3167 } else {
3168 /* ERROR: Allocation failed */
3169 log_model_1(lineno, 105);
3170 }
3171 } else {
3172 /* ERROR: Syntax, string empty */
3173 log_model_1(lineno, 106);
3174 }
3175 } else {
3176 /* ERROR: Syntax, closing double quote missing or text after */
3177 log_model_1(lineno, 107);
3178 }
3179 } else {
3180 sl = 0;
3181 cptr = str;
3182 back = 1;
3183 while ((NULL != cptr) && (0 != back)) {
3184 nptr = dk4str8_next(cptr, NULL);
3185 ep = NULL;
3186 res = dk4ma_input_c8_hex_uchar(&uc, cptr, &ep, 1, NULL);
3187 if (0 != res) {
3188 str[sl++] = (char)uc;
3189 } else {
3190 back = 0;
3191 /* ERROR: Syntax, not a hexadecimal byte */
3192 log_model_3(lineno, 108, 109, cptr);
3193 }
3194 cptr = nptr;
3195 }
3196 if (0 != back) {
3197 if (0 < sl) {
3198 stptr = stt_new((unsigned char *)str, sl, state, start);
3199 if (NULL != stptr) {
3200 if (0 == dk4sto_add(s_stn, stptr, NULL)) {
3201 back = 0;
3202 stt_delete(stptr);
3203 /* ERROR: Allocation failed */
3204 log_model_1(lineno, 105);
3205 }
3206 #if TRACE_DEBUG
3207 else {
3208 }
3209 #endif
3210 } else {
3211 back = 0;
3212 /* ERROR: Memory allocation failed */
3213 log_model_1(lineno, 105);
3214 }
3215 } else {
3216 /* ERROR: Syntax, empty string */
3217 log_model_1(lineno, 106);
3218 }
3219 }
3220 }
3221 } else {
3222 /* ERROR: Value required */
3223 log_model_1(lineno, 110);
3224 }
3225 } else {
3226 /* ERROR: Memory allocation failed */
3227 log_model_1(lineno, 105);
3228 }
3229 return back;
3230 }
3231
3232
3233
3234 /** Process one configuration line from model file.
3235 @param str Line to process.
3236 @param lineno Line number in the file (for diagnostics).
3237 @return 1 on success, 0 on error.
3238 */
3239 static
3240 int
process_model_line(char * str,dk4_um_t lineno)3241 process_model_line(char *str, dk4_um_t lineno)
3242 {
3243 char *valptr; /* Value in line */
3244 int back = 0;
3245
3246 valptr = dk4str8_chr(str, '=');
3247 if (NULL != valptr) {
3248 *(valptr++) = '\0';
3249 dk4str8_normalize(str, NULL);
3250 valptr = dk4str8_start(valptr, NULL);
3251 if (NULL != valptr) {
3252 switch (dk4str8_array_index(model_keys, str, 0)) {
3253 case 0: {
3254 back = model_conf_map(valptr, lineno);
3255 } break;
3256 case 1: {
3257 back = model_conf_error_condition(valptr, 0, lineno);
3258 } break;
3259 case 2: {
3260 back = model_conf_error_condition(valptr, 1, lineno);
3261 } break;
3262 case 3: {
3263 back = model_conf_status_oid(valptr, lineno);
3264 } break;
3265 case 4: {
3266 back = model_conf_state_text(valptr, PJSNMP_ST_STANDBY, 0, lineno);
3267 } break;
3268 case 5: {
3269 back = model_conf_state_text(valptr, PJSNMP_ST_STANDBY, 1, lineno);
3270 } break;
3271 case 6: {
3272 back = model_conf_state_text(valptr, PJSNMP_ST_IDLE, 0, lineno);
3273 } break;
3274 case 7: {
3275 back = model_conf_state_text(valptr, PJSNMP_ST_IDLE, 1, lineno);
3276 } break;
3277 case 8: {
3278 back = 1;
3279 if (NULL != valptr) {
3280 valptr = dk4str8_start(valptr, NULL);
3281 if (NULL != valptr) {
3282 dk4str8_normalize(valptr, NULL);
3283 if (dk4str8_is_bool(valptr)) {
3284 stat_always = dk4str8_is_on(valptr);
3285 } else {
3286 back = 0;
3287 /* ERROR: Not a boolean */
3288 log_model_3(lineno, 111, 112, valptr);
3289 }
3290 } else {
3291 stat_always = 1;
3292 }
3293 } else {
3294 stat_always = 1;
3295 }
3296 } break;
3297 case 9: {
3298 back = model_conf_state_text(valptr, PJSNMP_ST_BUSY, 0, lineno);
3299 } break;
3300 case 10: {
3301 back = model_conf_state_text(valptr, PJSNMP_ST_BUSY, 1, lineno);
3302 } break;
3303 case 11: {
3304 back = model_conf_state_text(valptr, PJSNMP_ST_UNKNOWN, 0, lineno);
3305 } break;
3306 case 12: {
3307 back = model_conf_state_text(valptr, PJSNMP_ST_UNKNOWN, 1, lineno);
3308 } break;
3309 case 13: {
3310 back = model_conf_state_text(valptr, PJSNMP_ST_ERROR, 0, lineno);
3311 } break;
3312 case 14: {
3313 back = model_conf_state_text(valptr, PJSNMP_ST_ERROR, 1, lineno);
3314 } break;
3315 case 15: {
3316 back = model_conf_state_text(valptr, PJSNMP_ST_WARMUP, 0, lineno);
3317 } break;
3318 case 16: {
3319 back = model_conf_state_text(valptr, PJSNMP_ST_WARMUP, 1, lineno);
3320 } break;
3321 default: {
3322 /* ERROR: Syntax, unknown key */
3323 log_model_3(lineno, 113, 114, str);
3324 } break;
3325 }
3326 } else {
3327 /* ERROR: Syntax, missing value */
3328 log_model_1(lineno, 110);
3329 }
3330 } else {
3331 /* ERROR: Syntax, missing value */
3332 log_model_1(lineno, 110);
3333 }
3334 return back;
3335 }
3336
3337
3338
3339 /** Configure for specified SNMP model.
3340 */
3341 static
3342 int
process_model_setup(void)3343 process_model_setup(void)
3344 {
3345 FILE *infile; /* Input file to read */
3346 char *p1; /* Start of text in line */
3347 dk4_um_t lineno = (dk4_um_t)0UL;
3348 int in_model = 0; /* Flag: In model to read */
3349 int found = 0; /* Flag: Model was found */
3350 int back = 0;
3351
3352 infile = fopen(((NULL != model_file) ? model_file : def_model_file), "r");
3353 if (NULL != infile) {
3354 back = 1;
3355 while (NULL != fgets(pjsnmp_buf, sizeof(pjsnmp_buf), infile)) {
3356 lineno++;
3357 #if TRACE_DEBUG
3358 dk4str8_delnl(pjsnmp_buf);
3359 #endif
3360 p1 = dk4str8_start(pjsnmp_buf, NULL);
3361 if (NULL != p1) {
3362 if ('[' == *p1) {
3363 in_model = check_in_model(++p1, lineno);
3364 if (0 != in_model) {
3365 found = 1;
3366 }
3367 } else {
3368 if (0 != in_model) {
3369 if ('#' != *p1) {
3370 #if !TRACE_DEBUG
3371 dk4str8_delnl(pjsnmp_buf);
3372 #endif
3373 if (0 == process_model_line(p1, lineno)) {
3374 back = 0;
3375 }
3376 }
3377 }
3378 }
3379 }
3380 }
3381 fclose(infile);
3382 if (0 == found) {
3383 back = 0;
3384 /* ERROR: Model not found */
3385 log_model_3((dk4_um_t)0UL, 115, 116, model_name);
3386 }
3387 } else {
3388 /* ERROR: Failed to open model file */
3389 log_model_3(
3390 (dk4_um_t)0UL, 115, 116,
3391 ((NULL != model_file) ? model_file : def_model_file)
3392 );
3393 }
3394 return back;
3395 }
3396
3397
3398
3399 /** Process PRINTCAP_ENTRY and optionally
3400 model file.
3401 @param argc Number of command line arguments.
3402 @param argv Command line arguments array.
3403 @return 1 on success, 0 on error.
3404 */
3405 static
3406 int
process_setup(void)3407 process_setup(void)
3408 {
3409 char *ptr; /* Environment variable value */
3410 char *cl; /* Current line */
3411 char *nl; /* Next line */
3412 char *ci; /* Current item */
3413 char *ni; /* Next item */
3414 int found = 0; /* Already item found */
3415 int back = 0;
3416
3417
3418 ptr = getenv(pjsnmp_kw[2]);
3419 if (NULL != ptr) {
3420 printcap_entry = dk4str8_dup(ptr, NULL);
3421 if (NULL != printcap_entry) {
3422 back = 1;
3423 cl = printcap_entry;
3424 while (NULL != cl) {
3425 nl = dk4str8_chr(cl, '\n');
3426 if (NULL != nl) {
3427 *(nl++) = '\0';
3428 nl = dk4str8_start(nl, NULL);
3429 }
3430 ptr = cl;
3431 while ('\0' != *ptr) {
3432 if ('\r' == *ptr) { *ptr = '\0'; } else { ptr++; }
3433 }
3434 ci = dk4str8_start(cl, NULL);
3435 if (NULL != ci) {
3436 if ('#' != *ci) {
3437 while (NULL != ci) {
3438 ni = dk4str8_chr(ci, ':');
3439 if (NULL != ni) {
3440 *(ni++) = '\0'; ni = dk4str8_start(ni, NULL);
3441 }
3442 ci = dk4str8_start(ci, NULL);
3443 if (NULL != ci) {
3444 if (0 != found) {
3445 if (0 == process_one_config_item(ci)) {
3446 back = 0;
3447 }
3448 }
3449 found = 1;
3450 }
3451 ci = ni;
3452 }
3453 }
3454 }
3455 cl = nl;
3456 }
3457 if (NULL == host_name) {
3458 back = 0;
3459 /* ERROR: Host name of printer required */
3460 log_1_with_header(2, 119);
3461 }
3462 if (0 != back) {
3463 if (NULL != model_name) {
3464 back = process_model_setup();
3465 }
3466 }
3467 if (0 != back) {
3468 if (0 != acct_achk) {
3469 acct_enab = 1;
3470 }
3471 if (0 != acct_enab) {
3472 if (NULL == acct_name) {
3473 if ((NULL == acct_host) || (0 == acct_port)) {
3474 log_1_with_header(2, 165);
3475 back = 0;
3476 }
3477 }
3478 }
3479 }
3480 } else {
3481 /* ERROR: Memory allocation failed */
3482 log_1_with_header(2, 105);
3483 }
3484 } else {
3485 /* ERROR: Missing printcap entry environment variable */
3486 log_1_with_header(2, 120);
3487 }
3488
3489 return back;
3490 }
3491
3492
3493
3494 /** Eat up remaining input from standard input.
3495 */
3496 static
3497 void
eatup_input(void)3498 eatup_input(void)
3499 {
3500 size_t rdb; /* Number of read bytes */
3501 int cc = 1;
3502
3503 do {
3504 if (0 != can_continue(0)) {
3505 if (0 == have_eof) {
3506 rdb = fread(pjsnmp_buf, 1, sizeof(pjsnmp_buf), stdin);
3507 if (0 == rdb) {
3508 cc = 0;
3509 have_eof = 1;
3510 }
3511 #if TRACE_DEBUG
3512 else {
3513 }
3514 #endif
3515 } else {
3516 cc = 0;
3517 }
3518 } else {
3519 cc = -1;
3520 }
3521 } while (1 == cc);
3522
3523 }
3524
3525
3526
3527 /** Release memory for the text nodes in s_stn.
3528 */
3529 static
3530 void
relase_status_text_nodes(void)3531 relase_status_text_nodes(void)
3532 {
3533 status_text_t *ptr; /* Current node to release */
3534
3535 if (NULL != s_stn) {
3536 if (NULL != i_stn) {
3537 dk4sto_it_reset(i_stn);
3538 do {
3539 ptr = (status_text_t *)dk4sto_it_next(i_stn);
3540 if (NULL != ptr) {
3541 stt_delete(ptr);
3542 }
3543 } while(NULL != ptr);
3544 dk4sto_it_close(i_stn); i_stn = NULL;
3545 }
3546 dk4sto_close(s_stn); s_stn = NULL;
3547 }
3548
3549 }
3550
3551
3552
3553 /** Attempt to open an SNMP session.
3554 @return 1 on success, 0 on error.
3555 */
3556 static
3557 int
attempt_snmp_session(void)3558 attempt_snmp_session(void)
3559 {
3560 struct snmp_session sesstemp; /* Template for the session */
3561 int back = 0;
3562
3563 init_snmp(pjsnmp_kw[4]);
3564 DK4_MEMRES(&sesstemp, sizeof(sesstemp));
3565 snmp_sess_init(&sesstemp);
3566 sesstemp.version = snmp_version;
3567 sesstemp.peername = host_name;
3568 sesstemp.community = (unsigned char *)snmp_comm;
3569 if (NULL == snmp_comm) {
3570 sesstemp.community = (unsigned char *)(pjsnmp_kw[5]);
3571 }
3572 sesstemp.community_len = strlen((char *)(sesstemp.community));
3573 snmp_sess = snmp_open(&sesstemp);
3574 if (NULL != snmp_sess) {
3575 back = 1;
3576 } else {
3577 /* ERROR: Failed to open SNMP session */
3578 log_1_no_header(121);
3579 if (0 != snmp_igfs) {
3580 back = 1;
3581 }
3582 }
3583 return back;
3584 }
3585
3586
3587
3588 /** Accounting data exchange via socket (either network socket
3589 or local UNIX domain socket) and close socket.
3590 @param sock Socket to use for data exchange.
3591 @param expect_response Flag: Response from accounting system expected.
3592 @return 1 on success, 0 on error.
3593 */
3594 static
3595 int
accounting_ex_socket(dk4_socket_t sock,int expect_response)3596 accounting_ex_socket(dk4_socket_t sock, int expect_response)
3597 {
3598 size_t sl; /* String length */
3599 size_t by; /* Bytes written or read */
3600 int res; /* Operation result */
3601 int cutted; /* String was cutted */
3602 int cc; /* Flag: Can continue */
3603 int back = 0;
3604
3605 cutted = 0;
3606 if (0 != can_continue(0)) {
3607 sl = strlen(pjsnmp_buf);
3608 by = sl;
3609 res = dk4socket_send(sock, pjsnmp_buf, &by, 0, 0L, 0L, NULL);
3610 if (DK4_SOCKET_RESULT_SUCCESS == res) {
3611 if (by == sl) {
3612 back = 1;
3613 } else {
3614 /* ERROR: Partial send */
3615 log_1_no_header(123);
3616 }
3617 } else {
3618 /* ERROR: Failed to send data */
3619 log_1_no_header(122);
3620 }
3621 pjsnmp_buf[0] = '\0';
3622 res = dk4socket_shutdown(sock, DK4_SOCKET_SHUT_WRITE, NULL);
3623 if (DK4_SOCKET_RESULT_SUCCESS == res) {
3624 cc = 1;
3625 do {
3626 if (0 != can_continue(0)) {
3627 by = sizeof(stat_text) - 1;
3628 res = dk4socket_recv(sock, stat_text, &by, 0, 0L, 0L, NULL);
3629 if (DK4_SOCKET_RESULT_SUCCESS == res) {
3630 if (0 < by) {
3631 stat_text[by] = '\0';
3632 res = dk4str8_cat_s(pjsnmp_buf,sizeof(pjsnmp_buf),stat_text,NULL);
3633 if (0 == res) {
3634 cutted = 1;
3635 back = 0;
3636 }
3637 } else {
3638 cc = 0;
3639 }
3640 } else {
3641 cc = -1;
3642 }
3643 } else {
3644 cc = -1;
3645 back = 0;
3646 }
3647 } while(1 == cc);
3648 } else {
3649 back = 0;
3650 /* ERROR: Socket shutdown failed */
3651 log_1_no_header(124);
3652 }
3653 if (0 != expect_response) {
3654 if (0 == strlen(pjsnmp_buf)) {
3655 back = 0;
3656 }
3657 if (0 != cutted) {
3658 back = 0;
3659 }
3660 }
3661 } else {
3662 pjsnmp_buf[0] = '\0';
3663 }
3664 dk4socket_close(sock, NULL);
3665
3666 return back;
3667 }
3668
3669
3670
3671 /** Accounting data exchange via local UNIX domain socket.
3672 @param expect_response Flag: Response from accounting system expected.
3673 @return 1 on success, 0 on error.
3674 */
3675 static
3676 int
accounting_ex_local_socket(int expect_response)3677 accounting_ex_local_socket(int expect_response)
3678 {
3679 dk4_socket_t sock; /* Socket to use for accounting */
3680 int back = 0;
3681
3682 sock = dk4socket_c8_unix_client(acct_name, NULL);
3683 if (INVALID_SOCKET != sock) {
3684 back = accounting_ex_socket(sock, expect_response);
3685 /* Socket not closed here, closed in accounting_ex_socket() */
3686 } else {
3687 /* ERROR: Failed to open UNIX domain socket */
3688 log_1_no_header(125);
3689 pjsnmp_buf[0] = '\0';
3690 }
3691
3692 return back;
3693 }
3694
3695
3696
3697 /** Accounting data exchange via network socket.
3698 @param expect_response Flag: Response from accounting system expected.
3699 @return 1 on success, 0 on error.
3700 */
3701 static
3702 int
accounting_ex_network(int expect_response)3703 accounting_ex_network(int expect_response)
3704 {
3705 dk4_socket_t sock; /* Socket to use for accounting */
3706 int back = 0;
3707
3708 sock = dk4socket_c8_tcp_client_host_port(acct_host,acct_port,0,0L,0L,NULL);
3709 if (INVALID_SOCKET != sock) {
3710 back = accounting_ex_socket(sock, expect_response);
3711 /* Socket not closed here, closed in accounting_ex_socket() */
3712 } else {
3713 /* ERROR: Failed to open network connection */
3714 log_1_no_header(125);
3715 pjsnmp_buf[0] = '\0';
3716 }
3717
3718 return back;
3719 }
3720
3721
3722
3723 /** Accounting data exchange to file.
3724 @param fipo Accounting file.
3725 @param expect_response Flag: Response from accounting system expected.
3726 @return 1 on success, 0 on error.
3727 */
3728 static
3729 int
accounting_ex_file(FILE * fipo,int expect_response)3730 accounting_ex_file(FILE *fipo, int expect_response)
3731 {
3732 int back = 0;
3733
3734 if (EOF != fputs(pjsnmp_buf, fipo)) {
3735 if (EOF != fflush(fipo)) {
3736 back = 1;
3737 }
3738 #if TRACE_DEBUG
3739 else {
3740 }
3741 #endif
3742 }
3743 #if TRACE_DEBUG
3744 else {
3745 }
3746 #endif
3747 pjsnmp_buf[0] = '\0';
3748 if (0 != expect_response) {
3749 back = 0;
3750 }
3751 return back;
3752 }
3753
3754
3755
3756 /** Accounting data exchange via pipe.
3757 @param expect_response Flag: Response from accounting system expected.
3758 @return 1 on success, 0 on error.
3759 */
3760 static
3761 int
accounting_ex_pipe(int expect_response)3762 accounting_ex_pipe(int expect_response)
3763 {
3764 FILE *fipo = NULL; /* Connection to pipe */
3765 char *prgn = NULL; /* Program name */
3766 int back = 0;
3767 int pcres = 0; /* Result from pclose */
3768
3769 prgn = dk4str8_start(&(acct_name[1]), NULL);
3770 if (NULL != prgn) {
3771 fipo = popen(prgn, "w");
3772 if (NULL != fipo) {
3773 back = accounting_ex_file(fipo, 0);
3774 pcres = pclose(fipo);
3775 pjsnmp_buf[0] = '\0';
3776 if (0 != expect_response) {
3777 switch (pcres) {
3778 case LPRNG_EXIT_REMOVE : {
3779 decision = 3;
3780 } break;
3781 case LPRNG_EXIT_HOLD : {
3782 decision = 2;
3783 } break;
3784 default : {
3785 decision = 1;
3786 } break;
3787 }
3788 log_3_no_header(166, 167, accounting_responses[decision - 1]);
3789 }
3790 } else {
3791 /* ERROR: Failed to start program for pipe */
3792 log_1_no_header(126);
3793 pjsnmp_buf[0] = '\0';
3794 }
3795 } else {
3796 /* ERROR: Not command name found */
3797 log_1_with_header(2, 127);
3798 pjsnmp_buf[0] = '\0';
3799 }
3800 return back;
3801 }
3802
3803
3804
3805 /** Accounting data exchange via file specified by name.
3806 @param expect_response Flag: Response from accounting system expected.
3807 @param crf Flag: Create new file.
3808 @return 1 on success, 0 on error.
3809 */
3810 static
3811 int
accounting_ex_filename(int expect_response,int crf)3812 accounting_ex_filename(int expect_response, int crf)
3813 {
3814 dk4_er_t er; /* Error report */
3815 FILE *fipo = NULL; /* File to write to */
3816 int back = 1;
3817 int tests = DK4_FOPEN_SC_USER; /* Security tests for fopen */
3818
3819 if (0 != dk4isadmin()) {
3820 tests = DK4_FOPEN_SC_PRIVILEGED;
3821 }
3822 dk4error_init(&er);
3823 fipo = dk4fopen_c8(acct_name, ((0 != crf) ? "w" : "a"), tests, &er);
3824 if (NULL != fipo) {
3825 back = accounting_ex_file(fipo, expect_response);
3826 if(EOF == fclose(fipo)) {
3827 back = 0;
3828 }
3829 } else {
3830 /* ERROR: Failed to open accounting file name */
3831 log_1_no_header(128);
3832 pjsnmp_buf[0] = '\0';
3833 }
3834 return back;
3835 }
3836
3837
3838
3839 #if 0
3840 /* 2016-02-10:
3841 Although chapter 15.5 of the LPRng reference manual states that the
3842 :af file is opened (file, pipe or network socket) and passed as
3843 file descriptor 3 to all filters, this did not work in my tests.
3844 Text written to fd 3 did not arrive at the accounting system.
3845 */
3846
3847 /** Accounting data exchange via file descriptor 3.
3848 @param expect_response Flag: Response from accounting system expected.
3849 @return 1 on success, 0 on error.
3850 */
3851 static
3852 int
3853 accounting_ex_fd3(int expect_response)
3854 {
3855 size_t sl; /* String length */
3856 ssize_t bwr; /* Number of bytes written or read */
3857 int back = 0;
3858
3859 sl = strlen(pjsnmp_buf);
3860 bwr = write(3, pjsnmp_buf, sl);
3861 pjsnmp_buf[0] = '\0';
3862 if (0 < bwr) {
3863 if (sl == (size_t)bwr) {
3864 back = 1;
3865 } else {
3866 /* ERROR: Incomplete write operation */
3867 log_1_no_header(122);
3868 }
3869 if (0 != expect_response) {
3870 bwr = read(3, pjsnmp_buf, sizeof(pjsnmp_buf));
3871 if (0 < bwr) {
3872 pjsnmp_buf[bwr] = '\0';
3873 } else {
3874 pjsnmp_buf[0] = '\0';
3875 back = 0;
3876 }
3877 }
3878 } else {
3879 /* ERROR: Write failed */
3880 log_1_no_header(122);
3881 }
3882 return back;
3883 }
3884
3885 #endif
3886
3887
3888
3889
3890
3891 /** Data exchange with accounting system.
3892 The contents of pjsnmp_buf is sent to the accounting system,
3893 the response - if any - is stored in pjsnmp_buf.
3894 @param expect_response Flag: Accounting system should send response.
3895 @return 1 on successfull exchange, 0 on connection error.
3896 */
3897 static
3898 int
accounting_exchange(int expect_response)3899 accounting_exchange(int expect_response)
3900 {
3901 dk4_stat_t stb; /* File status buffer */
3902 int issock = 0; /* Flag: File is socket */
3903 int back = 0; /* Function result */
3904 int crf = 0; /* Flag: Create log file */
3905
3906 if ((NULL != acct_host) && (0 < acct_port)) {
3907 log_1_no_header(154);
3908 back = accounting_ex_network(expect_response);
3909 log_1_no_header((0 != back) ? 159 : 160);
3910 } else {
3911 if (NULL != acct_name) {
3912 if ('|' == *acct_name) {
3913 #if 0
3914 if (0 != expect_response) {
3915 /* ERROR: Can not receive response from pipe */
3916 log_1_with_header(2, 129);
3917 }
3918 #endif
3919 log_1_no_header(155);
3920 back = accounting_ex_pipe(expect_response);
3921 log_1_no_header((0 != back) ? 159 : 160);
3922 } else {
3923 if (0 != dk4stat_c8(&stb, acct_name, NULL)) {
3924 if (0 != dk4stat_is_unix_domain_socket(&stb, NULL)) {
3925 issock = 1;
3926 }
3927 } else {
3928 crf = 1;
3929 }
3930 if (0 != issock) {
3931 log_1_no_header(156);
3932 back = accounting_ex_local_socket(expect_response);
3933 log_1_no_header((0 != back) ? 159 : 160);
3934 } else {
3935 if (0 != expect_response) {
3936 /* ERROR: Can not receive response from file */
3937 log_1_with_header(2, 130);
3938 }
3939 log_1_no_header(157);
3940 back = accounting_ex_filename(expect_response, crf);
3941 log_1_no_header((0 != back) ? 159 : 160);
3942 }
3943 }
3944 } else {
3945 #if 0
3946 log_1_no_header(158);
3947 back = accounting_ex_fd3(expect_response);
3948 log_1_no_header((0 != back) ? 159 : 160);
3949 #else
3950 log_1_with_header(2, 165);
3951 #endif
3952 }
3953 }
3954 return back;
3955 }
3956
3957
3958
3959 /** Check users permission to print on queue.
3960 @return 1 if user is allowed to print, 0 otherwise.
3961 */
3962 static
3963 int
check_permission(void)3964 check_permission(void)
3965 {
3966 int back = 1;
3967 int res = 0; /* Result from check */
3968 const size_t bsz = sizeof(pjsnmp_buf); /* Buffer size */
3969
3970 if ((0 != acct_enab) && (0 != acct_achk)) {
3971 /* LOG: Checking print permission */
3972 log_5_no_header(131, 132, 133, user_name, queue_name);
3973 if(0 != dk4str8_cpy_s(pjsnmp_buf, bsz, pjsnmp_kw[6], NULL)) {
3974 if(0 != dk4str8_cat_s(pjsnmp_buf, bsz, pjsnmp_kw[1], NULL)) {
3975 if(0 != dk4str8_cat_s(pjsnmp_buf, bsz, queue_name, NULL)) {
3976 if(0 != dk4str8_cat_s(pjsnmp_buf, bsz, pjsnmp_kw[1], NULL)) {
3977 if(0 != dk4str8_cat_s(pjsnmp_buf, bsz, user_name, NULL)) {
3978 if(0 != dk4str8_cat_s(pjsnmp_buf, bsz, pjsnmp_kw[0], NULL)) {
3979 back = 0;
3980 if (0 != accounting_exchange(1)) {
3981 dk4str8_normalize(pjsnmp_buf, NULL);
3982 if (0 < strlen(pjsnmp_buf)) {
3983 if (0 != decision) {
3984 res = decision - 1;
3985 } else {
3986 res = dk4str8_array_index(accounting_responses, pjsnmp_buf, 0);
3987 if (-1 < res) { decision = res + 1; }
3988 }
3989 switch (res) {
3990 case 0: {
3991 back = 1;
3992 } break;
3993 case 1: {
3994 exval = LPRNG_EXIT_HOLD;
3995 } break;
3996 }
3997 log_3_no_header(166, 167, pjsnmp_buf);
3998 } else {
3999 #if 0
4000 /* 2016-02-15 An empty response indicates permission
4001 to print.
4002 */
4003 if (0 != acct_apcf) { back = 1; }
4004 #endif
4005 back = 1;
4006 }
4007 } else {
4008 if (0 != acct_apcf) { back = 1; }
4009 else { decision = 3; }
4010 }
4011 }
4012 }
4013 }
4014 }
4015 }
4016 }
4017 /* LOG: Test result */
4018 log_1_no_header((0 != back) ? 134 : 135);
4019 }
4020 return back;
4021 }
4022
4023
4024
4025 /** Retrieve integer value from variable list.
4026 @param iptr Address of variable to set.
4027 @param var SNMP variable containing the value.
4028 @return 1 on success, 0 on error.
4029 */
4030 static
4031 int
get_int(int * iptr,struct variable_list * var)4032 get_int(int *iptr, struct variable_list *var)
4033 {
4034 char bu[64]; /* Buffer for numeric values as text */
4035 const char *endptr = NULL; /* First unprocessable character */
4036 long l; /* long value */
4037 int i; /* int value */
4038 int back = 0;
4039
4040 switch (var->type) {
4041 case ASN_OCTET_STR : {
4042 if ((var->val_len > 0) && (var->val_len < sizeof(bu))) {
4043 dk4mem_cpy(bu, (void *)((var->val).string), var->val_len, NULL);
4044 bu[var->val_len] = '\0';
4045 if (0 != dk4ma_input_c8_dec_int(&i, bu, &endptr, 1, NULL)) {
4046 *iptr = i;
4047 back = 1;
4048 }
4049 }
4050 } break;
4051 case ASN_TIMETICKS :
4052 case ASN_GAUGE :
4053 case ASN_COUNTER :
4054 case ASN_INTEGER : {
4055 l = *((var->val).integer);
4056 *iptr = (int)l;
4057 back = 1;
4058 } break;
4059 }
4060
4061 return back;
4062 }
4063
4064
4065
4066 /** Retrieve unsigned long value from variable list.
4067 @param ulptr Address of variable to set.
4068 @param var SNMP variable containing the value.
4069 @return 1 on success, 0 on error.
4070 */
4071 static
4072 int
get_ul(unsigned long * ulptr,struct variable_list * var)4073 get_ul(unsigned long *ulptr, struct variable_list *var)
4074 {
4075 unsigned long ul = 0UL; /* Result variable */
4076 long l; /* long value */
4077 int back = 0;
4078
4079 switch (var->type) {
4080 case ASN_OCTET_STR : {
4081 if (0 < var->val_len) {
4082 ul |=
4083 ((((unsigned long)(((var->val).string)[0])) << 24) & 0xFF000000UL);
4084 back = 1;
4085 }
4086 if (1 < var->val_len) {
4087 ul |=
4088 ((((unsigned long)(((var->val).string)[1])) << 16) & 0x00FF0000UL);
4089 }
4090 if (2 < var->val_len) {
4091 ul |=
4092 ((((unsigned long)(((var->val).string)[2])) << 8) & 0x0000FF00UL);
4093 }
4094 if (3 < var->val_len) {
4095 ul |=
4096 ((((unsigned long)(((var->val).string)[3])) ) & 0x000000FFUL);
4097 }
4098 if (0 != back) {
4099 *ulptr = ul;
4100 }
4101 } break;
4102 case ASN_TIMETICKS :
4103 case ASN_GAUGE :
4104 case ASN_COUNTER :
4105 case ASN_INTEGER : {
4106 l = *((var->val).integer);
4107 if (0L <= l) {
4108 *ulptr = (unsigned long)l;
4109 back = 1;
4110 }
4111 } break;
4112 }
4113
4114 return back;
4115 }
4116
4117
4118
4119 /** Check whether a buffer contains non-ASCII characters.
4120 @param ptr Buffer address.
4121 @param i Buffer size.
4122 @return 1 if non-ASCII found, 0 otherwise.
4123 */
4124 static
4125 int
contains_non_ascii(const char * ptr,size_t i)4126 contains_non_ascii(const char *ptr, size_t i)
4127 {
4128 int back = 0;
4129
4130 while ((0 == back) && (0 < i)) {
4131 if (0x7F < (unsigned char)(*(ptr++))) {
4132 back = 1;
4133 }
4134 i--;
4135 }
4136 return back;
4137 }
4138
4139
4140
4141 /** Replace all non-ascii characters by question marks.
4142 */
4143 static
4144 void
no_non_ascii(char * str)4145 no_non_ascii(char *str)
4146 {
4147 unsigned char uc;
4148 while ('\0' != *str) {
4149 uc = (unsigned char)(*str);
4150 if ((0x20 > uc) || (0x7E < uc)) {
4151 *str = '?';
4152 }
4153 str++;
4154 }
4155 }
4156
4157
4158
4159
4160 /** Check two OIDs for equality.
4161 @param pl Left OID.
4162 @param szl Left OID size.
4163 @param pr Right OID.
4164 @param szr Right OID size.
4165 @return 1 for equal OIDs, 0 otherwise.
4166 */
4167 static
4168 int
oids_equal(const oid * pl,size_t szl,const oid * pr,size_t szr)4169 oids_equal(const oid *pl, size_t szl, const oid *pr, size_t szr)
4170 {
4171 #if VERSION_BEFORE_20160206
4172 size_t i; /* Index of current sub-id to compare */
4173 #endif
4174 int back = 0;
4175 if ((szl == szr) && (0 < szl)) {
4176 #if VERSION_BEFORE_20160206
4177 back = 1;
4178 for (i = 0; ((i < szl) && (1 == back)); i++) {
4179 if (pl[i] != pr[i]) {
4180 back = 0;
4181 }
4182 }
4183 #else
4184 /* No check for overflow necessary, max 4 * 128. */
4185 if (0 == dk4mem_cmp(pl, pr, (szl * sizeof(oid)), NULL)) {
4186 back = 1;
4187 }
4188 #endif
4189 }
4190 return back;
4191 }
4192
4193
4194 /** Find OID index.
4195 @param oid Pointer to OID to check.
4196 @param sz OID size.
4197 @return 0 for hrDeviceStatus, 1 for hrPrinterStatus,
4198 2 for page counter, 3 for hrPrinterDetectedErrorState,
4199 4 for status text,
4200 -1 otherwise.
4201 */
4202 static
4203 int
oid_index(oid * p,size_t sz)4204 oid_index(oid *p, size_t sz)
4205 {
4206 int back = -1;
4207
4208 if (0 != oids_equal(p, sz, oid_ds, sz_oid_ds)) {
4209 back = 0;
4210 } else {
4211 if (0 != oids_equal(p, sz, oid_ps, sz_oid_ps)) {
4212 back = 1;
4213 } else {
4214 if (0 != oids_equal(p, sz, oid_pc, sz_oid_pc)) {
4215 back = 2;
4216 } else {
4217 if (0 != oids_equal(p, sz, oid_pe, sz_oid_pe)) {
4218 back = 3;
4219 } else {
4220 if (0 < sz_oid_st) {
4221 if (0 != oids_equal(p, sz, oid_st, sz_oid_st)) {
4222 back = 4;
4223 }
4224 }
4225 #if TRACE_DEBUG
4226 else {
4227 }
4228 #endif
4229 }
4230 }
4231 }
4232 }
4233 return back;
4234 }
4235
4236
4237
4238 /** Show one active condition from hrPrinterDetectedErrorState.
4239 @param cond Condition bit (checked against the found conditions).
4240 @param cind Index of corresponding text in pjsnmp_kw.
4241 */
4242 static
4243 void
show_pdes(unsigned long cond,size_t cind)4244 show_pdes(unsigned long cond, size_t cind)
4245 {
4246 if (0UL != (pdes_cond & cond)) {
4247 fputs(pjsnmp_kw[((0UL != (pdes_error & cond)) ? 32 : 33)], out_file);
4248 fputs(pjsnmp_kw[cind], out_file);
4249 }
4250 }
4251
4252
4253
4254 static
4255 void
show_text_containing_non_ascii(const unsigned char * ptr,size_t num)4256 show_text_containing_non_ascii(const unsigned char *ptr, size_t num)
4257 {
4258 while(0 < num--) {
4259 if (0x7F < *ptr) {
4260 fputc('?', out_file);
4261 } else {
4262 fputc(*ptr, out_file);
4263 }
4264 ptr++;
4265 }
4266 }
4267
4268
4269
4270 /** Report status if changed.
4271 @param found Bitmask for found information.
4272 */
4273 static
4274 void
report_status_if_changed(void)4275 report_status_if_changed(void)
4276 {
4277 unsigned long tcond = 0x80000000UL; /* Bit for one condition */
4278 int must_report = 0; /* Flag: Must report change */
4279 int i;
4280
4281 /* Check whether or not to report.
4282 */
4283 if (stsum != ostsum) {
4284 must_report = 1;
4285 } else {
4286 if (devst != odevst) {
4287 must_report = 1;
4288 } else {
4289 if (prist != oprist) {
4290 must_report = 1;
4291 } else {
4292 if (pdes_cond != opdes_cond) {
4293 must_report = 1;
4294 } else {
4295 if (0 < sz_stat_text) {
4296 if (0 != force_printable_status_text) {
4297 if (sz_ostat_text != sz_stat_text) {
4298 must_report = 1;
4299 } else {
4300 if (0 != dk4mem_cmp(stat_text,ostat_text,sz_stat_text,NULL)) {
4301 must_report = 1;
4302 }
4303 }
4304 } else {
4305 if (0 == contains_non_ascii(stat_text, sz_stat_text)) {
4306 if (sz_stat_text != sz_ostat_text) {
4307 must_report = 1;
4308 } else {
4309 if (0 != dk4mem_cmp(stat_text,ostat_text,sz_stat_text,NULL)) {
4310 must_report = 1;
4311 }
4312 }
4313 }
4314 }
4315 }
4316 }
4317 }
4318 }
4319 }
4320
4321 /* Report if necessary.
4322 */
4323 if (0 != must_report) {
4324 fputs(pjsnmp_kw[150], out_file);
4325 (void)log_timestamp();
4326 if ((-1 < devst) && (6 > devst)) {
4327 if ((-1 < prist) && (6 > prist)) {
4328 fputs(pjsnmp_kw[7], out_file);
4329 fputs(pjsnmp_kw[11 + devst], out_file);
4330 fputs(pjsnmp_kw[8], out_file);
4331 fputs(pjsnmp_kw[17 + prist], out_file);
4332 fputs(pjsnmp_kw[0], out_file);
4333 }
4334 }
4335 fputs(pjsnmp_kw[9], out_file);
4336 for (i = 0; i < 16; i++) {
4337 if ((0 < i) && (0 == (i % 4))) { fputc('-', out_file); }
4338 fputc(((0UL != (pdes_cond & tcond)) ? '1' : '0'), out_file);
4339 tcond = tcond / 2UL;
4340 }
4341 fputs(pjsnmp_kw[0], out_file);
4342 show_pdes(PDES_ERROR_DOOR_OPEN, 38);
4343 show_pdes(PDES_ERROR_OFFLINE, 40);
4344 show_pdes(PDES_ERROR_MARKER_SUPPLY_MISSING, 44);
4345 show_pdes(PDES_ERROR_NO_TONER, 37);
4346 show_pdes(PDES_WARNING_TONER_LOW, 36);
4347 show_pdes(PDES_ERROR_INPUT_TRAY_MISSING, 42);
4348 show_pdes(PDES_WARNING_INPUT_TRAY_EMPTY, 47);
4349 show_pdes(PDES_ERROR_NO_PAPER, 35);
4350 show_pdes(PDES_WARNING_PAPER_LOW, 34);
4351 show_pdes(PDES_ERROR_OUTPUT_TRAY_MISSING, 43);
4352 show_pdes(PDES_ERROR_PAPER_JAM, 39);
4353 show_pdes(PDES_ERROR_OUTPUT_FULL, 46);
4354 show_pdes(PDES_WARNING_OUTPUT_NEAR_FULL, 45);
4355 show_pdes(PDES_WARNING_SERVICE, 41);
4356 show_pdes(PDES_WARNING_OVERDUE_PREVENT_MAINT, 48);
4357 if (0 < sz_stat_text) {
4358 if (0 != force_printable_status_text) {
4359 fputs(pjsnmp_kw[30], out_file);
4360 show_text_containing_non_ascii((unsigned char *)stat_text,sz_stat_text);
4361 fputs(pjsnmp_kw[31], out_file);
4362 } else {
4363 if (0 == contains_non_ascii(stat_text, sz_stat_text)) {
4364 fputs(pjsnmp_kw[30], out_file);
4365 (void)fwrite(stat_text, 1, sz_stat_text, out_file);
4366 fputs(pjsnmp_kw[31], out_file);
4367 }
4368 }
4369 }
4370 if ((-1 < stsum) && (7 > stsum)) {
4371 fputs(pjsnmp_kw[10], out_file);
4372 fputs(pjsnmp_kw[23 + stsum], out_file);
4373 fputs(pjsnmp_kw[0], out_file);
4374 }
4375 if (PJSNMP_ST_ERROR == stsum) {
4376 fputs(pjsnmp_kw[49], out_file);
4377 } else {
4378 if (0UL != (pdes_cond & PDES_WARNING_SERVICE)) {
4379 if (0UL == (pdes_error & PDES_WARNING_SERVICE)) {
4380 fputs(pjsnmp_kw[50], out_file);
4381 }
4382 }
4383 }
4384 fflush(out_file);
4385 }
4386
4387 }
4388
4389
4390
4391 /** One SNMP data exchange.
4392 @param passno 0 before start, 1 during transfer, 2 at end.
4393 @return 1 on success, 0 on error (stop printing).
4394 */
4395 static
4396 int
one_snmp_request(int passno)4397 one_snmp_request(int passno)
4398 {
4399 status_text_t tx; /* Used in comparison */
4400 status_text_t *ptx = NULL; /* Text node found */
4401 struct snmp_pdu *rq = NULL; /* Request */
4402 struct snmp_pdu *rs = NULL; /* Response */
4403 struct variable_list *vars = NULL; /* Response variables */
4404 int status = 0; /* SNMP result */
4405 int found = 0; /* Information from response */
4406 int back = 0;
4407
4408 if (0 != can_continue(0)) {
4409 if (NULL != snmp_sess) {
4410 pdes_cond = 0UL;
4411 pcnt = 0UL;
4412 devst = 0;
4413 prist = 0;
4414 stsum = PJSNMP_ST_UNREACHABLE;
4415 stat_text[0] = '\0';
4416 sz_stat_text = 0;
4417 rq = snmp_pdu_create(SNMP_MSG_GET);
4418 if (NULL != rq) {
4419 back = 1;
4420 snmp_add_null_var(rq, oid_ds, sz_oid_ds);
4421 snmp_add_null_var(rq, oid_ps, sz_oid_ps);
4422 snmp_add_null_var(rq, oid_pc, sz_oid_pc);
4423 snmp_add_null_var(rq, oid_pe, sz_oid_pe);
4424 if (0 < sz_oid_st) {
4425 snmp_add_null_var(rq, oid_st, sz_oid_st);
4426 }
4427 status = snmp_synch_response(snmp_sess, rq, &rs);
4428 if (STAT_SUCCESS == status) {
4429 if (NULL != rs) {
4430 if (SNMP_ERR_NOERROR == rs->errstat) {
4431
4432 /* Obtain response elements.
4433 */
4434 stsum = PJSNMP_ST_UNKNOWN;
4435 vars = rs->variables;
4436 while (NULL != vars) {
4437 switch (oid_index(vars->name, vars->name_length)) {
4438 case 0: {
4439 if (0 != get_int(&devst, vars)) {
4440 found |= 1;
4441 }
4442 } break;
4443 case 1: {
4444 if (0 != get_int(&prist, vars)) {
4445 found |= 2;
4446 }
4447 } break;
4448 case 2: {
4449 if (0 != get_ul(&pcnt, vars)) {
4450 found |= 4;
4451 switch (passno) {
4452 case 0: {
4453 pc_start = pcnt;
4454 have_pcs = 1;
4455 } break;
4456 case 1: {
4457 /* When aborting due to timeout in
4458 error or unreachable state we use the
4459 last page counter seen.
4460 */
4461 pc_end = pcnt;
4462 have_pce = 1;
4463 } break;
4464 case 2: {
4465 pc_end = pcnt;
4466 have_pce = 1;
4467 } break;
4468 }
4469 }
4470 } break;
4471 case 3: {
4472 if (0 != get_ul(&pdes_cond, vars)) { found |= 8; }
4473 } break;
4474 case 4: {
4475 if (0 < vars->val_len) {
4476 if (sizeof(stat_text) > vars->val_len) {
4477 dk4mem_cpy(
4478 stat_text, (vars->val).string,
4479 vars->val_len, NULL
4480 );
4481 sz_stat_text = vars->val_len;
4482 } else {
4483 dk4mem_cpy(
4484 stat_text, (vars->val).string,
4485 sizeof(stat_text), NULL
4486 );
4487 sz_stat_text = sizeof(stat_text);
4488 }
4489 found |= 16;
4490 }
4491 } break;
4492 }
4493 vars = vars->next_variable;
4494 }
4495
4496 /* Build summary (1): device and printer status.
4497 */
4498 if (3 == (3 & found)) {
4499 stsum = get_state_mapping(devst, prist);
4500
4501 }
4502
4503 /* Build summary (2): printer detected error state
4504 */
4505 if (8 == (8 & found)) {
4506 if ((0 != pdes_always) || (PJSNMP_ST_UNKNOWN == stsum)) {
4507 if (0L != (pdes_error & pdes_cond)) {
4508 stsum = PJSNMP_ST_ERROR;
4509 }
4510 }
4511 }
4512
4513 /* Build summary (3): status text line
4514 */
4515 if (16 == (16 & found)) {
4516 if ((0 != stat_always) || (PJSNMP_ST_UNKNOWN == stsum)) {
4517 if ((NULL != s_stn) && (NULL != i_stn)) {
4518 DK4_MEMRES(&tx, sizeof(tx));
4519 tx.text = (unsigned char *)stat_text;
4520 tx.size = sz_stat_text;
4521 tx.state = 0;
4522 tx.start = 0;
4523 ptx = (status_text_t *)dk4sto_it_find_like(i_stn, &tx, 1);
4524 if (NULL != ptx) {
4525 stsum = ptx->state;
4526
4527 }
4528 }
4529 }
4530 }
4531
4532 /* Build summary (4): Map unknown to busy if configured
4533 */
4534 if ((0 != unkn_busy) && (PJSNMP_ST_UNKNOWN == stsum)) {
4535 stsum = PJSNMP_ST_BUSY;
4536 }
4537
4538 /* For error state mark this page counter as print
4539 job not yet finished.
4540 */
4541 switch (stsum) {
4542 case PJSNMP_ST_ERROR: {
4543 seen_printing = 0;
4544 if ((0 < passno) && (0UL < pcnt)) {
4545 pc_error = pcnt;
4546 }
4547 start_unreach = (dk4_time_t)0UL;
4548 if ((dk4_time_t)0UL == start_error) {
4549 dk4time_get(&start_error);
4550 }
4551 } break;
4552 case PJSNMP_ST_BUSY: {
4553 seen_printing = 1;
4554 start_unreach = (dk4_time_t)0UL;
4555 start_error = (dk4_time_t)0UL;
4556 } break;
4557 case PJSNMP_ST_UNREACHABLE : {
4558 if ((dk4_time_t)0UL == start_unreach) {
4559 dk4time_get(&start_unreach);
4560 }
4561 start_error = (dk4_time_t)0UL;
4562 } break;
4563 default : {
4564 start_unreach = (dk4_time_t)0UL;
4565 start_error = (dk4_time_t)0UL;
4566 } break;
4567 }
4568
4569 /* On change, print new state.
4570 */
4571 report_status_if_changed();
4572
4573 /* Save data for next comparison.
4574 */
4575 odevst = devst;
4576 oprist = prist;
4577 ostsum = stsum;
4578 opdes_cond = pdes_cond;
4579 if (0 < sz_stat_text) {
4580 DK4_MEMCPY(ostat_text, stat_text, sz_stat_text);
4581 }
4582 sz_ostat_text = sz_stat_text;
4583 }
4584 #if TRACE_DEBUG
4585 else {
4586 }
4587 #endif
4588 }
4589 #if TRACE_DEBUG
4590 else {
4591 }
4592 #endif
4593 }
4594 #if TRACE_DEBUG
4595 else {
4596 }
4597 #endif
4598 if (NULL != rs) {
4599 snmp_free_pdu(rs);
4600 }
4601 }
4602 #if TRACE_DEBUG
4603 else {
4604 }
4605 #endif
4606 }
4607 #if TRACE_DEBUG
4608 else {
4609 }
4610 #endif
4611 }
4612 #if TRACE_DEBUG
4613 else {
4614 }
4615 #endif
4616
4617 return back;
4618 }
4619
4620
4621
4622 /** Create accounting text line and do data exchange
4623 with accounting system.
4624 @param kwind Index of start keyword in pjsnmp_kw array.
4625 @param pc Page counter value to transmit.
4626 */
4627 static
4628 void
accounting_index_and_value(size_t kwind,unsigned long pc)4629 accounting_index_and_value(size_t kwind, unsigned long pc)
4630 {
4631 char buf[64]; /* Buffer for number as text */
4632 const char *jt; /* Job title */
4633 size_t szbuf = sizeof(pjsnmp_buf); /* Buffer size */
4634 size_t sl;
4635 int res; /* Conversion result */
4636
4637 res = dk4ma_write_c8_decimal_unsigned(
4638 buf, sizeof(buf), (dk4_um_t)pc, 0, NULL
4639 );
4640 if (0 != res) {
4641 if (0 != dk4str8_cpy_s(pjsnmp_buf, szbuf, pjsnmp_kw[kwind], NULL)) {
4642 if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, pjsnmp_kw[1], NULL)) {
4643 if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, queue_name, NULL)) {
4644 if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, pjsnmp_kw[1], NULL)) {
4645 if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, user_name, NULL)) {
4646 if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, pjsnmp_kw[1], NULL)) {
4647 if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, buf, NULL)) {
4648 if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, pjsnmp_kw[1], NULL)) {
4649 if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, job_name, NULL)) {
4650 if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, pjsnmp_kw[1], NULL)) {
4651 jt = job_title;
4652 if (NULL == jt) { jt = pjsnmp_kw[54]; }
4653 sl = strlen(pjsnmp_buf);
4654 if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, jt, NULL)) {
4655 if (0 == allow_non_ascii) { no_non_ascii(&(pjsnmp_buf[sl])); }
4656 if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, pjsnmp_kw[0], NULL)) {
4657
4658 (void)accounting_exchange(0);
4659 }
4660 }
4661 }
4662 }
4663 }
4664 }
4665 }
4666 }
4667 }
4668 }
4669 }
4670 }
4671 }
4672 }
4673
4674
4675 /** Report used pages to accounting system.
4676 */
4677 static
4678 void
report_used_pages(void)4679 report_used_pages(void)
4680 {
4681
4682 if (0 != acct_start_end) {
4683 accounting_index_and_value(51, pc_start);
4684 accounting_index_and_value(52, pc_end);
4685 } else {
4686 accounting_index_and_value(53, (pc_end - pc_start));
4687 }
4688
4689 }
4690
4691
4692
4693 /** Do accounting before and after print job.
4694 @param passno 0=before print job, 1=after print job.
4695 @return 1 on success, 0 on error.
4696 */
4697 static
4698 int
do_accounting(int passno)4699 do_accounting(int passno)
4700 {
4701 static
4702 char buf[8*sizeof(dk4_um_t)]; /* Number as text */
4703 struct timeval to; /* Timeout for select */
4704 dk4_time_t start_idle = (dk4_time_t)0UL; /* Start idle/standby */
4705 dk4_time_t current_time = (dk4_time_t)0UL; /* Current time */
4706 dk4_time_t timeout_time = (dk4_time_t)0UL; /* Timeout reached */
4707 int res = 0; /* Conversion result */
4708 int back = 0;
4709 int cc = 1; /* Flag: Can continue */
4710 int uncoi = 0; /* Flag: Unconditional idle reported */
4711 int cto = 0; /* Flag: Must check timeout */
4712
4713 if (NULL != snmp_sess) {
4714 /* In the loop retrieve printer status via SNMP,
4715 finish if printer is idle or in standby state.
4716 */
4717 log_1_no_header((size_t)(145+passno));
4718 do {
4719 if (0 != can_continue(0)) {
4720 if (0 != one_snmp_request((0 == passno) ? 0 : 2)) {
4721
4722
4723 /* In idle or standby we can retrieve the page counter
4724 if the page counter was changed or the printer was seen
4725 printing.
4726 */
4727 switch (stsum) {
4728 case PJSNMP_ST_IDLE : case PJSNMP_ST_STANDBY : {
4729 back = 1; cc = 0;
4730 cto = 0;
4731 if (1 == passno) {
4732 if ((0 == seen_printing) && (0 < print_timeout)) {
4733 cto = 1;
4734 } else {
4735 if ((0UL != pc_error) && (1 == have_pce)) {
4736 if ((pc_end == pc_error)&&((dk4_um_t)0UL < print_timeout)) {
4737 cto = 1;
4738 }
4739 }
4740 }
4741 if (0 != cto) {
4742 dk4time_get(¤t_time);
4743 if ((dk4_time_t)0UL < start_idle) {
4744 if ((start_idle+(dk4_time_t)print_timeout) > current_time) {
4745 back = 0; cc = 1;
4746 }
4747 } else {
4748 start_idle = current_time;
4749 back = 0; cc = 1;
4750 if (0 == uncoi) {
4751 uncoi = 1;
4752 res = dk4ma_write_c8_decimal_unsigned(
4753 buf, sizeof(buf), print_timeout, 0, NULL
4754 );
4755 log_timestamp();
4756 fputs(pjsnmp_kw[162], out_file);
4757 if (0 != res) {
4758 fputs(buf, out_file);
4759 fputs(pjsnmp_kw[163], out_file);
4760 }
4761 fputs(pjsnmp_kw[164], out_file);
4762 fflush(out_file);
4763 }
4764 }
4765 }
4766 }
4767 } break;
4768 case PJSNMP_ST_UNREACHABLE : {
4769 start_idle = (dk4_time_t)0UL; /* not idle */
4770 uncoi = 0;
4771 /* If printer is unreachable we abort after timeout.
4772 */
4773 if (((0 == passno) && ((dk4_um_t)0UL != unto_start))
4774 || ((1 == passno) && ((dk4_um_t)0UL != unto_end))
4775 )
4776 {
4777 if ((dk4_time_t)0UL < start_unreach) {
4778 dk4time_get(¤t_time);
4779 timeout_time =
4780 start_unreach
4781 + (dk4_time_t)((0 == passno) ? unto_start : unto_end);
4782 if (current_time > timeout_time) {
4783 cc = 0;
4784 /* ERROR: Timeout while printer unreachable */
4785 log_1_no_header(136);
4786 }
4787 } else {
4788 dk4time_get(&start_unreach);
4789 }
4790 }
4791 } break;
4792 case PJSNMP_ST_ERROR : {
4793 start_idle = (dk4_time_t)0UL; /* not idle */
4794 uncoi = 0;
4795 if (
4796 ((0 == passno) && ((dk4_um_t)0UL < erto_start))
4797 || ((1 == passno) && ((dk4_um_t)0UL < erto_end))
4798 )
4799 {
4800 if ((dk4_time_t)0UL < start_error) {
4801 dk4time_get(¤t_time);
4802 timeout_time =
4803 start_error
4804 + (dk4_time_t)((0 == passno) ? erto_start : erto_end);
4805 if (current_time > timeout_time) {
4806 cc = 0;
4807 /* ERROR: Timeout while printer in error state */
4808 log_1_no_header(168);
4809 if (0 == passno) {
4810 back = 0; /* Do not print */
4811 }
4812 }
4813 } else {
4814 dk4time_get(&start_error);
4815 }
4816 }
4817 } break;
4818 default: {
4819 start_idle = (dk4_time_t)0UL; /* not idle */
4820 uncoi = 0;
4821 } break;
4822 }
4823
4824 /* Decrease cpu and network load from polling
4825 */
4826 if (1 == cc) {
4827 if (0 != can_continue(0)) {
4828 if (PJSNMP_ST_UNREACHABLE == stsum) {
4829 sleep(1);
4830 } else {
4831 to.tv_sec = 0L;
4832 to.tv_usec = 250000L;
4833 (void)select(0, NULL, NULL, NULL, &to);
4834 }
4835 } else {
4836 cc = -1;
4837 }
4838 }
4839
4840 } else {
4841 cc = -1;
4842 }
4843 } else {
4844 cc = -1;
4845 }
4846 } while(1 == cc);
4847 fputs(pjsnmp_kw[150], out_file);
4848 if (((0 == passno)&&(0 != have_pcs))||((1 == passno)&&(0 != have_pce))) {
4849
4850 res = dk4ma_write_c8_decimal_unsigned(buf, sizeof(buf), pcnt, 0, NULL);
4851 if (0 != res) {
4852
4853 log_3_no_header((size_t)(147+passno), 0, buf);
4854
4855 }
4856
4857 } else {
4858
4859 log_1_no_header(149);
4860
4861 }
4862
4863 /* Save page counter at start to page counter unusable for
4864 print job finished.
4865 */
4866 if (0 == passno) {
4867 if (0 != have_pcs) {
4868 pc_error = pc_start;
4869 }
4870 }
4871 /* After successful page counting at job end, report to
4872 accounting system.
4873 */
4874 if (1 == passno) {
4875 if ((0 != have_pcs) && (0 != have_pce)) {
4876 if (pc_end > pc_start) {
4877 /* Report number of used pages to accounting system */
4878 report_used_pages();
4879 }
4880 }
4881 }
4882 } else {
4883 if (0 != snmp_igfs) {
4884 back = 1;
4885 }
4886 }
4887 return back;
4888 }
4889
4890
4891
4892 /** Transfer data to printer.
4893 @return 1 on success, 0 on error.
4894 */
4895 static
4896 int
data_transfer(void)4897 data_transfer(void)
4898 {
4899 fd_set wfds; /* Writable files set */
4900 fd_set rfds; /* Readable files set */
4901 struct timeval to; /* Timeout for select */
4902 char *wrptr; /* Pointer to data to write */
4903 dk4_time_t unwriteable; /* Socket started to be unwriteable */
4904 dk4_time_t current; /* Current time */
4905 dk4_time_t tots; /* Timestamp when timeout is reached */
4906 size_t rdb; /* Number of bytes read from stdin */
4907 size_t remaining; /* Remaining bytes to write to socket */
4908 size_t wrb; /* Bytes currently written to socket */
4909 dk4_socket_t sock; /* Network socket to printer */
4910 int back = 0;
4911 int cc = 1; /* Flag: Can continue */
4912 int ccwr; /* Flag: Can continue writing */
4913 int res; /* Number conversion result */
4914 int writable; /* Flag: Socket is writeable */
4915
4916 unwriteable = (dk4_time_t)0UL;
4917 if ((NULL != host_name) && (0 < host_port)) {
4918 sock = dk4socket_c8_tcp_client_host_port(
4919 host_name, host_port, local_port, 0L, 0L, NULL
4920 );
4921 if (INVALID_SOCKET != sock) {
4922 exval = LPRNG_EXIT_SUCCESS;
4923 dk4time_get(&t2);
4924 back = 1;
4925 log_1_no_header(151);
4926 do {
4927 if (0 != can_continue(0)) {
4928 rdb = fread(pjsnmp_buf, 1, sizeof(pjsnmp_buf), stdin);
4929 if (0 < rdb) {
4930 wrptr = pjsnmp_buf;
4931 remaining = rdb;
4932 ccwr = 1;
4933 do {
4934 if (0 != can_continue(0)) {
4935 writable = 0;
4936 FD_ZERO(&wfds);
4937 FD_SET(sock,&wfds);
4938 if (0 != read_responses) {
4939 FD_ZERO(&rfds);
4940 FD_SET(sock,&rfds);
4941 }
4942 to.tv_sec = 0L;
4943 to.tv_usec = 250000L;
4944 if (0 != read_responses) {
4945 res = select((sock + 1), &rfds, &wfds, NULL, &to);
4946 } else {
4947 res = select((sock + 1), NULL, &wfds, NULL, &to);
4948 }
4949 if (0 < res) {
4950 if (FD_ISSET(sock,&wfds)) {
4951 writable = 1;
4952 unwriteable = (dk4_time_t)0UL;
4953 } else {
4954 if (0 != unwt_print) {
4955 dk4time_get(¤t);
4956 if ((dk4_time_t)0UL != unwriteable) {
4957 tots = unwriteable + (dk4_time_t)unwt_print;
4958 if (current > tots) {
4959 cc = ccwr = -1;
4960 /* ERROR: Timeout, socket unwriteable */
4961 log_1_no_header(137);
4962 }
4963 } else {
4964 unwriteable = current;
4965 }
4966 }
4967 }
4968 if (0 != read_responses) {
4969 if (FD_ISSET(sock,&rfds)) {
4970 wrb = sizeof(dummy_text);
4971 (void)dk4socket_recv(sock,dummy_text,&wrb,0,0L,0L,NULL);
4972 }
4973 }
4974 }
4975 if (0 != can_continue(0)) {
4976 if ((0 == writable) || (0 != check_trans)) {
4977 if (0 != one_snmp_request(1)) {
4978 switch (stsum) {
4979 case PJSNMP_ST_UNREACHABLE : {
4980 if ((dk4_um_t)0UL < unto_print) {
4981 if ((dk4_time_t)0UL < start_unreach) {
4982 tots = start_unreach + (dk4_time_t)unto_print;
4983 dk4time_get(¤t);
4984 if (current > tots) {
4985 cc = ccwr = -1;
4986 /* ERROR: Timeout, printer start_unreach */
4987 log_1_no_header(136);
4988 }
4989 } else {
4990 dk4time_get(&start_unreach);
4991 }
4992 }
4993 } break;
4994 case PJSNMP_ST_ERROR : {
4995 if ((dk4_um_t)0UL < erto_print) {
4996 if ((dk4_time_t)0UL < start_error) {
4997 tots = start_error + (dk4_time_t)erto_print;
4998 dk4time_get(¤t);
4999 if (current > tots) {
5000 cc = ccwr = -1;
5001 /* ERROR: Timeout, printer start_unreach */
5002 log_1_no_header(168);
5003 }
5004 } else {
5005 dk4time_get(&start_error);
5006 }
5007 }
5008 } break;
5009 }
5010 }
5011 }
5012 if (0 != can_continue(0)) {
5013 wrb = remaining;
5014 res = dk4socket_send(sock, wrptr, &wrb, 0, 0L, 0L, NULL);
5015 switch (res) {
5016 case DK4_SOCKET_RESULT_SUCCESS:
5017 case DK4_SOCKET_RESULT_IN_PROGRESS :
5018 {
5019 if (0 < wrb) {
5020 bytes_trans = dk4ma_um_add(bytes_trans,wrb,&er_bytes);
5021 if (wrb < remaining) {
5022 remaining = remaining - wrb;
5023 wrptr = &(wrptr[wrb]);
5024 } else {
5025 remaining = 0;
5026 ccwr = 0;
5027 }
5028 }
5029 } break;
5030 default: {
5031 /* ERROR: Failed to send data */
5032 log_1_no_header(138);
5033 ccwr = -1; cc = -1; back = 0;
5034 } break;
5035 }
5036 } else {
5037 ccwr = -1; cc = -1; back = 0;
5038 }
5039 } else {
5040 ccwr = -1; cc = -1; back = 0;
5041 }
5042 } else {
5043 ccwr = -1; cc = -1; back = 0;
5044 }
5045 } while (1 == ccwr);
5046 } else {
5047 have_eof = 1;
5048 cc = 0;
5049 if (0 != ferror(stdin)) {
5050 /* ERROR: Failed to read from standard input */
5051 log_1_no_header(139);
5052 }
5053 }
5054 } else {
5055 cc = -1;
5056 back = 0;
5057 }
5058 } while (1 == cc);
5059 log_1_no_header((0 == cc) ? 152 : 153);
5060 if (0 == cc) {
5061 if (0 != host_ordr) {
5062 res = dk4socket_shutdown(sock, DK4_SOCKET_SHUT_WRITE, NULL);
5063 if (DK4_SOCKET_RESULT_SUCCESS == res) {
5064 cc = 1;
5065 do {
5066 if (0 != can_continue(0)) {
5067 wrb = sizeof(pjsnmp_buf);
5068 res = dk4socket_recv(sock, pjsnmp_buf, &wrb, 0, 0L, 0L, NULL);
5069 switch (res) {
5070 case DK4_SOCKET_RESULT_SUCCESS: {
5071 if (0 == wrb) {
5072 cc = 0;
5073 }
5074 } break;
5075 default: {
5076 cc = -1;
5077 } break;
5078 }
5079 } else {
5080 cc = -1;
5081 back = 0;
5082 }
5083 } while (1 == cc);
5084 }
5085 }
5086 }
5087 dk4socket_close(sock, NULL);
5088 dk4time_get(&t3);
5089 } else {
5090 /* ERROR: Failed to connect */
5091 log_1_no_header(140);
5092 }
5093 } else {
5094 }
5095
5096 return back;
5097 }
5098
5099
5100
5101 /** Write one line of final statistics.
5102 @param b Text containing a number.
5103 @param max Maximum number width.
5104 @param i1 Index in pjsnmp_kw of text before number.
5105 @param i2 Index in pjsnmp_kw of text after number.
5106 */
5107 static
5108 void
final_stat_line(const char * b,size_t max,size_t i1,size_t i2)5109 final_stat_line(const char *b, size_t max, size_t i1, size_t i2)
5110 {
5111 size_t sl; /* String length of number */
5112
5113 sl = strlen(b);
5114 fputs(pjsnmp_kw[i1], out_file);
5115 while (sl++ < max) { fputc(' ', out_file); }
5116 fputs(b, out_file);
5117 fputs(pjsnmp_kw[i2], out_file);
5118 }
5119
5120
5121
5122 /** Issue final statistics.
5123 */
5124 static
5125 void
final_statistics(void)5126 final_statistics(void)
5127 {
5128 char b1[64]; /* Number of bytes */
5129 char b2[64]; /* Transfer time */
5130 char b3[64]; /* Processing time */
5131 char b4[64]; /* Number of pages */
5132 size_t max; /* Maximum of all sizes */
5133 size_t sl; /* Current size */
5134 int res; /* Conversion result */
5135 int found = 0; /* Information found */
5136
5137 /* Initialize buffers.
5138 */
5139 b1[0] = '\0'; b2[0] = '\0'; b3[0] = '\0'; b4[0] = '\0';
5140
5141 /* Convert numbers to strings.
5142 */
5143 if (DK4_E_NONE == er_bytes.ec) {
5144 res = dk4ma_write_c8_decimal_unsigned(
5145 b1, sizeof(b1), (dk4_um_t)bytes_trans, 0, NULL
5146 );
5147 if (0 == res) { b1[0] = '\0'; }
5148 else { found |= 1; }
5149 }
5150 if (((dk4_time_t)0UL != t2) && ((dk4_time_t)0UL != t3)) {
5151 res = dk4ma_write_c8_decimal_unsigned(
5152 b2, sizeof(b2), (dk4_um_t)(t3 - t2), 0, NULL
5153 );
5154 if (0 == res) { b2[0] = '\0'; }
5155 else { found |= 2; }
5156 }
5157 res = dk4ma_write_c8_decimal_unsigned(
5158 b3, sizeof(b3), (dk4_um_t)(t4 - t1), 0, NULL
5159 );
5160 if (0 == res) { b3[0] = '\0'; }
5161 else { found |= 4; }
5162 if ((0 != have_pcs) && (0 != have_pce)) {
5163 res = dk4ma_write_c8_decimal_unsigned(
5164 b4, sizeof(b4), (dk4_um_t)(pc_end - pc_start), 0, NULL
5165 );
5166 if (0 == res) { b4[0] = '\0'; }
5167 else { found |= 8; }
5168 }
5169
5170 /* Find maximum string length.
5171 */
5172 max = 0;
5173 if (0 != (1 & found)) {
5174 max = sl = strlen(b1);
5175 }
5176 if (0 != (2 & found)) {
5177 sl = strlen(b2);
5178 if (sl > max) max = sl;
5179 }
5180 if (0 != (4 & found)) {
5181 sl = strlen(b3);
5182 if (sl > max) max = sl;
5183 }
5184 if (0 != (8 & found)) {
5185 sl = strlen(b4);
5186 if (sl > max) max = sl;
5187 }
5188
5189 /* Write texts.
5190 */
5191 fputs(pjsnmp_kw[55], out_file);
5192 (void)log_timestamp();
5193 if (0 != (1 & found)) {
5194 final_stat_line(b1, max, 56, 60);
5195 }
5196 if (0 != (2 & found)) {
5197 final_stat_line(b2, max, 57, 61);
5198 }
5199 if (0 != (4 & found)) {
5200 final_stat_line(b3, max, 58, 61);
5201 }
5202 if (0 != (8 & found)) {
5203 final_stat_line(b4, max, 59, 0);
5204 }
5205 if (0 != found) {
5206 fflush(out_file);
5207 }
5208 }
5209
5210
5211
5212 /** Initialization at startup.
5213 */
5214 static
5215 void
initialize_at_startup(void)5216 initialize_at_startup(void)
5217 {
5218 stat_text[0] = '\0'; sz_stat_text = 0;
5219 ostat_text[0] = '\0'; sz_ostat_text = 0;
5220 dk4error_init(&er_bytes);
5221 }
5222
5223
5224
5225 static
5226 void
report_at_job_start(int argc,char * argv[])5227 report_at_job_start(int argc, char *argv[])
5228 {
5229 int found = 0;
5230 char *ptr;
5231 if (0 != verbose) {
5232 int i;
5233 fputs(pjsnmp_kw[169], out_file);
5234 for (i = 1; i < argc; i++) {
5235 fputs(argv[i], out_file);
5236 fputc('\n', out_file);
5237 }
5238 fflush(out_file);
5239 } else {
5240 if (NULL != user_name) {
5241 fputs(user_name, out_file);
5242 found = 1;
5243 }
5244 if (NULL != queue_name) {
5245 if (0 != found) { fputc(' ', out_file); }
5246 fputs(queue_name, out_file);
5247 found = 1;
5248 }
5249 if (NULL != job_name) {
5250 if (0 != found) { fputc(' ', out_file); }
5251 fputs(job_name, out_file);
5252 found = 1;
5253 }
5254 if (0 != found) { fputc('\n', out_file); }
5255 if (NULL != job_title) {
5256 ptr = job_title;
5257 while ('\0' != *ptr) {
5258 if (0x80 > ((unsigned char)(*ptr))) {
5259 fputc(*ptr, out_file);
5260 } else {
5261 fputc('?', out_file);
5262 }
5263 ptr++;
5264 }
5265 fputc('\n', out_file);
5266 found = 1;
5267 }
5268 if (0 != found) {
5269 fflush(out_file);
5270 }
5271 }
5272 }
5273
5274
5275
5276
5277 #endif
5278 /* if DK4_HAVE_LIBNETSNMP && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) */
5279
5280
5281
5282 /** Main program.
5283 @param argc Number of command line arguments.
5284 @param argv Command line arguments array.
5285 @return Exit status code, one from LPRNG_EXIT_xxx.
5286 */
5287 int
main(int argc,char * argv[])5288 main(int argc, char *argv[])
5289 {
5290 #if DK4_HAVE_LIBNETSNMP && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET))
5291
5292 /* +++++ All libraries available */
5293
5294 #if DK4_HAVE_SIGACTION
5295 #ifdef SIGPIPE
5296 struct sigaction opipe; /* Old disposition for SIGPIPE */
5297 struct sigaction npipe; /* New disposition for SIGPIPE */
5298 #endif
5299 struct sigaction oint; /* Old disposition for SIGINT */
5300 struct sigaction nint; /* New disposition for SIGINT */
5301 struct sigaction oterm; /* Old disposition for SIGTERM */
5302 struct sigaction nterm; /* New disposition for SIGTERM */
5303 #else
5304 #ifdef SIGPIPE
5305 dk4_sig_handler_t *opipe = NULL; /* Old disposition for SIGPIPE */
5306 #endif
5307 dk4_sig_handler_t *oterm = NULL; /* Old disposition for SIGTERM */
5308 dk4_sig_handler_t *oint = NULL; /* Old disposition for SIGINT */
5309 #endif
5310
5311 #if DK4_HAVE_SIGACTION
5312 #ifdef SIGPIPE
5313 int set_pipe = 0; /* Flag: Disposition SIGPIPE changed */
5314 #endif
5315 int set_int = 0; /* Flag: Disposition SIGINT changed */
5316 int set_term = 0; /* Flag: Disposition SIGTERM changed */
5317 int sig_i_f = 0; /* Flag: Handler installation failed */
5318 int sig_r_f = 0; /* Flag: Handler restoration failed */
5319 #endif
5320 int sockinit = 0; /* Flag: Socket subsys initialized */
5321
5322
5323
5324
5325 /* Initialize output file.
5326 */
5327 out_file = stderr;
5328 dk4time_get(&t1);
5329
5330 /* Copy default OID for status text.
5331 */
5332 DK4_MEMCPY(oid_st, oid_dst, by_oid_dst);
5333 sz_oid_st = sz_oid_dst;
5334 by_oid_st = sz_oid_st * sizeof(oid);
5335
5336 /* Process command line arguments, open log file.
5337 */
5338 if (0 == process_command_line_arguments(argc, argv)) { goto finished; }
5339
5340 /* Set up signal handlers.
5341 */
5342 #if DK4_HAVE_SIGACTION
5343 #ifdef SIGPIPE
5344 DK4_MEMRES(&npipe, sizeof(npipe));
5345 npipe.sa_handler = sig_handler_pipe;
5346 npipe.sa_flags = 0;
5347 if (0 != sigemptyset(&npipe.sa_mask)) { sig_i_f = 1; goto finished; }
5348 if (0 != sigaddset(&npipe.sa_mask, SIGPIPE)) { sig_i_f = 1; goto finished; }
5349 if (0 != sigaction(SIGPIPE, &npipe, &opipe)) { sig_i_f = 1; goto finished; }
5350 set_pipe = 1;
5351 #endif
5352 DK4_MEMRES(&nterm, sizeof(nterm));
5353 nterm.sa_handler = sig_handler_term;
5354 nterm.sa_flags = 0;
5355 if (0 != sigemptyset(&nterm.sa_mask)) { sig_i_f = 1; goto finished; }
5356 if (0 != sigaddset(&nterm.sa_mask, SIGTERM)) { sig_i_f = 1; goto finished; }
5357 if (0 != sigaction(SIGTERM, &nterm, &oterm)) { sig_i_f = 1; goto finished; }
5358 set_term = 1;
5359 DK4_MEMRES(&nint, sizeof(nint));
5360 nint.sa_handler = sig_handler_int;
5361 nint.sa_flags = 0;
5362 if (0 != sigemptyset(&nint.sa_mask)) { sig_i_f = 1; goto finished; }
5363 if (0 != sigaddset(&nint.sa_mask, SIGINT)) { sig_i_f = 1; goto finished; }
5364 if (0 != sigaction(SIGINT, &nint, &oint)) { sig_i_f = 1; goto finished; }
5365 set_int = 1;
5366 #else
5367 #ifdef SIGPIPE
5368 opipe = sigset(SIGPIPE, sig_handler_pipe);
5369 #endif
5370 oint = sigset(SIGINT, sig_handler_int);
5371 oterm = sigset(SIGTERM, sig_handler_term);
5372 #endif
5373
5374 /* Process remaining setup (PRINTCAP_ENTRY, model file).
5375 */
5376 if (0 == process_setup()) { goto finished; }
5377
5378 /* Header files for job in log output.
5379 */
5380 report_at_job_start(argc, argv);
5381
5382 /* Initialize socket subsystem.
5383 */
5384 if (DK4_SOCKET_RESULT_SUCCESS != dk4socket_up(NULL)) {
5385 log_1_no_header(170);
5386 goto finished;
5387 }
5388 sockinit = 1;
5389
5390 /* Check users permission to print.
5391 */
5392 if (0 == check_permission()) { goto finished; }
5393
5394 /* Establish SNMP session.
5395 */
5396 if (0 == attempt_snmp_session()) { goto finished; }
5397
5398 /* Initialization.
5399 */
5400 initialize_at_startup();
5401
5402 /* Accounting before job.
5403 */
5404 if (0 == do_accounting(0)) { goto finished; }
5405
5406 /* Data transfer.
5407 */
5408 if (0 == data_transfer()) { goto finished; }
5409
5410 /* Accounting after job.
5411 */
5412 if (0 == do_accounting(1)) { goto finished; }
5413
5414 /* Cleanup
5415 -------
5416 */
5417 finished:
5418
5419 /* Clean up SNMP session.
5420 */
5421 if (NULL != snmp_sess) { snmp_close(snmp_sess); }
5422
5423 /* Eat up data from standard input.
5424 */
5425 eatup_input();
5426
5427 /* release socket subsystem.
5428 */
5429 if (0 != sockinit) { (void)dk4socket_down(NULL); }
5430
5431 /* Release status text nodes, if any.
5432 */
5433 relase_status_text_nodes();
5434
5435 /* Release dynamically allocated printcap entry copy.
5436 */
5437 dk4mem_release(printcap_entry);
5438
5439 /* Restore signal handlers
5440 */
5441 #if DK4_HAVE_SIGACTION
5442 if (0 != sig_i_f) {
5443 /* ERROR: Failed to set up signal handlers */
5444 log_1_no_header(141);
5445 }
5446 if (0 != set_int) {
5447 if (0 != sigaction(SIGINT, &oint, NULL)) { sig_r_f = 1; }
5448 }
5449 if (0 != set_term) {
5450 if (0 != sigaction(SIGTERM, &oterm, NULL)) { sig_r_f = 1; }
5451 }
5452 #ifdef SIGPIPE
5453 if (0 != set_pipe) {
5454 if (0 != sigaction(SIGPIPE, &opipe, NULL)) { sig_r_f = 1; }
5455 }
5456 #endif
5457 if (0 != sig_r_f) {
5458 /* ERROR: Failed to restore signal handlers */
5459 log_1_no_header(142);
5460 }
5461 #else
5462 if (NULL != oterm) { sigset(SIGTERM, oterm); }
5463 if (NULL != oint ) { sigset(SIGINT, oint ); }
5464 #ifdef SIGPIPE
5465 if (NULL != opipe) { sigset(SIGPIPE, opipe); }
5466 #endif
5467 #endif
5468
5469 /* Final log message, close log file
5470 */
5471 dk4time_get(&t4);
5472 if (2 > decision) { final_statistics(); }
5473 if (NULL != stt_file) { fclose(stt_file); }
5474 out_file = stderr;
5475
5476
5477
5478
5479 /* ----- All libraries available */
5480
5481 #else
5482 /* if DK4_HAVE_LIBNETSNMP && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) */
5483 /* +++++ Missing libraries */
5484 #if !DK4_HAVE_LIBNETSNMP
5485 fputs("ERROR: Missing Net-SNMP support!\n", stderr);
5486 #endif
5487 #if !((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET))
5488 fputs("ERROR: Missing sigaction()/sigset() support!\n", stderr);
5489 #endif
5490 fflush(stderr);
5491 /* ----- Missing libraries */
5492 #endif
5493 /* if DK4_HAVE_LIBNETSNMP && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) */
5494 exit(exval); return exval;
5495 }
5496
5497
5498 /*
5499
5500 2016-02-26 Added dk4socket_up() and dk4socket_down().
5501
5502 */
5503