%% options copyright owner = Dirk Krause copyright year = 2016-xxxx license = bsd %% module #include "dk4conf.h" #include "dk4types.h" #include #if DK4_HAVE_LIBNETSNMP && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) #include #include #include #include #include "dk4types.h" #include "dk4inst.h" #include "dk4maasz.h" #include "dk4maadu.h" #include "dk4mem.h" #include "dk4sto.h" #include "dk4str8.h" #include "dk4fopc8.h" #include "dk4stat.h" #include "dk4stat8.h" #include "dk4mai8dus.h" #include "dk4mai8huc.h" #include "dk4mai8dii.h" #include "dk4mai8ddu.h" #include "dk4mao8d.h" #include "dk4mao8h.h" #include "dk4time.h" #include "dk4time08.h" #include "dk4sock.h" #include "dk4isadm.h" #include "dk4lprng.h" $!trace-include #endif /* if DK4_HAVE_LIBNETSNMP && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) */ /** One status text entry from SNMP model. */ typedef struct { unsigned char *text; /**< Text pointer, not 0-terminated. */ size_t size; /**< Text size. */ int state; /**< Summary state if text was found. */ int start; /**< Flag: Text here is start text only. */ } status_text_t; #if DK4_HAVE_LIBNETSNMP && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) /** SNMP device status. */ enum { SNMP_DEVST_UNKNOWN = 1 , /**< Status is unknown. */ SNMP_DEVST_RUNNING = 2 , /**< Running. */ SNMP_DEVST_WARNING = 3 , /**< Warning condition active. */ SNMP_DEVST_TESTING = 4 , /**< In self test. */ SNMP_DEVST_DOWN = 5 /**< Down due to error condition. */ }; /** SNMP printer status. */ enum { SNMP_PRST_OTHER = 1 , /**< Any state not mentioned below. */ SNMP_PRST_UNKNOWN = 2 , /**< Unknown. */ SNMP_PRST_IDLE = 3 , /**< Ready, waiting for next job. */ SNMP_PRST_PRINTING = 4 , /**< Printing a job. */ SNMP_PRST_WARMUP = 5 /**< Warming up. */ }; /** State summary. For accounting we would only need to distinguish between standby/idle (secure to retrieve page counter) and anything else. But we want to print human readable state summaries to status file which are shown by lpq -L. */ enum { PJSNMP_ST_UNKNOWN = 0 , /**< Failed to retrieve printer state. */ PJSNMP_ST_UNREACHABLE = 1 , /**< Printer is unreachable. */ PJSNMP_ST_ERROR = 2 , /**< Error state on printer. */ PJSNMP_ST_BUSY = 3 , /**< Printer is printing. */ PJSNMP_ST_IDLE = 4 , /**< Printer idle, can retrieve page counter. */ PJSNMP_ST_WARMUP = 5 , /**< Warming up. */ PJSNMP_ST_STANDBY = 6 , /**< Standby. */ }; /** @defgroup pdes Printer detected error states. The first byte in the octet string is the most significant byte in the unsigned long value. */ /**@{*/ /** Warning: Paper low. */ #define PDES_WARNING_PAPER_LOW 0x80000000UL /** Error: Out of paper. */ #define PDES_ERROR_NO_PAPER 0x40000000UL /** Warning: Toner low. */ #define PDES_WARNING_TONER_LOW 0x20000000UL /** Error: Out of toner. */ #define PDES_ERROR_NO_TONER 0x10000000UL /** Error: Door open. */ #define PDES_ERROR_DOOR_OPEN 0x08000000UL /** Error: Paper jam. */ #define PDES_ERROR_PAPER_JAM 0x04000000UL /** Error: Printer offline. */ #define PDES_ERROR_OFFLINE 0x02000000UL /** Warning: Service requested. */ #define PDES_WARNING_SERVICE 0x01000000UL /** Input tray removed. */ #define PDES_ERROR_INPUT_TRAY_MISSING 0x00800000UL /** Output tray removed. */ #define PDES_ERROR_OUTPUT_TRAY_MISSING 0x00400000UL /** Marker supply missing. */ #define PDES_ERROR_MARKER_SUPPLY_MISSING 0x00200000UL /** Output tray nearly full. */ #define PDES_WARNING_OUTPUT_NEAR_FULL 0x00100000UL /** Output tray full. */ #define PDES_ERROR_OUTPUT_FULL 0x00080000UL /** At least one input tray empty. */ #define PDES_WARNING_INPUT_TRAY_EMPTY 0x00040000UL /** Overdue preventive maintenance. */ #define PDES_WARNING_OVERDUE_PREVENT_MAINT 0x00020000UL /** Bitmask to check if there is any error condition. */ #define PDES_ANY_ERROR \ ( PDES_ERROR_NO_PAPER \ | PDES_ERROR_NO_TONER \ | PDES_ERROR_DOOR_OPEN \ | PDES_ERROR_PAPER_JAM \ | PDES_ERROR_OFFLINE \ | PDES_ERROR_INPUT_TRAY_MISSING \ | PDES_ERROR_OUTPUT_TRAY_MISSING \ | PDES_ERROR_MARKER_SUPPLY_MISSING \ | PDES_ERROR_OUTPUT_FULL) /** Bitmask to check if there is any warning condition. */ #define PDES_ANY_WARNING \ ( PDES_WARNING_PAPER_LOW \ | PDES_WARNING_TONER_LOW \ | PDES_WARNING_SERVICE \ | PDES_WARNING_OUTPUT_NEAR_FULL \ | PDES_WARNING_OVERDUE_PREVENT_MAINT \ | PDES_WARNING_INPUT_TRAY_EMPTY) /** Bitmask to check whether any error or warning condition is set. */ #define PDES_ANY_CONDITION ((PDES_ANY_WARNING) | (PDES_ANY_ERROR)) /**@}*/ /** Buffer for sending. Should be large to minimize number of I/O operations. */ #if DK4_SIZEOF_SIZE_T > 2 static char pjsnmp_buf[1048576]; #else static char pjsnmp_buf[16384]; #endif /** Current status text, also used in accounting. */ static char stat_text[2048]; /** Buffer for responses from printer. */ static char dummy_text[2048]; /** Previous status text. */ static char ostat_text[sizeof(stat_text)]; /** Buffer for automatically constructed job name. */ static char job_name_buf[32+(8*sizeof(dk4_um_t))]; /** Status text oid. */ static oid oid_st[MAX_OID_LEN]; /** OID for device status. */ static const oid oid_ds[] = { (oid)1, (oid)3, (oid)6, (oid)1, (oid)2, (oid)1, (oid)25, (oid)3, (oid)2, (oid)1, (oid)5, (oid)1 }; /** OID for printer status. */ static const oid oid_ps[] = { (oid)1, (oid)3, (oid)6, (oid)1, (oid)2, (oid)1, (oid)25, (oid)3, (oid)5, (oid)1, (oid)1, (oid)1 }; /** OID for pagecount value. */ static const oid oid_pc[] = { (oid)1, (oid)3, (oid)6, (oid)1, (oid)2, (oid)1, (oid)43, (oid)10, (oid)2, (oid)1, (oid)4, (oid)1, (oid)1 }; /** OID for printer detected error state. */ static const oid oid_pe[] = { (oid)1, (oid)3, (oid)6, (oid)1, (oid)2, (oid)1, (oid)25, (oid)3, (oid)5, (oid)1, (oid)2, (oid)1 }; /** Default OID for status text. */ static const oid oid_dst[] = { (oid)1, (oid)3, (oid)6, (oid)1, (oid)2, (oid)1, (oid)43, (oid)16, (oid)5, (oid)1, (oid)2, (oid)1, (oid)1 }; /** Constant texts, not localized. */ static const char * const pjsnmp_kw[] = { $!string-table # # 0 Newline # \n # # 1 Space # # # 2 Environment variable name # PRINTCAP_ENTRY # # 3 Asterisk for all device/printer status names # * # # 4 Program name # pjsnmp # # 5 Default SNMP community # public # # 6 Keyword for permission checking. # acct-check # # 7 ... 10 Keywords # Device and printer status: / Printer detected error state: Current printer state: # # 11 ... 16 Device status # not found unknown running warning testing down # # 17 ... 22 Printer status # not found other unknown idle printing warmup # # 23 ... 29 Printer summary states # UNKNOWN UNREACHABLE ERROR BUSY IDLE WARMUP STANDBY # # 30 31 # " "\n # # 32 33 Abbreviations for errors and warnings # ERROR: Warning: # # 34 48 Printer detected error states. # Paper low (printer will run out of paper soon).\n Out of paper!\n Toner low (printer will run out of toner soon).\n Out of toner!\n Door open!\n Paper jam!\n Offline!\n Service requested.\n Input tray missing!\n Output tray missing!\n Marker supply missing!\n Output tray nearly full (will be full soon).\n Output tray full!\n At least one input tray is empty.\n Overdue preventive maintenance.\n # # 49 50 # ERROR ON PRINTER, MANUAL INTERVENTION REQUIRED!\n PRINTER PROBABLY REQUIRES MANUAL INTERVENTION!\n # # 51 52 53 Accounting keywords # acct-start acct-end acct-charge # # 54 # print-job # # 55 # Print job finished.\n # # 56 ... 59 # Job size: Data transfer: Processing: Pages: # # 60 ... 61 units # bytes\n seconds\n # # 62 Start of line before timestamps # \# # # 63 ... 65 (log_1_with_header 1...3) # Configuration problem (command line arguments)!\n Configuration problem (printcap file)!\n Configuration problem (SNMP model file)!\n # # 66 # ERROR: Missing option name!\n # # 67 # ERROR: Missing option argument!\n # # 68 # ERROR: Non-option command line argument found!\n # # 69 # ERROR: No user name specified!\n # # 70 # ERROR: No print queue name specified!\n # # 71 # ERROR: No print job name specified!\n # # 72 73 74 # Warning: Redefinition of " " to " "!\n # # 75 76 # ERROR: Empty text for option " "!\n # # 77 78 # ERROR: Missing text for " "!\n # # 79 80 81 # ERROR: Option " " requires unsigned short number!\nIllegal text: " "! # # 82 83 # ERROR: Illegal SNMP version: " "!\nAllowed: "1", "2c", "3"!\n # # 84 85 86 # ERROR: Option " " requires an integer value!\nIllegal text: " "!\n # # 87 # ERROR: Missing "]"!\n # # 88 # ERROR: Missing model name!\n # # 89 90 # ERROR: Illegal summary state: " "!\n # # 91 92 # ERROR: Illegal printer status name: " "!\n # # 93 94 # ERROR: Illegal device status name: " "! # # 95 # ERROR: Missing printer status name!\n # # 96 # ERROR: Missing summary state name!\n # # 97 98 # ERROR: Illegal condition name: " "!\n # # 99 # ERROR: OID too long!\n # # 100 101 # ERROR: Numeric overflow for sub id: " "!\n # # 102 103 # ERROR: Not a number: " "!\n # # 104 # ERROR: Empty OID text!\n # # 105 # ERROR: Memory allocation failed!\n # # 106 # ERROR: Empty string!\n # # 107 # ERROR: Incorrect string specification!\n # # 108 109 # ERROR: Not a hexadecimal byte: " "!\n # # 110 # ERROR: Value required!\n # # 111 112 # ERROR: Not a boolean: " "!\n # # 113 114 # ERROR: Illegal key: " "!\n # # 115 116 # ERROR: Model not found: " "!\n # # 117 118 # ERROR: Failed to open model file!\n" "!\n # # 119 # ERROR: Missing host name for printer!\n # # 120 # ERROR: PRINTCAP_ENTRY not found in environment!\n # # 121 # ERROR: Failed to create SNMP session!\n # # 122 # ERROR: Failed to send data to accounting system!\n # # 123 # ERROR: Partial success when sending data to accounting system!\n # # 124 # ERROR: Failed to shut down accounting socket!\n # # 125 # ERROR: Failed to connect to accounting system!\n # # 126 # ERROR: Failed to start accounting program!\n # # 127 # ERROR: Missing command name for accounting program!\n # # 128 # ERROR: Failed to open accounting file!\n # # 129 130 # ERROR: Can not receive response from pipe!\n ERROR: Can not receive response from file!\n # # 131 ... 133 # Checking permission: user=" " printer=" ".\n # # 134 135 # Printing allowed.\n ERROR: PRINTING DENIED BY ACCOUNTING/QUOTA SYSTEM!\n # # 136 # ERROR: Printer unreachable too long!\n # # 137 # ERROR: Network connection not writeable (timeout)!\n # # 138 # ERROR: Failed to send data!\n # # 139 # ERROR: Failed to read print data from standard input!\n # # 140 # ERROR: Failed to connect to printer!\n # # 141 142 # ERROR: Failed to set up signal handlers!\n ERROR: Failed to restore signal handlers!\n # # 143 # : # # 144 Empty string # # # 145 146 # Retrieving page counter at start.\n Retrieving page counter at end.\n # # 147 148 # Page counter at start: Page counter at end: # # 149 # Failed to retrieve page counter value!\n # # 150 # ---\n # # 151 ... 153 # Starting print data transfer.\n Finished print data transfer.\n Print data transfer aborted.\n # # 154 ... 158 # Transmitting accounting data via network socket.\n Writing accounting data pipe.\n Transmitting accounting data via local socket.\n Writing accounting data to file.\n Transmitting accounting data to fd 3.\n # # 159 160 # Accounting data transmitted successfully.\n ERROR: Failed to transmit accounting data!\n # # 161 # " requires an unsigned integer value!\nIllegal text: " # # 162 ... 164 # Warning: Unusual change to idle/standby state.\nWaiting seconds for printer to resume printing.\n # # 165 # ERROR: No accounting destination configured!\n # # 166 167 # Response from accounting system: " ".\n # # 168 # Printer in error state too long!\n # # 169 # Command line arguments:\n # # 170 # ERROR: Failed to initialize socket subsystem!\n # # 171 # pjsnmp@localhost+ $!end }; /** Responses from accounting system. */ static const char * const accounting_responses[] = { $!string-table ACCEPT HOLD REMOVE $!end }; /** Names for configuration items in the printcap entry. */ static const char * const printcap_entry_names[] = { $!string-table # # 0 Host name, default: none. # pjsnmp-host # # 1 Port number, default: 9100. # pjsnmp-port # # 2 Orderly release, default: off. # pjsnmp-ordrel # # 3 Accounting enabled, default: off # pjsnmp-accounting-enable # # 4 Check quota, default: off. # pjsnmp-accounting-check # # 5 Accounting file, default: none. # pjsnmp-accounting-file # # 6 Accounting host, default: none. # pjsnmp-accounting-host # # 7 Accounting port, default: 9100. # pjsnmp-accounting-port # # 8 Timeout if no printing occures, default: 300. # pjsnmp-require-printing # # 9 SNMP version, default: 1. # pjsnmp-version # # 10 SNMP community, default: public. # pjsnmp-community # # 11 Flag: SNMP checks also on successfully send, default: on # pjsnmp-during-transfer # # 12 File for descriptions, default: /usr/share/dktools/print-snmp.conf. # pjsnmp-model-file # # 13 SNMP model for the printer, default: none. # pjsnmp-model-name # # 14 Flag: Ignore failed SNMP session, default: off # pjsnmp-ignore-failed-session # # 15 Flag: Allow printing if accounting connection failed, default: off. # pjsnmp-allow-printing-if-accounting-connection-failed # # 16 Flag: Always use printer detected error state, default: on # pjsnmp-pdes-always # # 17 Flag: Show unknown states as busy, default: off. # pjsnmp-unknown-busy # # 18 Timeout if printer is unreachable at start, default: 3600 seconds. # pjsnmp-unreachable-timeout-start # # 19 Timeout if printer is unreachable at end, default: 3600 seconds. # pjsnmp-unreachable-timeout-end # # 20 Timeout if printer is unreachable during data transfer, # default: 3600 seconds. # pjsnmp-unreachable-timeout-printing # # 21 Local port number, default: none. # pjsnmp-local-port # # 22 Timeout if print socket is unwriteable during data transfer, # default: 3600 seconds. # pjsnmp-unwriteable-timeout-printing # # 23 Flag: Obtain responses from printer, default: off. # pjsnmp-read-response # # 24 Flag: Force printable status text. # pjsnmp-force-printable-status-text # # 25 Timeout for error state at start of job # pjsnmp-error-timeout-start # # 26 Timeout for error state at end of job # pjsnmp-error-timeout-end # # 27 Timeout for error state during printing # pjsnmp-error-timeout-printing # # 28 Flag: Verbose # pjsnmp-verbose # # 29 Flag: Allow Non-ASCII characters in job title for accounting. # pjsnmp-non-ascii-job-title # # 30 Flag: Use two accounting lines for start and end # pjsnmp-accounting-start-end $!end }; /** SNMP version names, 2 and 2c are the same. */ static const char * const snmp_version_names[] = { $!string-table 1 2 2c 3 $!end }; /** Keywords used in the SNMP models file. */ static const char * const model_keys[] = { $!string-table # 0 map # 1 2 error condition add error condition # 3 status text oid # 4 5 6 7 text standby start standby text idle start idle # 8 always use status text # # 9 10 # text busy start busy # # 11 ... 16 # text unknown start unknown text error start error text warmup start warmup $!end }; /** Summary state keywords used in the models file, can be abbreviated. */ static const char * const summary_state_names[] = { $!string-table u$nknown e$rror b$usy i$dle w$armup s$tandby $!end }; /** Device status names, can be abbreviated. */ static const char * const device_status_names[] = { $!string-table u$nknown r$unning w$arning t$esting d$own $!end }; /** Printer status names, can be abbreviated. */ static const char * const printer_status_names[] = { $!string-table o$ther u$nknown i$dle p$rinting w$armup $!end }; /** Names for the condition bits in the SNMP models file. */ static const char * const condition_names[] = { $!string-table low-paper no-paper low-toner no-toner door-open paper-jam offline service-requested input-tray-missing output-tray-missing marker-supply-missing output-near-full output-full input-tray-empty overdue-preventive-maintenance $!end }; /** Default configuration file for SNMP models. */ static const char def_model_file[] = { DK4_INST_DIR_SHARE "/dktools/print-snmp.conf" }; /** Option values for uppercase options. */ static char *upper[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; /** Option values for lowercase options. */ static char *lower[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; /** Mapping from device/printer status combinations to short summaries. */ static int summary_states[] = { /* unknown-other */ PJSNMP_ST_UNKNOWN , /* unknown-unknown */ PJSNMP_ST_UNKNOWN , /* unknown-idle */ PJSNMP_ST_UNKNOWN , /* unknown-printing */ PJSNMP_ST_UNKNOWN , /* unknown-warmup */ PJSNMP_ST_WARMUP , /* running-other */ PJSNMP_ST_STANDBY , /* running-unknown */ PJSNMP_ST_UNKNOWN , /* running-idle */ PJSNMP_ST_IDLE , /* running-printing */ PJSNMP_ST_BUSY , /* running-warmup */ PJSNMP_ST_WARMUP , /* warning-other */ PJSNMP_ST_STANDBY , /* warning-unknown */ PJSNMP_ST_UNKNOWN , /* warning-idle */ PJSNMP_ST_IDLE , /* warning-printing */ PJSNMP_ST_BUSY , /* warning-warmup */ PJSNMP_ST_WARMUP , /* testing-other */ PJSNMP_ST_UNKNOWN , /* testing-unknown */ PJSNMP_ST_UNKNOWN , /* testing-idle */ PJSNMP_ST_UNKNOWN , /* testing-printing */ PJSNMP_ST_UNKNOWN , /* testing-warmup */ PJSNMP_ST_WARMUP , /* down-other */ PJSNMP_ST_ERROR , /* down-unknown */ PJSNMP_ST_ERROR , /* down-idle */ PJSNMP_ST_ERROR , /* down-printing */ PJSNMP_ST_ERROR , /* down-warmup */ PJSNMP_ST_WARMUP }; /** Error report for number of bytes transferred. */ static dk4_er_t er_bytes; /** Copy of the printcap entry from PRINTCAP_ENTRY environment variable (allocated, freed during cleanup before exit). */ static char *printcap_entry = NULL; /** Printer host name (from printcap entry). */ static char *host_name = NULL; /** User name to be charged for the job (from -n or -L option). */ static char *user_name = NULL; /** Print queue name (from -P or -Q option). */ static char *queue_name = NULL; /** Job title (from -J -f or -N option). */ static char *job_title = NULL; /** Job name from -A -j -t or -D option (only needed for old accounting system). */ static char *job_name = NULL; /** File, socket, pipe or host name for accounting (from pjsnmp-accounting-file in printcap entry). */ static char *acct_name = NULL; /** Host to connect to for accounting (from pjsnmp-accounting-host in printcap entry). */ static char *acct_host = NULL; /** SNMP community name (from pjsnmp-community in printcap entry). */ static char *snmp_comm = NULL; /** File name containing SNMP models (from pjsnmp-model-file in printcap entry). */ static char *model_file = NULL; /** SNMP model name (from pjsnmp-model-name in printcap entry). */ static char *model_name = NULL; /** Status file for log messages (name obtained from -s cmd arg). */ static FILE *stt_file = NULL; /** Output file to use for log messages. This is either stt_file or stderr, but never NULL. */ static FILE *out_file = NULL; /** SNMP session. */ static struct snmp_session *snmp_sess = NULL; /** Storage for status text nodes. */ static dk4_sto_t *s_stn = NULL; /** Iterator through storage above. */ static dk4_sto_it_t *i_stn = NULL; /** Number of bytes transferred. */ static dk4_um_t bytes_trans = (dk4_um_t)0UL; /** Timeout if printer not seen printing (from pjsnmp-require-printing in printcap entry). If the first page contains a complicated graphics it might take a longer time to start printing. */ static dk4_um_t print_timeout = (dk4_um_t)600UL; /** Timeout if printer is unreachable at start of print job (from pjsnmp-unreachable-timeout-start in printcap entry). */ static dk4_um_t unto_start = (dk4_um_t)900UL; /** Timeout if printer is unreachable during data transfer (from pjsnmp-unreachable-timeout-printing in printcap entry). */ static dk4_um_t unto_print = (dk4_um_t)120UL; /** Timeout if printer socket is unwriteable during data transfer (from pjsnmp-unwriteable-timeout-printing in printcap entry). */ static dk4_um_t unwt_print = (dk4_um_t)900UL; /** Timeout if printer is unreachable at end of print job (from pjsnmp-unreachable-timeout-end in printcap entry). */ static dk4_um_t unto_end = (dk4_um_t)120UL; /** Timeout for error condition before printing to skip job. */ static dk4_um_t erto_start = (dk4_um_t)900UL; /** Timeout if printer is in error state during printing. */ static dk4_um_t erto_print = (dk4_um_t)86400UL; /** Timeout if printer is in error state after job. */ static dk4_um_t erto_end = (dk4_um_t)3600UL; /** Start time printer gone unreachable. */ static dk4_time_t start_unreach = (dk4_time_t)0UL; /** Start time printer gone error. */ static dk4_time_t start_error = (dk4_time_t)0UL; /** Size of device status OID (number of elements). */ static const size_t sz_oid_ds = sizeof(oid_ds)/sizeof(oid); /** Size of device status OID (number of bytes). */ static const size_t by_oid_ds = sizeof(oid_ds); /** Size of printer status OID (number of elements). */ static const size_t sz_oid_ps = sizeof(oid_ps)/sizeof(oid); /** Size of printer status OID (number of bytes). */ static const size_t by_oid_ps = sizeof(oid_ps); /** Size of page counter OID (number of elements). */ static const size_t sz_oid_pc = sizeof(oid_pc)/sizeof(oid); /** Size of page counter OID (number of bytes). */ static const size_t by_oid_pc = sizeof(oid_pc); /** Size of printer error OID (number of elements). */ static const size_t sz_oid_pe = sizeof(oid_pe)/sizeof(oid); /** Size of printer error OID (number of bytes). */ static const size_t by_oid_pe = sizeof(oid_pe); /** Size of printer error OID (number of elements). */ static const size_t sz_oid_dst = sizeof(oid_dst)/sizeof(oid); /** Size of printer error OID (number of bytes). */ static const size_t by_oid_dst = sizeof(oid_dst); /** Number of items in status text OID. */ static size_t sz_oid_st = sizeof(oid_dst)/sizeof(oid); /** Number of bytes in status text OID. */ static size_t by_oid_st = sizeof(oid_dst); /** Number of bytes currently stored in stat_text. */ static size_t sz_stat_text = 0; /** Number of bytes stored in ostat_text. */ static size_t sz_ostat_text = 0; /** Start of program. */ static dk4_time_t t1 = (dk4_time_t)0UL; /** Start of data transfer. */ static dk4_time_t t2 = (dk4_time_t)0UL; /** End of data transfer. */ static dk4_time_t t3 = (dk4_time_t)0UL; /** End of program. */ static dk4_time_t t4 = (dk4_time_t)0UL; /** SNMP version number (from pjsnmp-version in printcap entry). */ static long snmp_version = SNMP_VERSION_1; /** Bit mask to retrieve real error conditions from hrPrinterDetectedErrorStates (modified by error condition and add error condition in model). */ static unsigned long pdes_error = PDES_ANY_ERROR ; /** Previous printer detected error state conditions. */ static unsigned long opdes_cond = 0UL; /** Current printer detected error state conditions. */ static unsigned long pdes_cond = 0UL; /** Page counter at start of job. */ static unsigned long pc_start = 0UL; /** Unusable page counter value (start value or at last error). */ static unsigned long pc_error = 0UL; /** Page counter at end of job. */ static unsigned long pc_end = 0UL; /** Current page counter. */ static unsigned long pcnt = 0UL; #endif /* if DK4_HAVE_LIBNETSNMP && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) */ /** Exit status code, must be one from the enum. */ static int exval = LPRNG_EXIT_REMOVE; #if DK4_HAVE_LIBNETSNMP && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) /** Flag: Verbose */ static int verbose = 0; /** Flag: Allow non-ASCII characters in job title for accounting. */ static int allow_non_ascii = 0; /** Always use status text (default: only if summary is "unknown"). */ static int stat_always = 0; /** Flag: Always use printer detected error state (from pjsnmp-pdes-always in printcap entry), default: on. */ static int pdes_always = 1; /** Flag: Reset of pdes_error was done. */ static int pdes_reset = 0; /** Flag: Report unknown states as busy (from pjsnmp-unknown-busy in printcap entry), default: off. */ static int unkn_busy = 0; /** Flag: Do orderly release (from pjsnmp-ordrel in printcap entry). */ static int host_ordr = 0; /** Flag: Accounting enabled (from pjsnmp-accounting-enable in printcap entry). */ static int acct_enab = 0; /** Flag: Quota check enabled (from pjsnmp-accounting-check in printcap entry). */ static int acct_achk = 0; /** Flag: Use separated start/end lines for accounting. Default is to use just one charge line at end. */ static int acct_start_end = 0; /** Flag: Read printer responses (from pjsnmp-read-response in printcap entry). */ static int read_responses = 0; /** Flag: Force printable status text (from pjsnmp-force-printable-status-text in printcap entry). */ static int force_printable_status_text = 1; /** Flag: SNMP check during transfer (from pjsnmp-during-transfer in printcap entry). */ static int check_trans = 1; /** Flag: EOF found on standard input. */ static int have_eof = 0; /** Ignore failed SNMP session (from pjsnmp-ignore-failed-session in printcap entry). */ static int snmp_igfs = 0; /** Allow printing if accounting connection failed. (from pjsnmp-allow-printing-if-accounting-connection-failed). */ static int acct_apcf = 0; /** Flag: Have page counter at start. */ static int have_pcs = 0; /** Flag: Have page counter at end. */ static int have_pce = 0; /** Previous device status. */ static int odevst = 0; /** Current device status. */ static int devst = 0; /** Previous printer status. */ static int oprist = 0; /** Current printer status. */ static int prist = 0; /** Previous summary status. */ static int ostsum = PJSNMP_ST_UNKNOWN; /** Current summary status. */ static int stsum = PJSNMP_ST_UNKNOWN; /** Flag: The printer was seen in printing state. This flag is cleared if the printer was seen in error state, as some printers go from error to printing via idle. */ static int seen_printing = 0; /** Last header used for log messages. */ static int last_header = 0; /** Decision from permission check 0=unknown 1=accept 2=hold 3=remove */ static int decision = 0; /** Port number to send data to in host representation (from pjsnmp-port in printcap entry). */ static unsigned short host_port = 9100; /** Local port number. (from pjsnmp-local-port in printcap entry). */ static unsigned short local_port = 0; /** Accounting port number in host representation (from pjsnmp-accounting-port in printcap entry). */ static unsigned short acct_port = 9100; #ifdef SIGPIPE /** Indicator: SIGPIPE signal received. */ static DK4_VOLATILE dk4_sig_atomic_t sig_had_pipe = 0; #endif /** Indicator: SIGINT signal received. */ static DK4_VOLATILE dk4_sig_atomic_t sig_had_int = 0; /** Indicator: SIGTERM signal received. */ static DK4_VOLATILE dk4_sig_atomic_t sig_had_term = 0; /** Pass volatile pointer through to caller. The CERT C coding standard recommends to set signal indicator variables through pointers to avoid compilers from optimizing too hard (they might attempt to keep the value in a register although properly marked as volatile). @param ptr Address of volatile indicator variable. @return Same pointer as ptr. */ static DK4_VOLATILE dk4_sig_atomic_t * sig_pass_pointer(DK4_VOLATILE dk4_sig_atomic_t *ptr) { return ptr; } #ifdef SIGPIPE /** Handler for SIGPIPE signal. @param signo Signal number (always SIGPIPE, ignored). */ static void sig_handler_pipe(int signo) { *sig_pass_pointer(&sig_had_pipe) = 1; } #endif /** Handler for SIGINT signal. @param signo Signal number (always SIGINT, ignored). */ static void sig_handler_int(int signo) { *sig_pass_pointer(&sig_had_int) = 1; } /** Handler for SIGTERM signal. @param signo Signal number (always SIGTERM, ignored). */ static void sig_handler_term(int signo) { *sig_pass_pointer(&sig_had_term) = 1; } /** Read value from volatile atomic type. This function is necessary as some compilers mis-optimize direct access to volatile variables (at least if you believe one of the coding standards). @param ap Pointer to volatile atomic variable. @return Contents of the variable. */ static dk4_sig_atomic_t sig_read_atomic(DK4_VOLATILE dk4_sig_atomic_t *ap) { return (*ap); } /** Check whether we can continue or must abort due to signal. @param dopipe Flag: Check for SIGPIPE too. @return 1 if we can continue, 0 otherwise. */ static int can_continue(int dopipe) { int back = 1; if (0 != sig_read_atomic(&sig_had_term)) { back = 0; } if (0 != sig_read_atomic(&sig_had_int)) { back = 0; } #ifdef SIGPIPE if (0 != dopipe) { if (0 != sig_read_atomic(&sig_had_pipe)) { back = 0; } } #endif return back; } /** Log a timestamp. @return 1 if time changed since previous log, 0 otherwise. */ static int log_timestamp(void) { char buf[64]; static dk4_time_t previous_log = (dk4_time_t)0UL; dk4_time_t current; int back = 0; $? "+ log_timestamp" dk4time_get(¤t); if (current != previous_log) { $? ". time changed" if (dk4time_as_text_c8(buf, sizeof(buf), ¤t, NULL)) { back = 1; $? ". time converted to text" fputs(pjsnmp_kw[62], out_file); fputs(buf, out_file); fputs(pjsnmp_kw[0], out_file); previous_log = current; } } $? "- log_timestamp %d", back return back; } /** Log a 1 component message with header line. @param h Header line chooser (1, 2 or 3). @param i Index of message text in pjsnmp_kw. */ static void log_1_with_header(int h, size_t i) { (void)log_timestamp(); if (last_header != h) { fputs(pjsnmp_kw[62 + h], out_file); last_header = h; } fputs(pjsnmp_kw[i], out_file); fflush(out_file); } /** Log a 3 components message with header line. @param h Header line chooser (1, 2 or 3). @param i1 Index of first text component in pjsnmp_kw. @param i2 Index of third text component in pjsnmp_kw. @param s Second text component (variable text). */ static void log_3_with_header(int n, size_t i1, size_t i2, const char *s) { (void)log_timestamp(); if (last_header != n) { fputs(pjsnmp_kw[62 + n], out_file); last_header = n; } fputs(pjsnmp_kw[i1], out_file); if (NULL != s) { fputs(s, out_file); } fputs(pjsnmp_kw[i2], out_file); fflush(out_file); } /** Log a 5 components message with header line. @param h Header line chooser (1, 2 or 3). @param i1 Index of first text component in pjsnmp_kw. @param i2 Index of third text component in pjsnmp_kw. @param i3 Index of fifth text component in pjsnmp_kw. @param s1 Second text component (variable text). @param s2 Fourth text component (variable text). */ static void log_5_with_header( int n, size_t i1, size_t i2, size_t i3, const char *s1, const char *s2 ) { (void)log_timestamp(); if (last_header != n) { fputs(pjsnmp_kw[62 + n], out_file); last_header = n; } fputs(pjsnmp_kw[i1], out_file); if (NULL != s1) { fputs(s1, out_file); } fputs(pjsnmp_kw[i2], out_file); if (NULL != s2) { fputs(s2, out_file); } fputs(pjsnmp_kw[i3], out_file); fflush(out_file); } /** Log a 1 component message without header line. @param i Index of text component in pjsnmp_kw. */ static void log_1_no_header(size_t i) { $? "+ log_1_no_header %u", (unsigned)i log_timestamp(); fputs(pjsnmp_kw[i], out_file); fflush(out_file); $? "- log_1_no_header" } /** Log a 3 components message without header line. @param i1 Index of first text component in pjsnmp_kw. @param i2 Index of third text component in pjsnmp_kw. @param s Second text component (variable text). */ static void log_3_no_header(size_t i1, size_t i2, const char *s) { $? "+ log_3_no_header %u %u \"%s\"", (unsigned)i1, (unsigned)i2, TR_8STR(s) log_timestamp(); fputs(pjsnmp_kw[i1], out_file); if (NULL != s) { fputs(s, out_file); } fputs(pjsnmp_kw[i2], out_file); fflush(out_file); $? "- log_3_no_header" } /** Log a 5 components message without header line. @param i1 Index of first text component in pjsnmp_kw. @param i2 Index of third text component in pjsnmp_kw. @param i3 Index of fifth text component in pjsnmp_kw. @param s1 Second text component (variable text). @param s2 Fourth text component (variable text). */ static void log_5_no_header( size_t i1, size_t i2, size_t i3, const char *s1, const char *s2 ) { log_timestamp(); fputs(pjsnmp_kw[i1], out_file); if (NULL != s1) { fputs(s1, out_file); } fputs(pjsnmp_kw[i2], out_file); if (NULL != s2) { fputs(s2, out_file); } fputs(pjsnmp_kw[i3], out_file); fflush(out_file); } /** Log a 1 component message related to model file. @param lineno Line number in model file to complain about. @param i Index of first text component in pjsnmp_kw. */ static void log_model_1(dk4_um_t lineno, size_t i) { char buf[8*sizeof(dk4_um_t)]; const char *fn; const char *ptr; int res; (void)log_timestamp(); if (3 != last_header) { last_header = 3; fputs(pjsnmp_kw[65], out_file); } if ((dk4_um_t)0UL < lineno) { res = dk4ma_write_c8_decimal_unsigned(buf, sizeof(buf), lineno, 0, NULL); if (0 != res) { fn = ((NULL != model_file) ? model_file : def_model_file); ptr = dk4str8_rchr(fn, '/'); if (NULL != ptr) { ptr++; } else { ptr = fn; } fputs(ptr, out_file); fputs(pjsnmp_kw[143], out_file); fputs(buf, out_file); fputs(pjsnmp_kw[143], out_file); fputs(pjsnmp_kw[1], out_file); } } fputs(pjsnmp_kw[i], out_file); fflush(out_file); } /** Log a 3 components message related to model file. @param lineno Line number in model file to complain about. @param i1 Index of first text component in pjsnmp_kw. @param i2 Index of third text component in pjsnmp_kw. @param s Second text component (variable text). */ static void log_model_3( dk4_um_t lineno, size_t i1, size_t i2, const char *s ) { char buf[8*sizeof(dk4_um_t)]; const char *fn; const char *ptr; int res; (void)log_timestamp(); if (3 != last_header) { last_header = 3; fputs(pjsnmp_kw[65], out_file); } if ((dk4_um_t)0UL < lineno) { res = dk4ma_write_c8_decimal_unsigned(buf, sizeof(buf), lineno, 0, NULL); if (0 != res) { fn = ((NULL != model_file) ? model_file : def_model_file); ptr = dk4str8_rchr(fn, '/'); if (NULL != ptr) { ptr++; } else { ptr = fn; } fputs(ptr, out_file); fputs(pjsnmp_kw[143], out_file); fputs(buf, out_file); fputs(pjsnmp_kw[143], out_file); fputs(pjsnmp_kw[1], out_file); } } fputs(pjsnmp_kw[i1], out_file); if (NULL != s) { fputs(s, out_file); } fputs(pjsnmp_kw[i2], out_file); fflush(out_file); } /** Create new status text node. @param t Text, not 0-terminated. @param tsz Text size. @param st Summary state if text is found. @param fl Flag: This is the start text only. @return Valid pointer on success, NULL on error. */ static status_text_t * stt_new( const unsigned char *t, size_t tsz, int st, int fl ) { status_text_t *back = NULL; $? "+ stt_new %s %u state=%d start=%d", TR_8PTR(t), (unsigned)tsz, st, fl if ((NULL != t) && (0 < tsz)) { back = dk4mem_new(status_text_t,1,NULL); if (NULL != back) { DK4_MEMRES(back, sizeof(status_text_t)); back->start = fl; back->state = st; back->size = tsz; back->text = dk4mem_new(unsigned char,tsz,NULL); if (NULL != back->text) { DK4_MEMCPY(back->text, t, tsz); } else { $? "! second allocation" dk4mem_free(back); back = NULL; } } #if TRACE_DEBUG else { $? "! first allocation" } #endif } #if TRACE_DEBUG else { $? "! args" } #endif $? "- stt_new %s", TR_8PTR(back) return back; } /** Destroy status text node, release memory. @param ptr Node to destroy. */ static void stt_delete(status_text_t *ptr) { $? "+ stt_delete %s", TR_8PTR(ptr) if (NULL != ptr) { $? ". lgt=%u", (unsigned)(ptr->size) dk4mem_release(ptr->text); dk4mem_free(ptr); } $? "- stt_delete" } /** Compare two status text nodes. While building the storage the start flag is ignored. When searching a text for a status text received via SNMP we have to consider the flag. @param l Left object. @param r Right object. @param cr Comparison criteria (0=build storage, 1=search). @return Comparison result. */ static int stt_compare(const void *l, const void *r, int cr) { const status_text_t *pl; /* Left operand */ const status_text_t *pr; /* Right operand */ size_t min; /* Minimum of both text sizes */ int back = 0; $? "+ stt_compare cr=%d", cr if (NULL != l) { if (NULL != r) { pl = (const status_text_t *)l; pr = (const status_text_t *)r; min = pl->size; if (min > pr->size) { min = pr->size; } if (0 < min) { back = dk4mem_cmp(pl->text, pr->text, min, NULL); } if (0 == back) { if ((0 == cr) || (0 == pl->start)) { if (pl->size > pr->size) { back = 1; } else { if (pl->size < pr->size) { back = -1; } } } } } else { back = 1; } } else { if (NULL != r) { back = -1; } } $? "- stt_compare %d", back return back; } /** Set one command line option. @param Option character. @param Option argument. */ static void set_option(char c, char *val) { size_t sz = 0; /* Index of option in arguments array */ $? "+ set_option %c %s", c, TR_8STR(val) if (NULL != val) { if (('a' <= c) && ('z' >= c)) { sz = (size_t)(c - 'a'); if (DK4_SIZEOF(lower,DK4_PCHAR) > sz) { lower[sz] = val; $? ". ok" } } else { if (('A' <= c) && ('Z' >= c)) { sz = (size_t)(c - 'A'); if (DK4_SIZEOF(upper,DK4_PCHAR) > sz) { upper[sz] = val; $? ". ok" } } } } $? "- set_option" } /** Retrieve argument for one option. @param c Option character (option name). @return The value provided as option argument, NULL otherwise. */ static char * get_option(char c) { char *back = NULL; size_t sz = 0; /* Index of option in arguments array */ $? "- get_option %c", c if (('a' <= c) && ('z' >= c)) { sz = (size_t)(c - 'a'); if (DK4_SIZEOF(lower,DK4_PCHAR) > sz) { back = lower[sz]; } } else { if (('A' <= c) && ('Z' >= c)) { sz = (size_t)(c - 'A'); if (DK4_SIZEOF(upper,DK4_PCHAR) > sz) { back = upper[sz]; } } } $? "- get_option \"%s\"", TR_8STR(back) return back; } /** Process command line arguments. @param argc Number of command line arguments. @param argv Command line arguments array. @return 1 on success, 0 on error. */ static int process_command_line_arguments(int argc, char *argv[]) { char *curarg; /* Current argument to process */ char *s_name; /* Status file name */ size_t szjnb; /* Size of job name buffer */ int back = 1; int i; /* Index of current argument */ int res; /* Result for number to text conversion */ char c; /* Option name */ $? "+ process_command_line_arguments" for (i = 1; i < argc; i++) { curarg = argv[i]; $? ". argv[%d]=\"%s\"", i, curarg if ('-' == *curarg) { $? ". option" curarg++; if ('\0' != *curarg) { $? ". option name" c = *(curarg++); if ('\0' != *curarg) { $? ". option argument" set_option(c, curarg); } else { $? "! missing option argument" #if VERSION_BEFORE_20160208 /* -c may appear without an argument. */ back = 0; /* ERROR: Missing option argument */ log_1_with_header(1, 67); #else set_option(c, (char *)(pjsnmp_kw[144])); #endif } } else { $? "! missing option name" back = 0; /* ERROR: Only minus */ log_1_with_header(1, 66); } } else { $? "! not an option \"%s\"", curarg #if 0 /* 2016-02-15 Seeing non-option arguments is not an error, the accounting file name is passed once as argument to -a and once as file name. */ back = 0; /* ERROR: Non-option argument found */ log_1_with_header(1, 68); #endif } } if (0 != back) { $? ". options processed" s_name = get_option('s'); if (NULL != s_name) { $? ". status file name ok" stt_file = fopen(s_name, "w"); if (NULL != stt_file) { $? ". status file opened" out_file = stt_file; } #if TRACE_DEBUG else { $? "! failed to open status file" } #endif } #if TRACE_DEBUG else { $? "! no status file name" } #endif user_name = get_option('n'); if (NULL == user_name) user_name = get_option('L'); if (NULL != user_name) { user_name = dk4str8_start(user_name, NULL); } if (NULL != user_name) { (void)dk4str8_next(user_name, NULL); if (0 == strlen(user_name)) { user_name = NULL; } } if (NULL == user_name) { $? "! missing user name" /* ERROR: No user name */ log_1_with_header(1, 69); back = 0; } #if TRACE_DEBUG else { $? ". user \"%s\"", user_name } #endif if (0 != back) { $? ". user name ok" queue_name = get_option('P'); if (NULL == queue_name) queue_name = get_option('Q'); if (NULL != queue_name) { queue_name = dk4str8_start(queue_name, NULL); } if (NULL != queue_name) { (void)dk4str8_next(queue_name, NULL); if (0 == strlen(queue_name)) { queue_name = NULL; } } if (NULL == queue_name) { $? "! missing queue name" /* ERROR: No queue name */ log_1_with_header(1, 70); back = 0; } #if TRACE_DEBUG else { $? ". queue \"%s\"", queue_name } #endif } if (0 != back) { job_name = get_option('A'); $? ". check job name" if (NULL == job_name) job_name = get_option('j'); if (NULL == job_name) job_name = get_option('t'); if (NULL == job_name) job_name = get_option('D'); if (NULL != job_name) { job_name = dk4str8_start(job_name, NULL); } if (NULL != job_name) { (void)dk4str8_next(job_name, NULL); if (0 == strlen(job_name)) { job_name = NULL; } } if (NULL == job_name) { szjnb = sizeof(job_name_buf); if (0 != dk4str8_cpy_s(job_name_buf, szjnb, pjsnmp_kw[171], NULL)) { res = dk4ma_write_c8_decimal_unsigned( &(job_name_buf[strlen(job_name_buf)]), (szjnb - strlen(job_name_buf)), (dk4_um_t)getpid(), 0, NULL ); if (0 != res) { job_name = job_name_buf; } } } if (NULL == job_name) { $? "! missing job name" /* ERROR: No job name */ log_1_with_header(1, 71); back = 0; } #if TRACE_DEBUG else { $? ". job \"%s\"", job_name } #endif } if (0 != back) { $? ". checking job title" job_title = get_option('J'); if (NULL == job_title) job_title = get_option('f'); if (NULL == job_title) job_title = get_option('N'); if (NULL != job_title) { job_title = dk4str8_start(job_title, NULL); } if (NULL != job_title) { dk4str8_normalize(job_title, NULL); } #if 0 /* 20160206 Allow job_title to be NULL, pjsnmp_kw[54] is used instead. */ if (NULL == job_title) { $? "! missing job title" back = 0; } #endif #if TRACE_DEBUG else { $? ". title \"%s\"", job_title } #endif } } $? "- process_command_line_arguments %d", back return back; } /** Set string pointer. @param dptr Address of pointer to set. @param src Value. @param name Configuration entry name. @return 1 on success, 0 on error. */ static int set_string(char **dptr, char *src, const char *name) { int back = 0; $? "+ set_string" if (NULL != src) { src = dk4str8_start(src, NULL); if (NULL != src) { $? ". src ok" if (NULL != *dptr) { $? ". redefined" /* WARNING: Redefinition */ log_5_with_header(2, 72, 73, 74, name, src); } *dptr = src; back = 1; } else { $? "! empty" /* ERROR: Empty text */ log_3_with_header(2, 75, 76, name); } } else { $? "! NULL pointer" /* ERROR: Text string required */ log_3_with_header(2, 77, 78, name); } $? "- set_string %d", back return back; } /** Set unsigned short variable. @param dptr Address of variable to set. @param src Text containing the value. @return 1 on success, 0 on error. */ static int set_unsigned_short(unsigned short *dptr, char *src, const char *name) { const char *ep = NULL; /* First unprocessabe char */ int back = 0; int res; /* Operation result */ unsigned short us; $? "+ set_unsigned_short %s", TR_8PTR(src) if (NULL != src) { src = dk4str8_start(src, NULL); if (NULL != src) { res = dk4ma_input_c8_dec_ushort(&us, src, &ep, 1, NULL); if (0 != res) { *dptr = us; back = 1; $? ". setting to %u", (unsigned)us } else { $? "! not a number" /* ERROR: Not a number! */ log_5_with_header(2, 79, 80, 81, name, src); } } else { $? "! empty text" /* ERROR: Empty text */ log_3_with_header(2, 75, 76, name); } } else { $? "! NULL" /* ERROR: Numeric value required */ log_3_with_header(2, 77, 78, name); } $? "- set_unsigned_short %d", back return back; } /** Set SNMP version. @param dptr Address of variable to set. @param src Text containing the SNMP version. @return 1 on success, 0 on error. */ static int set_snmp_version(long *dptr, char *src) { int back = 0; $? "+ set_snmp_version %s", TR_8PTR(src) if (NULL != src) { src = dk4str8_start(src, NULL); if (NULL != src) { switch (dk4str8_array_index(snmp_version_names, src, 0)) { case 0: { $? ". 1" *dptr = SNMP_VERSION_1; back = 1; } break; case 1: case 2: { $? ". 2c" *dptr = SNMP_VERSION_2c; back = 1; } break; case 3: { $? ". 3" *dptr = SNMP_VERSION_3; back = 1; } break; default: { $? "! illegal text" /* ERROR: Illegal SNMP version */ log_3_with_header(2, 82, 83, src); } break; } } else { $? "! empty" /* ERROR: Empty text */ log_3_with_header(2, 75, 76, printcap_entry_names[9]); } } else { $? "! NULL" /* ERROR: Value required */ log_3_with_header(2, 77, 78, printcap_entry_names[9]); } $? "- set_snmp_version %d", back return back; } /** Set maximum unsigned integer variable from text. @param dptr Address of destination variable. @param src Text containing the value. @param name Setting name for diagnostics. @return 1 on success, 0 on error. */ static int set_umax(dk4_um_t *dptr, char *src, const char *name) { const char *ep = NULL; /* First unprocessable character */ dk4_um_t i = 0UL; /* Result value */ int back = 0; int res; /* Operation result */ $? "+ set_umax %s", TR_8PTR(src) if (NULL != src) { res = dk4ma_input_c8_dec_dk4_um_t(&i, src, &ep, 1, NULL); if (0 != res) { *dptr = i; $? ". value" back = 1; } else { $? "! not a number" /* ERROR: Not a numeric value */ log_5_with_header(2, 84, 161, 86, name, src); } } else { $? "! NULL" /* ERROR: Value required */ log_3_with_header(2, 77, 78, name); } $? "- set_umax %d", back return back; } #if 0 /** Set integer variable from text. @param dptr Address of destination variable. @param src Text containing the value. @param name Setting name for diagnostics. @return 1 on success, 0 on error. */ static int set_integer(int *dptr, char *src, const char *name) { const char *ep = NULL; /* First unprocessable character */ int i = 0; /* Result value */ int back = 0; int res; /* Operation result */ $? "+ set_integer %s", TR_8PTR(src) if (NULL != src) { res = dk4ma_input_c8_dec_int(&i, src, &ep, 1, NULL); if (0 != res) { *dptr = i; $? ". value %d", i back = 1; } else { $? "! not a number" /* ERROR: Not a numeric value */ log_5_with_header(2, 84, 85, 86, name, src); } } else { $? "! NULL" /* ERROR: Value required */ log_3_with_header(2, 77, 78, name); } $? "- set_integer %d", back return back; } #endif /** Process one configuration item from a printcap entry. @param ci Item to process. @return 1 on success, 0 on error. */ static int process_one_config_item(char *ci) { char *value = NULL; /* Value for config item */ int btrue = 1; /* Condition */ int back = 1; $? "+ process_one_config_item \"%s\"", TR_8STR(ci) value = dk4str8_chr(ci, '='); if (NULL != value) { *(value++) = '\0'; value = dk4str8_start(value, NULL); $? "\"%s\"=\"%s\"", ci, TR_8STR(value) } else { value = dk4str8_chr(ci, '@'); if (NULL != value) { $? ". negative condition" *value = '\0'; btrue = 0; } #if TRACE_DEBUG else { $? ". positive condition" } #endif } switch (dk4str8_array_index(printcap_entry_names, ci, 0)) { case 0: { $? ". host" if (0 == set_string(&host_name, value, printcap_entry_names[0])) { back = 0; } } break; case 1: { $? ". port" if (0 == set_unsigned_short(&host_port, value, printcap_entry_names[1])) { back = 0; } } break; case 2: { $? ". orderly release" host_ordr = btrue; } break; case 3: { $? ". enable accounting" acct_enab = btrue; } break; case 4: { $? ". enable quota check" acct_achk = btrue; } break; case 5: { $? ". accounting file" if (0 == set_string(&acct_name, value, printcap_entry_names[5])) { back = 0; } } break; case 6: { $? ". accounting host" if (0 == set_string(&acct_host, value, printcap_entry_names[6])) { back = 0; } } break; case 7: { $? ". accounting port" if (0 == set_unsigned_short(&acct_port, value, printcap_entry_names[7])) { back = 0; } } break; case 8: { $? ". timeout if not printing" if (NULL != value) { if (0 == set_umax(&print_timeout, value, printcap_entry_names[8])) { back = 0; } } else { if (0 == btrue) { print_timeout = (dk4_um_t)0UL; } else { back = 0; } } } break; case 9: { $? ". SNMP version" if (0 == set_snmp_version(&snmp_version, value)) { back = 0; } } break; case 10: { $? ". SNMP community" if (0 == set_string(&snmp_comm, value, printcap_entry_names[10])) { back = 0; } } break; case 11: { $? ". SNMP during transfer" check_trans = btrue; } break; case 12: { $? ". model file" if (0 == set_string(&model_file, value, printcap_entry_names[12])) { back = 0; } } break; case 13: { $? ". model name" if (0 == set_string(&model_name, value, printcap_entry_names[13])) { back = 0; } } break; case 14: { $? ". ignore failed session" snmp_igfs = btrue; } break; case 15: { $? ". allow printing when accounting down" acct_apcf = btrue; } break; case 16: { $? ". always use printer detected error state" pdes_always = btrue; } break; case 17: { $? ". report unknown as busy" unkn_busy = btrue; } break; case 18: { $? ". timeout for unreachable at start" if (NULL != value) { if (0 == set_umax(&unto_start, value, printcap_entry_names[18])) { back = 0; } } else { if (0 == btrue) { unto_start = (dk4_um_t)0UL; } else { back = 0; } } } break; case 19: { $? ". timeout for unreachable at end" if (NULL != value) { if (0 == set_umax(&unto_end, value, printcap_entry_names[19])) { back = 0; } } else { if (0 == btrue) { unto_end = (dk4_um_t)0UL; } else { back = 0; } } } break; case 20: { $? ". timeout for unreachable during transfer" if (NULL != value) { if (0 == set_umax(&unto_print, value, printcap_entry_names[20])) { back = 0; } } else { if (0 == btrue) { unto_print = (dk4_um_t)0UL; } else { back = 0; } } } break; case 21: { $? ". local port number" if (0 == set_unsigned_short(&local_port, value, printcap_entry_names[21])) { back = 0; $? ". back = 0" } } break; case 22: { $? ". timeout for unwriteable during transfer" if (NULL != value) { if (0 == set_umax(&unwt_print, value, printcap_entry_names[22])) { back = 0; } } else { if (0 == btrue) { unwt_print = (dk4_um_t)0UL; } else { $? ". back = 0" back = 0; } } } break; case 23: { $? ". read printer responses" read_responses = btrue; } break; case 24: { $? ". force printable status text" force_printable_status_text = btrue; } break; case 25: { if (NULL != value) { if (0 == set_umax(&erto_start, value, printcap_entry_names[22])) { back = 0; } } else { if (0 == btrue) { erto_start = (dk4_um_t)0UL; } else { $? ". back = 0" back = 0; } } } break; case 26: { if (NULL != value) { if (0 == set_umax(&erto_end, value, printcap_entry_names[22])) { back = 0; } } else { if (0 == btrue) { erto_end = (dk4_um_t)0UL; } else { $? ". back = 0" back = 0; } } } break; case 27: { if (NULL != value) { if (0 == set_umax(&erto_print, value, printcap_entry_names[22])) { back = 0; } } else { if (0 == btrue) { erto_print = (dk4_um_t)0UL; } else { $? ". back = 0" back = 0; } } } break; case 28: { verbose = btrue; } break; case 29: { allow_non_ascii = btrue; } break; case 30: { acct_start_end = btrue; } break; } $? "- process_one_config_item %d", back return back; } /** Check whether the line opens the model we are searching for. @param str Text to check. @param bptr Address of success indicator. @return 1 on success, 0 on error. */ static int check_in_model(char *str, int *bptr, dk4_um_t lineno) { char *ptr; /* Closing square bracket */ int back = 0; $? "+ check_in_model" str = dk4str8_start(str, NULL); if (NULL != str) { ptr = dk4str8_chr(str, ']'); if (NULL != ptr) { *ptr = '\0'; dk4str8_normalize(str, NULL); $? ". model \"%s\"", str if (0 == strcmp(str, model_name)) { back = 1; $? ". model found" } #if TRACE_DEBUG else { $? ". not the requested model" } #endif } else { $? "! no closing bracket" /* ERROR: Syntax */ log_model_1(lineno, 87); } } else { $? "! no text" /* ERROR: Syntax */ log_model_1(lineno, 88); } $? "- check_in_model %d", back return back; } /** Overwrite one mapping from device/printer status to summary state. @param devst Device status. @param prst Printer status. @param sujmst Summary state resulting from device and printer status. */ static void set_state_mapping(int devst, int prst, int sumst) { $? "+ set_state_mapping %d/%d %d", devst, prst, sumst if ((0 < devst) && (6 > devst)) { if ((0 < prst) && (6 > prst)) { $? ". ok" summary_states[5 * devst + prst - 6] = sumst; } #if TRACE_DEBUG else { $? "! prst %d", prst } #endif } #if TRACE_DEBUG else { $? "! devst %d", devst } #endif $? "- set_state_mapping" } /** Map device and printer status to summary state. @param devst Device status. @param prst Printer status. @return Summary state. */ static int get_state_mapping(int devst, int prst) { int back = PJSNMP_ST_UNKNOWN; $? "+ get_state_mapping %d/%d", devst, prst if ((0 < devst) && (6 > devst)) { if ((0 < prst) && (6 > prst)) { $? ". ok" back = summary_states[5 * devst + prst - 6]; $? ". %s/%s => %s", pjsnmp_kw[11 + devst], pjsnmp_kw[17 + prist], pjsnmp_kw[23 + back] } #if TRACE_DEBUG else { $? "! prst %d", prst } #endif } #if TRACE_DEBUG else { $? "! devst %d", devst } #endif $? "- get_state_mapping %d", back return back; } /** Add a state mapping. @param str Mapping text. @param lineno Line number (for diagnostics). @return 1 on success, 0 on error. */ static int model_conf_map(char *str, dk4_um_t lineno) { char *psummary; /* Summary state name */ char *ppr; /* Printer status */ int stsum; /* Summary state */ int stdev; /* Device status */ int stpri; /* Printer status */ int back = 0; $? "+ model_conf_map %s", TR_8STR(str) psummary = dk4str8_next(str, NULL); if (NULL != psummary) { $? ". summary" ppr = dk4str8_chr(str, '/'); if (NULL != ppr) { $? ". ppr" *(ppr++) = '\0'; ppr = dk4str8_start(ppr, NULL); if (NULL != ppr) { $? ". ppr" back = 1; switch (dk4str8_abbr_index(summary_state_names, '$', psummary, 0)) { case 0: { $? ". unknown" stsum = PJSNMP_ST_UNKNOWN; } break; case 1: { $? ". error" stsum = PJSNMP_ST_ERROR; } break; case 2: { $? ". busy" stsum = PJSNMP_ST_BUSY; } break; case 3: { $? ". idle" stsum = PJSNMP_ST_IDLE; } break; case 4: { $? ". warmup" stsum = PJSNMP_ST_WARMUP; } break; case 5: { $? ". standby" stsum = PJSNMP_ST_STANDBY; } break; default: { $? "! illegal summary" back = 0; /* ERROR: Illegal summary state */ log_model_3(lineno, 89, 90, psummary); } break; } if (0 != back) { $? ". summary ok" if (0 == strcmp(str, pjsnmp_kw[3])) { if (0 == strcmp(ppr, pjsnmp_kw[3])) { $? ". */*" for (stdev = 1; stdev < 6; stdev++) { for (stpri = 1; stpri < 6; stpri++) { set_state_mapping(stdev, stpri, stsum); } } } else { $? ". */x" stpri = dk4str8_abbr_index(printer_status_names, '$', ppr, 0); if (-1 < stpri) { stpri++; $? ". stpri %d", stpri for (stdev = 1; stdev < 6; stdev++) { set_state_mapping(stdev, stpri, stsum); } } else { $? "! illegal printer status" back = 0; /* ERROR: Syntax, illegal printer status name */ log_model_3(lineno, 91, 92, ppr); } } } else { stdev = dk4str8_abbr_index(device_status_names, '$', str, 0); if (-1 < stdev) { stdev++; $? ". stdev %d", stdev if (0 == strcmp(ppr, pjsnmp_kw[3])) { $? ". x/*" for (stpri = 1; stpri < 6; stpri++) { set_state_mapping(stdev, stpri, stsum); } } else { $? ". x/x" stpri = dk4str8_abbr_index(printer_status_names, '$', ppr, 0); if (-1 < stpri) { stpri++; $? ". stpri %d", stpri set_state_mapping(stdev, stpri, stsum); } else { $? "! illegal printer status" back = 0; /* ERROR: Syntax, illegal printer status name */ log_model_3(lineno, 91, 92, ppr); } } } else { $? "! illegal device status" back = 0; /* ERROR: Syntax, illegal device status name */ log_model_3(lineno, 93, 94, ppr); } } } else { $? "! summary state not found" } } else { $? "! printer status missing" /* ERROR: Syntax, printer status missing */ log_model_1(lineno, 95); } } else { $? "! printer status missing" /* ERROR: Syntax, printer status missing */ log_model_1(lineno, 95); } } else { $? "! summary missing" /* ERROR: Syntax, summary state missing */ log_model_1(lineno, 96); } $? "- model_conf_map %d", back return back; } /** Add a condition to the error conditions list. @param str Text for condition. @param addcond Flag: Add to existing list, do not reset. @param lineno Line number (for diagnostics). @return 1 on success, 0 on error. */ static int model_conf_error_condition(char *str, int addcond, dk4_um_t lineno) { unsigned long cond; /* Used to build condition bit */ int i; /* Index of keyword in condition_names */ int back = 0; $? "+ model_conf_error_condition %s", TR_8STR(str) if ((0 == addcond) && (0 == pdes_reset)) { pdes_error = 0UL; $? ". reset pdes error" } pdes_reset = 1; dk4str8_normalize(str, NULL); i = dk4str8_array_index(condition_names, str, 0); $? ". i %d", i if (-1 < i) { back = 1; cond = 0x80000000UL; while (0 < i--) { cond = cond / 2UL; } pdes_error |= cond; $? ". cond %lx", cond } else { $? "! illegal condition" /* ERROR: Syntax, illegal condition name */ log_model_3(lineno, 97, 98, str); } $? "- model_conf_error_condition %d", back return back; } /** Set status text OID. @param str Text containing the OID. @param lineno Line number (for diagnostics). @return 1 on success, 0 on error. */ static int model_conf_status_oid(char *str, dk4_um_t lineno) { const char *ep; /* First unprocessable character */ char *np; /* Next part */ dk4_um_t num; /* Numeric value for sub-id */ size_t used = 0; /* Number of used sub-ids */ int back = 1; int res; /* Operation result */ $? "+ model_conf_status_oid" dk4str8_normalize(str, NULL); if ('.' == *str) { $? ". skip leading dot" str++; } while ((NULL != str) && (1 == back)) { np = dk4str8_chr(str, '.'); if (NULL != np) { *(np++) = '\0'; } $? ". current=\"%s\" next=\"%s\"", str, TR_8STR(np) ep = NULL; res = dk4ma_input_c8_dec_dk4_um_t(&num, str, &ep, 1, NULL); if (0 != res) { $? ". number %lu", (unsigned long)num if ((dk4_um_t)(MAX_SUBID) >= num) { $? ". in range" if (used < DK4_SIZEOF(oid_st,oid)) { $? ". length ok" oid_st[used++] = (oid)num; } else { $? "! too long" /* ERROR: Syntax, OID too long */ log_model_1(lineno, 99); } } else { $? "! numeric overflow" back = 0; /* ERROR: Syntax, overflow */ log_model_3(lineno, 100, 101, str); } } else { $? "! not a number" back = 0; /* ERROR: Syntax, not a number */ log_model_3(lineno, 102, 103, str); } str = np; } if (0 == used) { $? "! empty text" back = 0; /* ERROR: Syntax, empty OID */ log_model_1(lineno, 104); } if (0 != back) { sz_oid_st = used; } else { sz_oid_st = 0; } by_oid_st = sz_oid_st * sizeof(oid); $? "- model_conf_status_oid %d", back return back; } /** Remove final double quote from string. @param str String to modify. @return 1 on success, 0 on error. */ static int remove_final_double_quote(char *str) { char *ptr; /* Position of final double quote */ int back = 0; $? "+ remove_final_double_quote \"%s\"", TR_8STR(str) ptr = dk4str8_rchr(str, '"'); if (NULL != ptr) { $? ". closing double quote found" *(ptr++) = '\0'; back = 1; while ('\0' != *ptr) { if (' ' != *ptr) { if ('\t' != *ptr) { if ('\r' != *ptr) { if ('\n' != *ptr) { back = 0; $? "! non-whitespace after string" } } } } ptr++; } } #if TRACE_DEBUG else { $? "! no closing double quote" } #endif $? "- remove_final_double_quote %d \"%s\"", back, TR_8STR(str) return back; } /** Add a text indicating a state to the storage. @param str Indicator text. @param state State number indicated by the text. @param start Flag: This text is just the start of the real text. @param lineno Line number (for diagnostics). @return 1 on success, 0 on error. */ static int model_conf_state_text(char *str, int state, int start, dk4_um_t lineno) { status_text_t *stptr; /* New configuration node to add */ const char *ep; /* First unprocessable char when reading number */ char *cptr; /* Current word to process */ char *nptr; /* Next word (remaining text) to process */ size_t sl; /* String length */ int res; /* Operation result */ int back = 0; unsigned char uc; /* Current byte found */ $? "+ model_conf_state_text %s", TR_8STR(str) if (NULL == s_stn) { s_stn = dk4sto_open(NULL); if (NULL != s_stn) { dk4sto_set_comp(s_stn, stt_compare, 0); } } if ((NULL != s_stn) && (NULL == i_stn)) { i_stn = dk4sto_it_open(s_stn, NULL); } if ((NULL != s_stn) && (NULL != i_stn)) { $? ". allocations ok" if (NULL != str) { $? ". have text" if ('"' == *str) { $? ". double quoted string" str++; if (0 != remove_final_double_quote(str)) { sl = strlen(str); if (0 < sl) { $? ". string not empty" stptr = stt_new((unsigned char *)str, sl, state, start); if (NULL != stptr) { $? ". allocation ok" if (0 != dk4sto_add(s_stn, stptr, NULL)) { back = 1; $? ". added to storage" } else { $? "! allocation" /* ERROR: Allocation failed */ log_model_1(lineno, 105); stt_delete(stptr); } } else { $? "! allocation" /* ERROR: Allocation failed */ log_model_1(lineno, 105); } } else { $? "! empty" /* ERROR: Syntax, string empty */ log_model_1(lineno, 106); } } else { $? "! closing double quote" /* ERROR: Syntax, closing double quote missing or text after */ log_model_1(lineno, 107); } } else { $? ". hex sequence" sl = 0; cptr = str; back = 1; while ((NULL != cptr) && (0 != back)) { nptr = dk4str8_next(cptr, NULL); ep = NULL; $? ". current=\"%s\" next=\"%s\"", cptr, TR_8STR(nptr) res = dk4ma_input_c8_hex_uchar(&uc, cptr, &ep, 1, NULL); if (0 != res) { str[sl++] = (char)uc; $? ". hex byte ok" } else { back = 0; $? "! not a hex byte" /* ERROR: Syntax, not a hexadecimal byte */ log_model_3(lineno, 108, 109, cptr); } cptr = nptr; } if (0 != back) { if (0 < sl) { $? ". string ok" stptr = stt_new((unsigned char *)str, sl, state, start); if (NULL != stptr) { $? ". allocation ok" if (0 == dk4sto_add(s_stn, stptr, NULL)) { back = 0; $? "! allocation" stt_delete(stptr); /* ERROR: Allocation failed */ log_model_1(lineno, 105); } #if TRACE_DEBUG else { $? ". added to storage" } #endif } else { $? "! allocation" back = 0; /* ERROR: Memory allocation failed */ log_model_1(lineno, 105); } } else { $? "! empty string" /* ERROR: Syntax, empty string */ log_model_1(lineno, 106); } } } } else { $? "! NULL" /* ERROR: Value required */ log_model_1(lineno, 110); } } else { $? "! memory allocation" /* ERROR: Memory allocation failed */ log_model_1(lineno, 105); } $? "- model_conf_state_text %d", back return back; } /** Process one configuration line from model file. @param str Line to process. @param lineno Line number in the file (for diagnostics). @return 1 on success, 0 on error. */ static int process_model_line(char *str, dk4_um_t lineno) { char *valptr; /* Value in line */ int back = 0; $? "+ process_model_line \"%s\"", TR_8STR(str) valptr = dk4str8_chr(str, '='); if (NULL != valptr) { $? ". value ok" *(valptr++) = '\0'; dk4str8_normalize(str, NULL); valptr = dk4str8_start(valptr, NULL); if (NULL != valptr) { $? ". value contains text" switch (dk4str8_array_index(model_keys, str, 0)) { case 0: { back = model_conf_map(valptr, lineno); } break; case 1: { back = model_conf_error_condition(valptr, 0, lineno); } break; case 2: { back = model_conf_error_condition(valptr, 1, lineno); } break; case 3: { back = model_conf_status_oid(valptr, lineno); } break; case 4: { back = model_conf_state_text(valptr, PJSNMP_ST_STANDBY, 0, lineno); } break; case 5: { back = model_conf_state_text(valptr, PJSNMP_ST_STANDBY, 1, lineno); } break; case 6: { back = model_conf_state_text(valptr, PJSNMP_ST_IDLE, 0, lineno); } break; case 7: { back = model_conf_state_text(valptr, PJSNMP_ST_IDLE, 1, lineno); } break; case 8: { back = 1; if (NULL != valptr) { valptr = dk4str8_start(valptr, NULL); if (NULL != valptr) { dk4str8_normalize(valptr, NULL); if (dk4str8_is_bool(valptr)) { stat_always = dk4str8_is_on(valptr); } else { back = 0; /* ERROR: Not a boolean */ log_model_3(lineno, 111, 112, valptr); } } else { stat_always = 1; } } else { stat_always = 1; } } break; case 9: { back = model_conf_state_text(valptr, PJSNMP_ST_BUSY, 0, lineno); } break; case 10: { back = model_conf_state_text(valptr, PJSNMP_ST_BUSY, 1, lineno); } break; case 11: { back = model_conf_state_text(valptr, PJSNMP_ST_UNKNOWN, 0, lineno); } break; case 12: { back = model_conf_state_text(valptr, PJSNMP_ST_UNKNOWN, 1, lineno); } break; case 13: { back = model_conf_state_text(valptr, PJSNMP_ST_ERROR, 0, lineno); } break; case 14: { back = model_conf_state_text(valptr, PJSNMP_ST_ERROR, 1, lineno); } break; case 15: { back = model_conf_state_text(valptr, PJSNMP_ST_WARMUP, 0, lineno); } break; case 16: { back = model_conf_state_text(valptr, PJSNMP_ST_WARMUP, 1, lineno); } break; default: { $? "! illegal key" /* ERROR: Syntax, unknown key */ log_model_3(lineno, 113, 114, str); } break; } } else { $? "! missing value" /* ERROR: Syntax, missing value */ log_model_1(lineno, 110); } } else { $? "! missing value" /* ERROR: Syntax, missing value */ log_model_1(lineno, 110); } $? "- process_model_line %d", back return back; } /** Configure for specified SNMP model. */ static int process_model_setup(void) { FILE *infile; /* Input file to read */ char *p1; /* Start of text in line */ dk4_um_t lineno = (dk4_um_t)0UL; int in_model = 0; /* Flag: In model to read */ int found = 0; /* Flag: Model was found */ int back = 0; $? "+ process_model_setup" infile = fopen(((NULL != model_file) ? model_file : def_model_file), "r"); if (NULL != infile) { $? ". file opened" back = 1; while (NULL != fgets(pjsnmp_buf, sizeof(pjsnmp_buf), infile)) { lineno++; #if TRACE_DEBUG dk4str8_delnl(pjsnmp_buf); $? ". buffer \"%s\"", pjsnmp_buf #endif p1 = dk4str8_start(pjsnmp_buf, NULL); if (NULL != p1) { if ('[' == *p1) { in_model = check_in_model(++p1, &back, lineno); if (0 != in_model) { found = 1; } } else { if (0 != in_model) { if ('#' != *p1) { #if !TRACE_DEBUG dk4str8_delnl(pjsnmp_buf); #endif if (0 == process_model_line(p1, lineno)) { back = 0; } } } } } } fclose(infile); if (0 == found) { $? "! model not found" back = 0; /* ERROR: Model not found */ log_model_3((dk4_um_t)0UL, 115, 116, model_name); } } else { $? "! failed to open model file" /* ERROR: Failed to open model file */ log_model_3( (dk4_um_t)0UL, 115, 116, ((NULL != model_file) ? model_file : def_model_file) ); } $? "- process_model_setup %d", back return back; } /** Process PRINTCAP_ENTRY and optionally model file. @param argc Number of command line arguments. @param argv Command line arguments array. @return 1 on success, 0 on error. */ static int process_setup(void) { char *ptr; /* Environment variable value */ char *cl; /* Current line */ char *nl; /* Next line */ char *ci; /* Current item */ char *ni; /* Next item */ int found = 0; /* Already item found */ int back = 0; $? "+ process_setup" ptr = getenv(pjsnmp_kw[2]); if (NULL != ptr) { $? ". have printcap entry" printcap_entry = dk4str8_dup(ptr, NULL); if (NULL != printcap_entry) { $? ". have private copy" back = 1; $? ". back = 1" cl = printcap_entry; while (NULL != cl) { nl = dk4str8_chr(cl, '\n'); if (NULL != nl) { *(nl++) = '\0'; nl = dk4str8_start(nl, NULL); } $? ". line current=\"%s\" next=\"%s\"", cl, TR_8STR(nl) ptr = cl; while ('\0' != *ptr) { if ('\r' == *ptr) { *ptr = '\0'; } else { ptr++; } } ci = dk4str8_start(cl, NULL); if (NULL != ci) { if ('#' != *ci) { while (NULL != ci) { ni = dk4str8_chr(ci, ':'); if (NULL != ni) { *(ni++) = '\0'; ni = dk4str8_start(ni, NULL); } $? ". item current=\"%s\" next=\"%s\"", ci, TR_8STR(ni) ci = dk4str8_start(ci, NULL); if (NULL != ci) { if (0 != found) { if (0 == process_one_config_item(ci)) { back = 0; $? ". back = 0" } } found = 1; } ci = ni; } } } cl = nl; } if (NULL == host_name) { back = 0; $? "! missing host name, back = 0" /* ERROR: Host name of printer required */ log_1_with_header(2, 119); } if (0 != back) { if (NULL != model_name) { back = process_model_setup(); $? ". back = %d", back } } if (0 != back) { if (0 != acct_achk) { $? ". checking requires enab" acct_enab = 1; } if (0 != acct_enab) { if (NULL == acct_name) { if ((NULL == acct_host) || (0 == acct_port)) { log_1_with_header(2, 165); back = 0; } } } } } else { $? "! allocation" /* ERROR: Memory allocation failed */ log_1_with_header(2, 105); } } else { $? "! missing PRINTCAP_ENTRY" /* ERROR: Missing printcap entry environment variable */ log_1_with_header(2, 120); } $? "- process_setup %d", back return back; } /** Eat up remaining input from standard input. */ static void eatup_input(void) { size_t rdb; /* Number of read bytes */ int cc = 1; $? "+ eatup_input" do { if (0 != can_continue(0)) { if (0 == have_eof) { $? ". must read" rdb = fread(pjsnmp_buf, 1, sizeof(pjsnmp_buf), stdin); if (0 == rdb) { $? ". final 0-bytes read" cc = 0; have_eof = 1; } #if TRACE_DEBUG else { $? ". data found" } #endif } else { $? ". eof found" cc = 0; } } else { $? "! signal" cc = -1; } } while (1 == cc); $? "- eatup_input" } /** Release memory for the text nodes in s_stn. */ static void relase_status_text_nodes(void) { status_text_t *ptr; /* Current node to release */ $? "+ relase_status_text_nodes" if (NULL != s_stn) { if (NULL != i_stn) { dk4sto_it_reset(i_stn); do { ptr = (status_text_t *)dk4sto_it_next(i_stn); if (NULL != ptr) { $? ". release one node" stt_delete(ptr); } } while(NULL != ptr); dk4sto_it_close(i_stn); i_stn = NULL; } dk4sto_close(s_stn); s_stn = NULL; } $? "- relase_status_text_nodes" } /** Attempt to open an SNMP session. @return 1 on success, 0 on error. */ static int attempt_snmp_session(void) { struct snmp_session template; /* Template for the session */ int back = 0; $? "+ attempt_snmp_session" init_snmp(pjsnmp_kw[4]); DK4_MEMRES(&template, sizeof(template)); snmp_sess_init(&template); template.version = snmp_version; template.peername = host_name; template.community = (unsigned char *)snmp_comm; if (NULL == snmp_comm) { template.community = (unsigned char *)(pjsnmp_kw[5]); } template.community_len = strlen((char *)(template.community)); snmp_sess = snmp_open(&template); if (NULL != snmp_sess) { $? ". session" back = 1; } else { $? "! no session" /* ERROR: Failed to open SNMP session */ log_1_no_header(121); if (0 != snmp_igfs) { $? ". ignore failed session" back = 1; } } $? "- attempt_snmp_session %d", back return back; } /** Accounting data exchange via socket (either network socket or local UNIX domain socket) and close socket. @param sock Socket to use for data exchange. @param expect_response Flag: Response from accounting system expected. @return 1 on success, 0 on error. */ static int accounting_ex_socket(dk4_socket_t sock, int expect_response) { size_t sl; /* String length */ size_t by; /* Bytes written or read */ int res; /* Operation result */ int cutted; /* String was cutted */ int cc; /* Flag: Can continue */ int back = 0; $? "+ accounting_ex_socket %d %d", (int)sock, expect_response cutted = 0; if (0 != can_continue(0)) { $? ". can run" sl = strlen(pjsnmp_buf); by = sl; res = dk4socket_send(sock, pjsnmp_buf, &by, 0, 0L, 0L, NULL); if (DK4_SOCKET_RESULT_SUCCESS == res) { $? ". send ok" if (by == sl) { $? ". number of bytes ok" back = 1; $? ". back=1" } else { $? "! partial" /* ERROR: Partial send */ log_1_no_header(123); } } else { $? "! send" /* ERROR: Failed to send data */ log_1_no_header(122); } sl = 0; pjsnmp_buf[0] = '\0'; res = dk4socket_shutdown(sock, DK4_SOCKET_SHUT_WRITE, NULL); if (DK4_SOCKET_RESULT_SUCCESS == res) { $? ". shutdown ok" cc = 1; do { if (0 != can_continue(0)) { $? ". can run" by = sizeof(stat_text) - 1; res = dk4socket_recv(sock, stat_text, &by, 0, 0L, 0L, NULL); if (DK4_SOCKET_RESULT_SUCCESS == res) { $? ". recv" if (0 < by) { $? ". bytes" stat_text[by] = '\0'; res = dk4str8_cat_s(pjsnmp_buf,sizeof(pjsnmp_buf),stat_text,NULL); if (0 == res) { cutted = 1; $? "! too long" back = 0; } } else { $? ". 0 bytes" cc = 0; } } else { $? "! recv" cc = -1; } } else { $? "! signal" cc = -1; back = 0; } } while(1 == cc); } else { $? "! socket shutdown" back = 0; /* ERROR: Socket shutdown failed */ log_1_no_header(124); } if (0 != expect_response) { $? ". response expected" if (0 == strlen(pjsnmp_buf)) { $? "! no response" back = 0; } if (0 != cutted) { $? "! too much response data" back = 0; } } } else { $? "! signal" pjsnmp_buf[0] = '\0'; } dk4socket_close(sock, NULL); $? "- accounting_ex_socket %d", back return back; } /** Accounting data exchange via local UNIX domain socket. @param expect_response Flag: Response from accounting system expected. @return 1 on success, 0 on error. */ static int accounting_ex_local_socket(int expect_response) { dk4_socket_t sock; /* Socket to use for accounting */ int back = 0; $? "+ accounting_ex_local_socket response=%d", expect_response sock = dk4socket_c8_unix_client(acct_name, NULL); if (INVALID_SOCKET != sock) { back = accounting_ex_socket(sock, expect_response); /* Socket not closed here, closed in accounting_ex_socket() */ } else { /* ERROR: Failed to open UNIX domain socket */ log_1_no_header(125); pjsnmp_buf[0] = '\0'; } $? "- accounting_ex_local_socket %d", back return back; } /** Accounting data exchange via network socket. @param expect_response Flag: Response from accounting system expected. @return 1 on success, 0 on error. */ static int accounting_ex_network(int expect_response) { dk4_socket_t sock; /* Socket to use for accounting */ int back = 0; $? "+ accounting_ex_network response=%d", expect_response sock = dk4socket_c8_tcp_client_host_port(acct_host,acct_port,0,0L,0L,NULL); if (INVALID_SOCKET != sock) { back = accounting_ex_socket(sock, expect_response); /* Socket not closed here, closed in accounting_ex_socket() */ } else { /* ERROR: Failed to open network connection */ log_1_no_header(125); pjsnmp_buf[0] = '\0'; } $? "- accounting_ex_network %d", back return back; } /** Accounting data exchange to file. @param fipo Accounting file. @param expect_response Flag: Response from accounting system expected. @return 1 on success, 0 on error. */ static int accounting_ex_file(FILE *fipo, int expect_response) { int back = 0; $? "+ accounting_ex_file response=%d", expect_response if (EOF != fputs(pjsnmp_buf, fipo)) { $? ". fputs" if (EOF != fflush(fipo)) { $? ". fflush" back = 1; } #if TRACE_DEBUG else { $? "! fflush" } #endif } #if TRACE_DEBUG else { $? "! fputs" } #endif pjsnmp_buf[0] = '\0'; if (0 != expect_response) { $? "! response wanted" back = 0; } $? "- accounting_ex_file %d", back return back; } /** Accounting data exchange via pipe. @param expect_response Flag: Response from accounting system expected. @return 1 on success, 0 on error. */ static int accounting_ex_pipe(int expect_response) { FILE *fipo = NULL; /* Connection to pipe */ char *prgn = NULL; /* Program name */ int back = 0; int pcres = 0; /* Result from pclose */ $? "+ accounting_ex_pipe response=%d", expect_response prgn = dk4str8_start(&(acct_name[1]), NULL); if (NULL != prgn) { $? ". command name ok" fipo = popen(prgn, "w"); if (NULL != fipo) { $? ". pipe opened" back = accounting_ex_file(fipo, 0); pcres = pclose(fipo); pjsnmp_buf[0] = '\0'; if (0 != expect_response) { switch (pcres) { case LPRNG_EXIT_REMOVE : { decision = 3; } break; case LPRNG_EXIT_HOLD : { decision = 2; } default : { decision = 1; } break; } log_3_no_header(166, 167, accounting_responses[decision - 1]); } } else { $? "! popen" /* ERROR: Failed to start program for pipe */ log_1_no_header(126); pjsnmp_buf[0] = '\0'; } } else { $? "! command name not found" /* ERROR: Not command name found */ log_1_with_header(2, 127); pjsnmp_buf[0] = '\0'; } $? "- accounting_ex_pipe %d", back return back; } /** Accounting data exchange via file specified by name. @param expect_response Flag: Response from accounting system expected. @param crf Flag: Create new file. @return 1 on success, 0 on error. */ static int accounting_ex_filename(int expect_response, int crf) { dk4_er_t er; /* Error report */ FILE *fipo = NULL; /* File to write to */ int back = 1; int tests = DK4_FOPEN_SC_USER; /* Security tests for fopen */ $? "+ accounting_ex_filename response=%d, create=%d", expect_response, crf if (0 != dk4isadmin()) { $? ". administrative privileges" tests = DK4_FOPEN_SC_PRIVILEGED; } dk4error_init(&er); fipo = dk4fopen_c8(acct_name, ((0 != crf) ? "w" : "a"), tests, &er); if (NULL != fipo) { $? ". fopen" back = accounting_ex_file(fipo, expect_response); if(EOF == fclose(fipo)) { $? "! fclose" back = 0; } } else { $? "! fopen" /* ERROR: Failed to open accounting file name */ log_1_no_header(128); pjsnmp_buf[0] = '\0'; } $? "- accounting_ex_filename %d", back return back; } #if 0 /* 2016-02-10: Although chapter 15.5 of the LPRng reference manual states that the :af file is opened (file, pipe or network socket) and passed as file descriptor 3 to all filters, this did not work in my tests. Text written to fd 3 did not arrive at the accounting system. */ /** Accounting data exchange via file descriptor 3. @param expect_response Flag: Response from accounting system expected. @return 1 on success, 0 on error. */ static int accounting_ex_fd3(int expect_response) { size_t sl; /* String length */ ssize_t bwr; /* Number of bytes written or read */ int back = 0; $? "+ accounting_ex_fd3 response=%d", expect_response sl = strlen(pjsnmp_buf); bwr = write(3, pjsnmp_buf, sl); pjsnmp_buf[0] = '\0'; if (0 < bwr) { $? ". write" if (sl == (size_t)bwr) { $? ". full" back = 1; } else { $? "! partial" /* ERROR: Incomplete write operation */ log_1_no_header(122); } if (0 != expect_response) { $? ". expect response" bwr = read(3, pjsnmp_buf, sizeof(pjsnmp_buf)); if (0 < bwr) { $? ". bytes read" pjsnmp_buf[bwr] = '\0'; } else { $? "! no response" pjsnmp_buf[0] = '\0'; back = 0; } } } else { $? "! write" /* ERROR: Write failed */ log_1_no_header(122); } $? "- accounting_ex_fd3 %d", back return back; } #endif /** Data exchange with accounting system. The contents of pjsnmp_buf is sent to the accounting system, the response - if any - is stored in pjsnmp_buf. @param expect_response Flag: Accounting system should send response. @return 1 on successfull exchange, 0 on connection error. */ static int accounting_exchange(int expect_response) { dk4_stat_t stb; /* File status buffer */ int issock = 0; /* Flag: File is socket */ int back = 0; /* Function result */ int crf = 0; /* Flag: Create log file */ $? "+ accounting_exchange response=%d", expect_response if ((NULL != acct_host) && (0 < acct_port)) { $? ". host+port" log_1_no_header(154); back = accounting_ex_network(expect_response); log_1_no_header((0 != back) ? 159 : 160); } else { $? ". no host+port" if (NULL != acct_name) { $? ". acct_name" if ('|' == *acct_name) { $? ". pipe" #if 0 if (0 != expect_response) { /* ERROR: Can not receive response from pipe */ log_1_with_header(2, 129); } #endif log_1_no_header(155); back = accounting_ex_pipe(expect_response); log_1_no_header((0 != back) ? 159 : 160); } else { $? ". not a pipe" if (0 != dk4stat_c8(&stb, acct_name, NULL)) { if (0 != dk4stat_is_unix_domain_socket(&stb, NULL)) { issock = 1; $? ". existing socket" } } else { crf = 1; $? ". create new file" } if (0 != issock) { log_1_no_header(156); back = accounting_ex_local_socket(expect_response); log_1_no_header((0 != back) ? 159 : 160); } else { if (0 != expect_response) { /* ERROR: Can not receive response from file */ log_1_with_header(2, 130); } log_1_no_header(157); back = accounting_ex_filename(expect_response, crf); log_1_no_header((0 != back) ? 159 : 160); } } } else { $? ". fd3" #if 0 log_1_no_header(158); back = accounting_ex_fd3(expect_response); log_1_no_header((0 != back) ? 159 : 160); #else log_1_with_header(2, 165); #endif } } $? "- accounting_exchange %d", back return back; } /** Check users permission to print on queue. @return 1 if user is allowed to print, 0 otherwise. */ static int check_permission(void) { int back = 1; int res = 0; /* Result from check */ const size_t bsz = sizeof(pjsnmp_buf); /* Buffer size */ $? "+ check_permission" if ((0 != acct_enab) && (0 != acct_achk)) { $? ". check required" /* LOG: Checking print permission */ log_5_no_header(131, 132, 133, user_name, queue_name); if(0 != dk4str8_cpy_s(pjsnmp_buf, bsz, pjsnmp_kw[6], NULL)) { if(0 != dk4str8_cat_s(pjsnmp_buf, bsz, pjsnmp_kw[1], NULL)) { if(0 != dk4str8_cat_s(pjsnmp_buf, bsz, queue_name, NULL)) { if(0 != dk4str8_cat_s(pjsnmp_buf, bsz, pjsnmp_kw[1], NULL)) { if(0 != dk4str8_cat_s(pjsnmp_buf, bsz, user_name, NULL)) { if(0 != dk4str8_cat_s(pjsnmp_buf, bsz, pjsnmp_kw[0], NULL)) { back = 0; $? ". request ok" if (0 != accounting_exchange(1)) { $? ". request/response" dk4str8_normalize(pjsnmp_buf, NULL); $? ". \"%s\"", pjsnmp_buf if (0 < strlen(pjsnmp_buf)) { $? ". data in response" if (0 != decision) { res = decision - 1; } else { res = dk4str8_array_index(accounting_responses, pjsnmp_buf, 0); if (-1 < res) { decision = res + 1; } } switch (res) { case 0: { back = 1; $? ". ACCEPT" } break; case 1: { $? ". HOLD" exval = LPRNG_EXIT_HOLD; } break; } log_3_no_header(166, 167, pjsnmp_buf); } else { $? "! empty response" #if 0 /* 2016-02-15 An empty response indicates permission to print. */ if (0 != acct_apcf) { back = 1; } #endif back = 1; } } else { $? "! no response" if (0 != acct_apcf) { back = 1; } else { decision = 3; } } } } } } } } /* LOG: Test result */ log_1_no_header((0 != back) ? 134 : 135); } $? "- check_permission %d", back return back; } /** Retrieve integer value from variable list. @param iptr Address of variable to set. @param var SNMP variable containing the value. @return 1 on success, 0 on error. */ static int get_int(int *iptr, struct variable_list *var) { char bu[64]; /* Buffer for numeric values as text */ const char *endptr = NULL; /* First unprocessable character */ long l; /* long value */ int i; /* int value */ int back = 0; $? "+ get_int" switch (var->type) { case ASN_OCTET_STR : { $? ". from string" if ((var->val_len > 0) && (var->val_len < sizeof(bu))) { dk4mem_cpy(bu, (void *)((var->val).string), var->val_len, NULL); bu[var->val_len] = '\0'; if (0 != dk4ma_input_c8_dec_int(&i, bu, &endptr, 1, NULL)) { *iptr = i; back = 1; } } } break; case ASN_TIMETICKS : case ASN_GAUGE : case ASN_COUNTER : case ASN_INTEGER : { $? ". from integer" l = *((var->val).integer); *iptr = (int)l; back = 1; } break; } $? "- get_int %d", back return back; } /** Retrieve unsigned long value from variable list. @param ulptr Address of variable to set. @param var SNMP variable containing the value. @return 1 on success, 0 on error. */ static int get_ul(unsigned long *ulptr, struct variable_list *var) { unsigned long ul = 0UL; /* Result variable */ long l; /* long value */ int back = 0; $? "+ get_ul" switch (var->type) { case ASN_OCTET_STR : { $? ". from byte array" if (0 < var->val_len) { ul |= ((((unsigned long)(((var->val).string)[0])) << 24) & 0xFF000000UL); back = 1; } if (1 < var->val_len) { ul |= ((((unsigned long)(((var->val).string)[1])) << 16) & 0x00FF0000UL); } if (2 < var->val_len) { ul |= ((((unsigned long)(((var->val).string)[2])) << 8) & 0x0000FF00UL); } if (3 < var->val_len) { ul |= ((((unsigned long)(((var->val).string)[3])) ) & 0x000000FFUL); } if (0 != back) { *ulptr = ul; } } break; case ASN_TIMETICKS : case ASN_GAUGE : case ASN_COUNTER : case ASN_INTEGER : { $? ". from integer" l = *((var->val).integer); if (0L <= l) { *ulptr = (unsigned long)l; back = 1; } } break; } $? "- get_ul %d", back return back; } /** Check whether a buffer contains non-ASCII characters. @param ptr Buffer address. @param i Buffer size. @return 1 if non-ASCII found, 0 otherwise. */ static int contains_non_ascii(const char *ptr, size_t i) { int back = 0; $? "+ contains_non_ascii" while ((0 == back) && (0 < i)) { if (0x7F < (unsigned char)(*(ptr++))) { back = 1; } i--; } $? "- contains_non_ascii %d", back return back; } /** Replace all non-ascii characters by question marks. */ static void no_non_ascii(char *str) { unsigned char uc; while ('\0' != *str) { uc = (unsigned char)(*str); if ((0x20 > uc) || (0x7E < uc)) { *str = '?'; } str++; } } /** Check two OIDs for equality. @param pl Left OID. @param szl Left OID size. @param pr Right OID. @param szr Right OID size. @return 1 for equal OIDs, 0 otherwise. */ static int oids_equal(const oid *pl, size_t szl, const oid *pr, size_t szr) { #if VERSION_BEFORE_20160206 size_t i; /* Index of current sub-id to compare */ #endif int back = 0; if ((szl == szr) && (0 < szl)) { #if VERSION_BEFORE_20160206 back = 1; for (i = 0; ((i < szl) && (1 == back)); i++) { if (pl[i] != pr[i]) { back = 0; } } #else /* No check for overflow necessary, max 4 * 128. */ if (0 == dk4mem_cmp(pl, pr, (szl * sizeof(oid)), NULL)) { back = 1; } #endif } return back; } /** Find OID index. @param oid Pointer to OID to check. @param sz OID size. @return 0 for hrDeviceStatus, 1 for hrPrinterStatus, 2 for page counter, 3 for hrPrinterDetectedErrorState, 4 for status text, -1 otherwise. */ static int oid_index(oid *p, size_t sz) { int back = -1; $? "+ oid_index" if (0 != oids_equal(p, sz, oid_ds, sz_oid_ds)) { back = 0; } else { if (0 != oids_equal(p, sz, oid_ps, sz_oid_ps)) { back = 1; } else { if (0 != oids_equal(p, sz, oid_pc, sz_oid_pc)) { back = 2; } else { if (0 != oids_equal(p, sz, oid_pe, sz_oid_pe)) { back = 3; } else { if (0 < sz_oid_st) { $? ". have status text oid" if (0 != oids_equal(p, sz, oid_st, sz_oid_st)) { back = 4; } } #if TRACE_DEBUG else { $? "! no status text oid" } #endif } } } } $? "- oid_index %d", back return back; } /** Show one active condition from hrPrinterDetectedErrorState. @param cond Condition bit (checked against the found conditions). @param cind Index of corresponding text in pjsnmp_kw. */ static void show_pdes(unsigned long cond, size_t cind) { if (0UL != (pdes_cond & cond)) { fputs(pjsnmp_kw[((0UL != (pdes_error & cond)) ? 32 : 33)], out_file); fputs(pjsnmp_kw[cind], out_file); } } static void show_text_containing_non_ascii(const unsigned char *ptr, size_t num) { while(0 < num--) { if (0x7F < *ptr) { fputc('?', out_file); } else { fputc(*ptr, out_file); } ptr++; } } /** Report status if changed. @param found Bitmask for found information. */ static void report_status_if_changed(int found) { unsigned long tcond = 0x80000000UL; /* Bit for one condition */ int must_report = 0; /* Flag: Must report change */ int i; $? "+ report_status_if_changed found=%d", found /* Check whether or not to report. */ if (stsum != ostsum) { $? ". summary changed" must_report = 1; } else { if (devst != odevst) { $? ". device status changed" must_report = 1; } else { if (prist != oprist) { $? ". printer status changed" must_report = 1; } else { if (pdes_cond != opdes_cond) { $? ". error conditions changed" must_report = 1; } else { if (0 < sz_stat_text) { if (0 != force_printable_status_text) { if (sz_ostat_text != sz_stat_text) { must_report = 1; } else { if (0 != dk4mem_cmp(stat_text,ostat_text,sz_stat_text,NULL)) { must_report = 1; } } } else { if (0 == contains_non_ascii(stat_text, sz_stat_text)) { if (sz_stat_text != sz_ostat_text) { must_report = 1; $? ". status text size changed" } else { if (0 != dk4mem_cmp(stat_text,ostat_text,sz_stat_text,NULL)) { must_report = 1; $? ". status text changed" } } } } } } } } } /* Report if necessary. */ if (0 != must_report) { $? ". must report" fputs(pjsnmp_kw[150], out_file); (void)log_timestamp(); if ((-1 < devst) && (6 > devst)) { if ((-1 < prist) && (6 > prist)) { fputs(pjsnmp_kw[7], out_file); fputs(pjsnmp_kw[11 + devst], out_file); fputs(pjsnmp_kw[8], out_file); fputs(pjsnmp_kw[17 + prist], out_file); fputs(pjsnmp_kw[0], out_file); } } fputs(pjsnmp_kw[9], out_file); for (i = 0; i < 16; i++) { if ((0 < i) && (0 == (i % 4))) { fputc('-', out_file); } fputc(((0UL != (pdes_cond & tcond)) ? '1' : '0'), out_file); tcond = tcond / 2UL; } fputs(pjsnmp_kw[0], out_file); show_pdes(PDES_ERROR_DOOR_OPEN, 38); show_pdes(PDES_ERROR_OFFLINE, 40); show_pdes(PDES_ERROR_MARKER_SUPPLY_MISSING, 44); show_pdes(PDES_ERROR_NO_TONER, 37); show_pdes(PDES_WARNING_TONER_LOW, 36); show_pdes(PDES_ERROR_INPUT_TRAY_MISSING, 42); show_pdes(PDES_WARNING_INPUT_TRAY_EMPTY, 47); show_pdes(PDES_ERROR_NO_PAPER, 35); show_pdes(PDES_WARNING_PAPER_LOW, 34); show_pdes(PDES_ERROR_OUTPUT_TRAY_MISSING, 43); show_pdes(PDES_ERROR_PAPER_JAM, 39); show_pdes(PDES_ERROR_OUTPUT_FULL, 46); show_pdes(PDES_WARNING_OUTPUT_NEAR_FULL, 45); show_pdes(PDES_WARNING_SERVICE, 41); show_pdes(PDES_WARNING_OVERDUE_PREVENT_MAINT, 48); if (0 < sz_stat_text) { if (0 != force_printable_status_text) { fputs(pjsnmp_kw[30], out_file); show_text_containing_non_ascii((unsigned char *)stat_text,sz_stat_text); fputs(pjsnmp_kw[31], out_file); } else { if (0 == contains_non_ascii(stat_text, sz_stat_text)) { fputs(pjsnmp_kw[30], out_file); fwrite(stat_text, 1, sz_stat_text, out_file); fputs(pjsnmp_kw[31], out_file); } } } if ((-1 < stsum) && (7 > stsum)) { fputs(pjsnmp_kw[10], out_file); fputs(pjsnmp_kw[23 + stsum], out_file); fputs(pjsnmp_kw[0], out_file); } if (PJSNMP_ST_ERROR == stsum) { fputs(pjsnmp_kw[49], out_file); } else { if (0UL != (pdes_cond & PDES_WARNING_SERVICE)) { if (0UL == (pdes_error & PDES_WARNING_SERVICE)) { fputs(pjsnmp_kw[50], out_file); } } } fflush(out_file); } $? "- report_status_if_changed" } /** One SNMP data exchange. @param passno 0 before start, 1 during transfer, 2 at end. @return 1 on success, 0 on error (stop printing). */ static int one_snmp_request(int passno) { status_text_t tx; /* Used in comparison */ status_text_t *ptx = NULL; /* Text node found */ struct snmp_pdu *rq = NULL; /* Request */ struct snmp_pdu *rs = NULL; /* Response */ struct variable_list *vars = NULL; /* Response variables */ int status = 0; /* SNMP result */ int found = 0; /* Information from response */ int back = 0; $? "+ one_snmp_request pass=%d", passno if (0 != can_continue(0)) { $? ". no signal" if (NULL != snmp_sess) { $? ". session" pdes_cond = 0UL; pcnt = 0UL; devst = 0; prist = 0; stsum = PJSNMP_ST_UNREACHABLE; stat_text[0] = '\0'; sz_stat_text = 0; rq = snmp_pdu_create(SNMP_MSG_GET); if (NULL != rq) { $? ". rq" back = 1; snmp_add_null_var(rq, oid_ds, sz_oid_ds); snmp_add_null_var(rq, oid_ps, sz_oid_ps); snmp_add_null_var(rq, oid_pc, sz_oid_pc); snmp_add_null_var(rq, oid_pe, sz_oid_pe); if (0 < sz_oid_st) { $? ". have status text oid" snmp_add_null_var(rq, oid_st, sz_oid_st); } status = snmp_synch_response(snmp_sess, rq, &rs); if (STAT_SUCCESS == status) { $? ". status" if (NULL != rs) { $? ". rs" if (SNMP_ERR_NOERROR == rs->errstat) { $? ". errstat" /* Obtain response elements. */ stsum = PJSNMP_ST_UNKNOWN; vars = rs->variables; while (NULL != vars) { switch (oid_index(vars->name, vars->name_length)) { case 0: { $? ". device status" if (0 != get_int(&devst, vars)) { $? ". value ok" found |= 1; } } break; case 1: { $? ". printer status" if (0 != get_int(&prist, vars)) { $? ". value ok" found |= 2; } } break; case 2: { $? ". page count" if (0 != get_ul(&pcnt, vars)) { $? ". value ok" found |= 4; $? ". pcnt=%lu", pcnt switch (passno) { case 0: { pc_start = pcnt; have_pcs = 1; } break; case 1: { /* When aborting due to timeout in error or unreachable state we use the last page counter seen. */ pc_end = pcnt; have_pce = 1; } break; case 2: { pc_end = pcnt; have_pce = 1; } break; } } } break; case 3: { $? ". printer detected error state" if (0 != get_ul(&pdes_cond, vars)) { found |= 8; } } break; case 4: { $? ". status text" if (0 < vars->val_len) { if (sizeof(stat_text) > vars->val_len) { $? ". length ok" dk4mem_cpy( stat_text, (vars->val).string, vars->val_len, NULL ); sz_stat_text = vars->val_len; } else { $? "! too long, cutted" dk4mem_cpy( stat_text, (vars->val).string, sizeof(stat_text), NULL ); sz_stat_text = sizeof(stat_text); } found |= 16; } } break; } vars = vars->next_variable; } /* Build summary (1): device and printer status. */ if (3 == (3 & found)) { stsum = get_state_mapping(devst, prist); $? ". stsum=%s (1)", pjsnmp_kw[23+stsum] } /* Build summary (2): printer detected error state */ if (8 == (8 & found)) { if ((0 != pdes_always) || (PJSNMP_ST_UNKNOWN == stsum)) { if (0L != (pdes_error & pdes_cond)) { stsum = PJSNMP_ST_ERROR; $? ". stsum=ERROR (2)" } } } /* Build summary (3): status text line */ if (16 == (16 & found)) { if ((0 != stat_always) || (PJSNMP_ST_UNKNOWN == stsum)) { if ((NULL != s_stn) && (NULL != i_stn)) { DK4_MEMRES(&tx, sizeof(tx)); tx.text = (unsigned char *)stat_text; tx.size = sz_stat_text; tx.state = 0; tx.start = 0; ptx = (status_text_t *)dk4sto_it_find_like(i_stn, &tx, 1); if (NULL != ptx) { stsum = ptx->state; $? ". stsum=%s (3)", pjsnmp_kw[23 + stsum] } } } } /* Build summary (4): Map unknown to busy if configured */ if ((0 != unkn_busy) && (PJSNMP_ST_UNKNOWN == stsum)) { stsum = PJSNMP_ST_BUSY; $? ". stsum=BUSY (4)" } /* For error state mark this page counter as print job not yet finished. */ switch (stsum) { case PJSNMP_ST_ERROR: { seen_printing = 0; $? ". seen_printing=0" if ((0 < passno) && (0UL < pcnt)) { pc_error = pcnt; $? ". pc_error=%lu", pc_error } start_unreach = (dk4_time_t)0UL; if ((dk4_time_t)0UL == start_error) { dk4time_get(&start_error); } } break; case PJSNMP_ST_BUSY: { seen_printing = 1; $? ". seen_printing=1" start_unreach = (dk4_time_t)0UL; start_error = (dk4_time_t)0UL; } break; case PJSNMP_ST_UNREACHABLE : { if ((dk4_time_t)0UL == start_unreach) { dk4time_get(&start_unreach); } start_error = (dk4_time_t)0UL; } break; default : { start_unreach = (dk4_time_t)0UL; start_error = (dk4_time_t)0UL; } break; } /* On change, print new state. */ report_status_if_changed(found); /* Save data for next comparison. */ odevst = devst; oprist = prist; ostsum = stsum; opdes_cond = pdes_cond; if (0 < sz_stat_text) { DK4_MEMCPY(ostat_text, stat_text, sz_stat_text); } sz_ostat_text = sz_stat_text; } #if TRACE_DEBUG else { $? "! errstat" } #endif } #if TRACE_DEBUG else { $? "! rs" } #endif } #if TRACE_DEBUG else { $? "! status" } #endif if (NULL != rs) { snmp_free_pdu(rs); } } #if TRACE_DEBUG else { $? "! rq" } #endif } #if TRACE_DEBUG else { $? "! no session" } #endif } #if TRACE_DEBUG else { $? "! signal" } #endif $? "- one_snmp_request %d", back return back; } /** Create accounting text line and do data exchange with accounting system. @param kwind Index of start keyword in pjsnmp_kw array. @param pc Page counter value to transmit. */ static void accounting_index_and_value(size_t kwind, unsigned long pc) { char buf[64]; /* Buffer for number as text */ const char *jt; /* Job title */ size_t szbuf = sizeof(pjsnmp_buf); /* Buffer size */ size_t sl; int res; /* Conversion result */ $? "+ accounting_index_and_value %u %lu", (unsigned)kwind, pc res = dk4ma_write_c8_decimal_unsigned( buf, sizeof(buf), (dk4_um_t)pc, 0, NULL ); if (0 != res) { if (0 != dk4str8_cpy_s(pjsnmp_buf, szbuf, pjsnmp_kw[kwind], NULL)) { if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, pjsnmp_kw[1], NULL)) { if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, queue_name, NULL)) { if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, pjsnmp_kw[1], NULL)) { if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, user_name, NULL)) { if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, pjsnmp_kw[1], NULL)) { if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, buf, NULL)) { if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, pjsnmp_kw[1], NULL)) { if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, job_name, NULL)) { if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, pjsnmp_kw[1], NULL)) { jt = job_title; if (NULL == jt) { jt = pjsnmp_kw[54]; } sl = strlen(pjsnmp_buf); if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, jt, NULL)) { if (0 == allow_non_ascii) { no_non_ascii(&(pjsnmp_buf[sl])); } if (0 != dk4str8_cat_s(pjsnmp_buf, szbuf, pjsnmp_kw[0], NULL)) { $? ". request string created" (void)accounting_exchange(0); } } } } } } } } } } } } } $? "- accounting_index_and_value" } /** Report used pages to accounting system. */ static void report_used_pages(void) { $? "+ report_used_pages" if (0 != acct_start_end) { accounting_index_and_value(51, pc_start); accounting_index_and_value(52, pc_end); } else { accounting_index_and_value(53, (pc_end - pc_start)); } $? "- report_used_pages" } /** Do accounting before and after print job. @param passno 0=before print job, 1=after print job. @return 1 on success, 0 on error. */ static int do_accounting(int passno) { static char buf[8*sizeof(dk4_um_t)]; /* Number as text */ struct timeval to; /* Timeout for select */ dk4_time_t start_idle = (dk4_time_t)0UL; /* Start idle/standby */ dk4_time_t current_time = (dk4_time_t)0UL; /* Current time */ dk4_time_t timeout_time = (dk4_time_t)0UL; /* Timeout reached */ int res = 0; /* Conversion result */ int back = 0; int cc = 1; /* Flag: Can continue */ int uncoi = 0; /* Flag: Unconditional idle reported */ int cto = 0; /* Flag: Must check timeout */ $? "+ do_accounting %d", passno if (NULL != snmp_sess) { $? ". session" /* In the loop retrieve printer status via SNMP, finish if printer is idle or in standby state. */ log_1_no_header(145+passno); do { if (0 != can_continue(0)) { $? ". no signal" if (0 != one_snmp_request((0 == passno) ? 0 : 2)) { $? ". SNMP exchange" /* In idle or standby we can retrieve the page counter if the page counter was changed or the printer was seen printing. */ switch (stsum) { case PJSNMP_ST_IDLE : case PJSNMP_ST_STANDBY : { back = 1; cc = 0; $? ". idle or standby" cto = 0; if (1 == passno) { if ((0 == seen_printing) && (0 < print_timeout)) { cto = 1; $? ". cto=1 (not seen printing)" } else { if ((0UL != pc_error) && (1 == have_pce)) { if ((pc_end == pc_error)&&((dk4_um_t)0UL < print_timeout)) { cto = 1; $? ". cto=1 (pagecounter)" } } } if (0 != cto) { dk4time_get(¤t_time); if ((dk4_time_t)0UL < start_idle) { if ((start_idle+(dk4_time_t)print_timeout) > current_time) { back = 0; cc = 1; $? ". wait to start printing" } } else { start_idle = current_time; back = 0; cc = 1; if (0 == uncoi) { uncoi = 1; res = dk4ma_write_c8_decimal_unsigned( buf, sizeof(buf), print_timeout, 0, NULL ); log_timestamp(); fputs(pjsnmp_kw[162], out_file); if (0 != res) { fputs(buf, out_file); fputs(pjsnmp_kw[163], out_file); } fputs(pjsnmp_kw[164], out_file); fflush(out_file); } } } } } break; case PJSNMP_ST_UNREACHABLE : { start_idle = (dk4_time_t)0UL; /* not idle */ uncoi = 0; /* If printer is unreachable we abort after timeout. */ if (((0 == passno) && ((dk4_um_t)0UL != unto_start)) || ((1 == passno) && ((dk4_um_t)0UL != unto_end)) ) { if ((dk4_time_t)0UL < start_unreach) { dk4time_get(¤t_time); timeout_time = start_unreach + (dk4_time_t)((0 == passno) ? unto_start : unto_end); if (current_time > timeout_time) { $? ". timeout unreach" cc = 0; /* ERROR: Timeout while printer unreachable */ log_1_no_header(136); } } else { dk4time_get(&start_unreach); } } } break; case PJSNMP_ST_ERROR : { start_idle = (dk4_time_t)0UL; /* not idle */ uncoi = 0; if ( ((0 == passno) && ((dk4_um_t)0UL < erto_start)) || ((1 == passno) && ((dk4_um_t)0UL < erto_end)) ) { if ((dk4_um_t)0UL < start_error) { dk4time_get(¤t_time); timeout_time = start_error + (dk4_time_t)((0 == passno) ? erto_start : erto_end); if (current_time > timeout_time) { cc = 0; /* ERROR: Timeout while printer in error state */ log_1_no_header(168); if (0 == passno) { back = 0; /* Do not print */ } } } else { dk4time_get(&start_error); } } } break; default: { start_idle = (dk4_time_t)0UL; /* not idle */ uncoi = 0; } break; } /* Decrease cpu and network load from polling */ if (1 == cc) { if (0 != can_continue(0)) { if (PJSNMP_ST_UNREACHABLE == stsum) { sleep(1); } else { to.tv_sec = 0L; to.tv_usec = 250000L; (void)select(0, NULL, NULL, NULL, &to); } } else { cc = -1; } } } else { $? "! SNMP exchange failed" cc = -1; } } else { $? "! signal" cc = -1; } } while(1 == cc); fputs(pjsnmp_kw[150], out_file); if (((0 == passno)&&(0 != have_pcs))||((1 == passno)&&(0 != have_pce))) { $? ". attempt to report page counter" res = dk4ma_write_c8_decimal_unsigned(buf, sizeof(buf), pcnt, 0, NULL); if (0 != res) { $? ". before log3" log_3_no_header((147+passno), 0, buf); $? ". after log3" } $? ". page counter reported" } else { $? ". before log1" log_1_no_header(149); $? ". after log1" } /* Save page counter at start to page counter unusable for print job finished. */ if (0 == passno) { if (0 != have_pcs) { pc_error = pc_start; } } /* After successful page counting at job end, report to accounting system. */ if (1 == passno) { $? ". end of print job" if ((0 != have_pcs) && (0 != have_pce)) { $? ". have page counters" if (pc_end > pc_start) { $? ". page counter changed" /* Report number of used pages to accounting system */ report_used_pages(); } } } } else { $? "! session" if (0 != snmp_igfs) { $? ". ignore missing session" back = 1; } } $? "- do_accounting %d", back return back; } /** Transfer data to printer. @return 1 on success, 0 on error. */ static int data_transfer(void) { fd_set wfds; /* Writable files set */ fd_set rfds; /* Readable files set */ struct timeval to; /* Timeout for select */ char *wrptr; /* Pointer to data to write */ dk4_time_t unwriteable; /* Socket started to be unwriteable */ dk4_time_t current; /* Current time */ dk4_time_t tots; /* Timestamp when timeout is reached */ size_t rdb; /* Number of bytes read from stdin */ size_t remaining; /* Remaining bytes to write to socket */ size_t wrb; /* Bytes currently written to socket */ dk4_socket_t sock; /* Network socket to printer */ int back = 0; int cc = 1; /* Flag: Can continue */ int ccwr; /* Flag: Can continue writing */ int res; /* Number conversion result */ int writable; /* Flag: Socket is writeable */ $? "+ data_transfer" unwriteable = (dk4_time_t)0UL; if ((NULL != host_name) && (0 < host_port)) { sock = dk4socket_c8_tcp_client_host_port( host_name, host_port, local_port, 0L, 0L, NULL ); if (INVALID_SOCKET != sock) { exval = LPRNG_EXIT_SUCCESS; dk4time_get(&t2); back = 1; log_1_no_header(151); do { if (0 != can_continue(0)) { rdb = fread(pjsnmp_buf, 1, sizeof(pjsnmp_buf), stdin); if (0 < rdb) { wrptr = pjsnmp_buf; remaining = rdb; ccwr = 1; do { if (0 != can_continue(0)) { writable = 0; FD_ZERO(&wfds); FD_SET(sock,&wfds); if (0 != read_responses) { FD_ZERO(&rfds); FD_SET(sock,&rfds); } to.tv_sec = 0L; to.tv_usec = 250000L; if (0 != read_responses) { res = select((sock + 1), &rfds, &wfds, NULL, &to); } else { res = select((sock + 1), NULL, &wfds, NULL, &to); } if (0 < res) { if (FD_ISSET(sock,&wfds)) { writable = 1; unwriteable = (dk4_time_t)0UL; } else { if (0 != unwt_print) { dk4time_get(¤t); if ((dk4_time_t)0UL != unwriteable) { tots = unwriteable + (dk4_time_t)unwt_print; if (current > tots) { cc = ccwr = -1; /* ERROR: Timeout, socket unwriteable */ log_1_no_header(137); } } else { unwriteable = current; } } } if (0 != read_responses) { if (FD_ISSET(sock,&rfds)) { wrb = sizeof(dummy_text); res = dk4socket_recv(sock,dummy_text,&wrb,0,0L,0L,NULL); } } } if (0 != can_continue(0)) { if ((0 == writable) || (0 != check_trans)) { if (0 != one_snmp_request(1)) { switch (stsum) { case PJSNMP_ST_UNREACHABLE : { if ((dk4_um_t)0UL < unto_print) { if ((dk4_time_t)0UL < start_unreach) { tots = start_unreach + (dk4_time_t)unto_print; dk4time_get(¤t); if (current > tots) { cc = ccwr = -1; /* ERROR: Timeout, printer start_unreach */ log_1_no_header(136); } } else { dk4time_get(&start_unreach); } } } break; case PJSNMP_ST_ERROR : { if ((dk4_um_t)0UL < erto_print) { if ((dk4_time_t)0UL < start_error) { tots = start_error + (dk4_time_t)erto_print; dk4time_get(¤t); if (current > tots) { cc = ccwr = -1; /* ERROR: Timeout, printer start_unreach */ log_1_no_header(168); } } else { dk4time_get(&start_error); } } } break; } } } if (0 != can_continue(0)) { wrb = remaining; res = dk4socket_send(sock, wrptr, &wrb, 0, 0L, 0L, NULL); switch (res) { case DK4_SOCKET_RESULT_SUCCESS: case DK4_SOCKET_RESULT_IN_PROGRESS : { if (0 < wrb) { bytes_trans = dk4ma_um_add(bytes_trans,wrb,&er_bytes); if (wrb < remaining) { remaining = remaining - wrb; wrptr = &(wrptr[wrb]); } else { remaining = 0; ccwr = 0; } } } break; default: { /* ERROR: Failed to send data */ log_1_no_header(138); ccwr = -1; cc = -1; back = 0; } break; } } else { ccwr = -1; cc = -1; back = 0; } } else { ccwr = -1; cc = -1; back = 0; } } else { ccwr = -1; cc = -1; back = 0; } } while (1 == ccwr); } else { have_eof = 1; cc = 0; if (0 != ferror(stdin)) { /* ERROR: Failed to read from standard input */ log_1_no_header(139); } } } else { cc = -1; back = 0; } } while (1 == cc); log_1_no_header((0 == cc) ? 152 : 153); if (0 == cc) { if (0 != host_ordr) { res = dk4socket_shutdown(sock, DK4_SOCKET_SHUT_WRITE, NULL); if (DK4_SOCKET_RESULT_SUCCESS == res) { cc = 1; do { if (0 != can_continue(0)) { wrb = sizeof(pjsnmp_buf); res = dk4socket_recv(sock, pjsnmp_buf, &wrb, 0, 0L, 0L, NULL); switch (res) { case DK4_SOCKET_RESULT_SUCCESS: { if (0 == wrb) { cc = 0; } } break; default: { cc = -1; } break; } } else { cc = -1; back = 0; } } while (1 == cc); } } } dk4socket_close(sock, NULL); dk4time_get(&t3); } else { $? "! connection failed" /* ERROR: Failed to connect */ log_1_no_header(140); } } else { $? "! host name or port number invalid" } $? "- data_transfer %d", back return back; } /** Write one line of final statistics. @param b Text containing a number. @param max Maximum number width. @param i1 Index in pjsnmp_kw of text before number. @param i2 Index in pjsnmp_kw of text after number. */ static void final_stat_line(const char *b, size_t max, size_t i1, size_t i2) { size_t sl; /* String length of number */ sl = strlen(b); fputs(pjsnmp_kw[i1], out_file); while (sl++ < max) { fputc(' ', out_file); } fputs(b, out_file); fputs(pjsnmp_kw[i2], out_file); } /** Issue final statistics. */ static void final_statistics(void) { char b1[64]; /* Number of bytes */ char b2[64]; /* Transfer time */ char b3[64]; /* Processing time */ char b4[64]; /* Number of pages */ size_t max; /* Maximum of all sizes */ size_t sl; /* Current size */ int res; /* Conversion result */ int found = 0; /* Information found */ /* Initialize buffers. */ b1[0] = '\0'; b2[0] = '\0'; b3[0] = '\0'; b4[0] = '\0'; /* Convert numbers to strings. */ if (DK4_E_NONE == er_bytes.ec) { res = dk4ma_write_c8_decimal_unsigned( b1, sizeof(b1), (dk4_um_t)bytes_trans, 0, NULL ); if (0 == res) { b1[0] = '\0'; } else { found |= 1; } } if (((dk4_time_t)0UL != t2) && ((dk4_time_t)0UL != t3)) { res = dk4ma_write_c8_decimal_unsigned( b2, sizeof(b2), (dk4_um_t)(t3 - t2), 0, NULL ); if (0 == res) { b2[0] = '\0'; } else { found |= 2; } } res = dk4ma_write_c8_decimal_unsigned( b3, sizeof(b3), (dk4_um_t)(t4 - t1), 0, NULL ); if (0 == res) { b3[0] = '\0'; } else { found |= 4; } if ((0 != have_pcs) && (0 != have_pce)) { res = dk4ma_write_c8_decimal_unsigned( b4, sizeof(b4), (dk4_um_t)(pc_end - pc_start), 0, NULL ); if (0 == res) { b4[0] = '\0'; } else { found |= 8; } } /* Find maximum string length. */ max = 0; if (0 != (1 & found)) { max = sl = strlen(b1); } if (0 != (2 & found)) { sl = strlen(b2); if (sl > max) max = sl; } if (0 != (4 & found)) { sl = strlen(b3); if (sl > max) max = sl; } if (0 != (8 & found)) { sl = strlen(b4); if (sl > max) max = sl; } /* Write texts. */ fputs(pjsnmp_kw[55], out_file); (void)log_timestamp(); if (0 != (1 & found)) { final_stat_line(b1, max, 56, 60); } if (0 != (2 & found)) { final_stat_line(b2, max, 57, 61); } if (0 != (4 & found)) { final_stat_line(b3, max, 58, 61); } if (0 != (8 & found)) { final_stat_line(b4, max, 59, 0); } if (0 != found) { fflush(out_file); } } /** Initialization at startup. */ static void initialize_at_startup(void) { stat_text[0] = '\0'; sz_stat_text = 0; ostat_text[0] = '\0'; sz_ostat_text = 0; dk4error_init(&er_bytes); } static void report_at_job_start(int argc, char *argv[]) { int found = 0; char *ptr; if (0 != verbose) { int i; fputs(pjsnmp_kw[169], out_file); for (i = 1; i < argc; i++) { fputs(argv[i], out_file); fputc('\n', out_file); } fflush(out_file); } else { if (NULL != user_name) { fputs(user_name, out_file); found = 1; } if (NULL != queue_name) { if (0 != found) { fputc(' ', out_file); } fputs(queue_name, out_file); found = 1; } if (NULL != job_name) { if (0 != found) { fputc(' ', out_file); } fputs(job_name, out_file); found = 1; } if (0 != found) { fputc('\n', out_file); } if (NULL != job_title) { ptr = job_title; while ('\0' != *ptr) { if (0x80 > ((unsigned char)(*ptr))) { fputc(*ptr, out_file); } else { fputc('?', out_file); } ptr++; } fputc('\n', out_file); found = 1; } if (0 != found) { fflush(out_file); } } } #endif /* if DK4_HAVE_LIBNETSNMP && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) */ /** Main program. @param argc Number of command line arguments. @param argv Command line arguments array. @return Exit status code, one from LPRNG_EXIT_xxx. */ int main(int argc, char *argv[]) { #if DK4_HAVE_LIBNETSNMP && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) /* +++++ All libraries available */ #if DK4_HAVE_SIGACTION #ifdef SIGPIPE struct sigaction opipe; /* Old disposition for SIGPIPE */ struct sigaction npipe; /* New disposition for SIGPIPE */ #endif struct sigaction oint; /* Old disposition for SIGINT */ struct sigaction nint; /* New disposition for SIGINT */ struct sigaction oterm; /* Old disposition for SIGTERM */ struct sigaction nterm; /* New disposition for SIGTERM */ #else #ifdef SIGPIPE dk4_sig_handler_t *opipe = NULL; /* Old disposition for SIGPIPE */ #endif dk4_sig_handler_t *oterm = NULL; /* Old disposition for SIGTERM */ dk4_sig_handler_t *oint = NULL; /* Old disposition for SIGINT */ #endif #if DK4_HAVE_SIGACTION #ifdef SIGPIPE int set_pipe = 0; /* Flag: Disposition SIGPIPE changed */ #endif int set_int = 0; /* Flag: Disposition SIGINT changed */ int set_term = 0; /* Flag: Disposition SIGTERM changed */ int sig_i_f = 0; /* Flag: Handler installation failed */ int sig_r_f = 0; /* Flag: Handler restoration failed */ #endif int sockinit = 0; /* Flag: Socket subsys initialized */ $!trace-init /tmp/pjsnmp.deb $? "+ main" /* Initialize output file. */ out_file = stderr; dk4time_get(&t1); /* Copy default OID for status text. */ DK4_MEMCPY(oid_st, oid_dst, by_oid_dst); sz_oid_st = sz_oid_dst; by_oid_st = sz_oid_st * sizeof(oid); /* Process command line arguments, open log file. */ if (0 == process_command_line_arguments(argc, argv)) { goto finished; } /* Set up signal handlers. */ #if DK4_HAVE_SIGACTION #ifdef SIGPIPE DK4_MEMRES(&npipe, sizeof(npipe)); npipe.sa_handler = sig_handler_pipe; npipe.sa_flags = 0; if (0 != sigemptyset(&npipe.sa_mask)) { sig_i_f = 1; goto finished; } if (0 != sigaddset(&npipe.sa_mask, SIGPIPE)) { sig_i_f = 1; goto finished; } if (0 != sigaction(SIGPIPE, &npipe, &opipe)) { sig_i_f = 1; goto finished; } set_pipe = 1; #endif DK4_MEMRES(&nterm, sizeof(nterm)); nterm.sa_handler = sig_handler_term; nterm.sa_flags = 0; if (0 != sigemptyset(&nterm.sa_mask)) { sig_i_f = 1; goto finished; } if (0 != sigaddset(&nterm.sa_mask, SIGTERM)) { sig_i_f = 1; goto finished; } if (0 != sigaction(SIGTERM, &nterm, &oterm)) { sig_i_f = 1; goto finished; } set_term = 1; DK4_MEMRES(&nint, sizeof(nint)); nint.sa_handler = sig_handler_int; nint.sa_flags = 0; if (0 != sigemptyset(&nint.sa_mask)) { sig_i_f = 1; goto finished; } if (0 != sigaddset(&nint.sa_mask, SIGINT)) { sig_i_f = 1; goto finished; } if (0 != sigaction(SIGINT, &nint, &oint)) { sig_i_f = 1; goto finished; } set_int = 1; #else #ifdef SIGPIPE opipe = sigset(SIGPIPE, sig_handler_pipe); #endif oint = sigset(SIGINT, sig_handler_int); oterm = sigset(SIGTERM, sig_handler_term); #endif /* Process remaining setup (PRINTCAP_ENTRY, model file). */ if (0 == process_setup()) { goto finished; } /* Header files for job in log output. */ report_at_job_start(argc, argv); /* Initialize socket subsystem. */ if (DK4_SOCKET_RESULT_SUCCESS != dk4socket_up(NULL)) { log_1_no_header(170); goto finished; } sockinit = 1; /* Check users permission to print. */ if (0 == check_permission()) { goto finished; } /* Establish SNMP session. */ if (0 == attempt_snmp_session()) { goto finished; } /* Initialization. */ initialize_at_startup(); /* Accounting before job. */ if (0 == do_accounting(0)) { goto finished; } /* Data transfer. */ if (0 == data_transfer()) { goto finished; } /* Accounting after job. */ if (0 == do_accounting(1)) { goto finished; } /* Cleanup ------- */ finished: /* Clean up SNMP session. */ if (NULL != snmp_sess) { snmp_close(snmp_sess); } /* Eat up data from standard input. */ eatup_input(); /* release socket subsystem. */ if (0 != sockinit) { (void)dk4socket_down(NULL); } /* Release status text nodes, if any. */ relase_status_text_nodes(); /* Release dynamically allocated printcap entry copy. */ dk4mem_release(printcap_entry); /* Restore signal handlers */ #if DK4_HAVE_SIGACTION if (0 != sig_i_f) { /* ERROR: Failed to set up signal handlers */ log_1_no_header(141); } if (0 != set_int) { if (0 != sigaction(SIGINT, &oint, NULL)) { sig_r_f = 1; } } if (0 != set_term) { if (0 != sigaction(SIGTERM, &oterm, NULL)) { sig_r_f = 1; } } #ifdef SIGPIPE if (0 != set_pipe) { if (0 != sigaction(SIGPIPE, &opipe, NULL)) { sig_r_f = 1; } } #endif if (0 != sig_r_f) { /* ERROR: Failed to restore signal handlers */ log_1_no_header(142); } #else if (NULL != oterm) { sigset(SIGTERM, oterm); } if (NULL != oint ) { sigset(SIGINT, oint ); } #ifdef SIGPIPE if (NULL != opipe) { sigset(SIGPIPE, opipe); } #endif #endif /* Final log message, close log file */ dk4time_get(&t4); if (2 > decision) { final_statistics(); } if (NULL != stt_file) { fclose(stt_file); } out_file = stderr; $? "- main %d", exval $!trace-end /* ----- All libraries available */ #else /* if DK4_HAVE_LIBNETSNMP && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) */ /* +++++ Missing libraries */ #if !DK4_HAVE_LIBNETSNMP fputs("ERROR: Missing Net-SNMP support!\n", stderr); #endif #if !((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) fputs("ERROR: Missing sigaction()/sigset() support!\n", stderr); #endif fflush(stderr); /* ----- Missing libraries */ #endif /* if DK4_HAVE_LIBNETSNMP && ((DK4_HAVE_SIGACTION) || (DK4_HAVE_SIGSET)) */ exit(exval); return exval; } /* 2016-02-26 Added dk4socket_up() and dk4socket_down(). */