%% options copyright owner = Dirk Krause copyright year = 2012-2014 license = bsd %% header #include "dk3conf.h" #include "dk3types.h" #if DK3_CHAR_SIZE == 1 #if (DK3_HAVE_SIGACTION) || (DK3_HAVE_SIGSET) #if DK3_HAVE_LIBNETSNMP #include "dk3all.h" #include #include #include /** @defgroup pjsnmpexitcodes Exit codes for pjsnmp. */ /**@{*/ /** Exit code: Success. */ #define PJSNMP_EXIT_SUCCESS 0 /** Exit code: Transient error condition, retry later. */ #define PJSNMP_EXIT_FAIL 1 /** Exit code: Unrecoverable error, remove job from queue. */ #define PJSNMP_EXIT_REMOVE 3 /** Exit code: Hold job for later processing. */ #define PJSNMP_EXIT_HOLD 6 /** Exit code: Filter disturbed by signal. */ #define PJSNMP_EXIT_SIGNAL 9 /**@}*/ /** @defgroup pjsnmpdevicestatus Device status from SNMP. */ /**@{*/ /** Device status: Unknown. */ #define PJSNMP_DEVICE_UNKNOWN 1 /** Device status: Device is running. */ #define PJSNMP_DEVICE_RUNNING 2 /** Device status: Warning like paper or toner low. */ #define PJSNMP_DEVICE_WARNING 3 /** Device status: In self-test. */ #define PJSNMP_DEVICE_TESTING 4 /** Device status: Down due to error condition, i.e. paper jam. */ #define PJSNMP_DEVICE_DOWN 5 /**@}*/ /** @defgroup pjsnmpprinterstatus Printer status from SNMP. */ /**@{*/ /** Printer status: Any state not mentioned below. */ #define PJSNMP_PRINTER_OTHER 1 /** Printer status: Unknown. */ #define PJSNMP_PRINTER_UNKNOWN 2 /** Printer status: Ready, waiting for next job. */ #define PJSNMP_PRINTER_IDLE 3 /** Printer status: Printing a job. */ #define PJSNMP_PRINTER_PRINTING 4 /** Printer status: Warming up. */ #define PJSNMP_PRINTER_WARMUP 5 /**@}*/ /** @defgroup pjsnmpprintersummary Printer state summary. */ /**@{*/ /** State summary: Printer unreachable, probably turned off. */ #define PJSNMP_STATE_UNREACHABLE 1 /** State summary: Failed to create SNMP request! */ #define PJSNMP_STATE_REQUEST_FAILED 2 /** State summary: Error in SNMP response! */ #define PJSNMP_STATE_ERROR_IN_RESPONSE 3 /** State summary: Printer ready, can use pagecounter. */ #define PJSNMP_STATE_READY 4 /** State summary: Waiting for printer to start printing. */ #define PJSNMP_STATE_WAIT_START 5 /** State summary: Printer warming up. */ #define PJSNMP_STATE_WARMUP 6 /** State summary: Printing. */ #define PJSNMP_STATE_PRINTING 7 /** State summary: Printing interrupted due to error. */ #define PJSNMP_STATE_ERROR 8 /** State summary: Printer unreachable, probably turned off. */ #define PJSNMP_STATE_OTHER 9 /**@}*/ /** Ensure value to be in range. */ #define PJSNMP_IN_RANGE(a,b,c) (((c) >= (a)) ? (c) : (((c) <= (b)) ? (c) : (a))) /** Printer state. */ typedef struct { unsigned long pc; /**< Pagecounter value. */ unsigned long ec; /**< Error conditions. */ int ds; /**< Device state, see @ref pjsnmpdevicestatus. */ int ps; /**< Printer state, see @ref pjsnmpprinterstatus. */ int st; /**< State summary, see @ref pjsnmpprintersummary. */ } pjsnmp_printer_state_t; /** Job structure. */ typedef struct { struct snmp_session st; /**< Session template. */ pjsnmp_printer_state_t os; /**< Old SNMP state. */ pjsnmp_printer_state_t cs; /**< Current SNMP state. */ struct snmp_session *ss; /**< SNMP session. */ char const *un; /**< User name. */ char const *qn; /**< Printer name. */ char const *jn; /**< Job name. */ char const *jt; /**< Job title. */ char const *sf; /**< Status file. */ dk3_app_t *app; /**< Application structure. */ dkChar const * const *lm; /**< Localized message texts. */ char *hn; /**< Host name to transfer data to. */ char *af; /**< Accounting file/socket name. */ char *sc; /**< SNMP community. */ char *tf; /**< Temporary file. */ char *sl; /**< SNMP log file. */ time_t ti; /**< Start time of stage 2. */ time_t tl; /**< Time of last log. */ unsigned long pc; /**< Page count at start of job. */ int ex; /**< Exit status code. */ int ac; /**< Flag: achk accounting check. */ int as; /**< Accounting style: 0=printqd, 1=lprng. */ int sv; /**< SNMP version. */ int od; /**< Flag: Orderly release printer socket. */ int fe; /**< Flag: ERROR or signal, job cancelled. */ int ct; /**< Flag: SNMP check during data transfer. */ int is; /**< Flag: Ignore SNMP failure. */ int wp; /**< Flag: Printer was seen printing. */ int hl; /**< Flag: Already have log messages. */ int pdes; /**< Flag: Use hrPrinterDetectedErrorState. */ int poid; /**< Flag: running/warning+other = standby. */ int repr; /**< Flag: Report response. */ unsigned short pn; /**< Port name to transfer data to. */ } pjsnmp_job_t; #endif #endif #endif %% module #include "dk3conf.h" #include "dk3types.h" #if DK3_CHAR_SIZE == 1 #if DK3_HAVE_SIGSET #if DK3_HAVE_LIBNETSNMP #include "dk3all.h" #include #include #include #include "printqd.h" #include "pjsnmp.h" $!trace-include /** Constant keywords used by the module, not localized. */ static char const * const pjsnmp_kw[] = { $!string-table # # 0 Application group name. # dkt-3 # # 1 String table name. # pjsnmp.str # # 2 Environment variable name. # PRINTCAP_ENTRY # # 3 Keyword acct-check # acct-check # # 4 Keyword jobstart # jobstart # # 5 Space used in accounting requests. # # # 6 Newline used in accounting requests. # \n # # 7 # ' # # 8 # '-P # # 9 # '-n # # 10 Program name # pjsnmp # # 11 SNMP default community # public # # 12 File open mode: Write # w # # 13 File open mode: Append # a # # 14 Keyword filestart # filestart # # 15 Keyword fileend # fileend # # 16 Keyword acct-start # acct-start # # 17 keyword acct-end # acct-end # # 18 sprintf format # %lu # # 19 used for LPRng accounting # '-p # # 20 used for LPRng accounting # '-A # # 21 used for LPRng accounting # '-J # # 22 File mode for binary writing # wb # # 23 File mode for binary reading # rb # # 24 Format for sscanf # %d # # 25 Format for sscanf # %u # # 26 Format for sprintf, filename for temporary file # pjsnmp-%lu.dat # # 27 Format for sprintf, filename for SNMP log file # pjsnmp-%lu.log # # 28 Format string for timestamp # \# %04d-%02d-%02d %02d:%02d:%02d\n $!end }; /** Expected responses from accounting check. */ static char const * const pjsnmp_achk_responses[] = { $!string-table ACCEPT REMOVE HOLD $!end }; /** Keys in printcap entry. */ static char const * const pjsnmp_printcap_entries[] = { $!string-table # 0 pjsnmpaf # 1 pjsnmpachk # 2 pjsnmphost # 3 pjsnmpport # 4 pjsnmpvers # 5 pjsnmpcomm # 6 pjsnmpacst # 7 pjsnmpordr # 8 pjsnmpscdt # 9 pjsnmpigsf # 10 pjsnmppdes # 11 pjsnmppoid # 12 pjsnmpresp $!end }; /** Localized message texts. The texts here are used as defaults if the localized message texts are not found. */ static char const * const pjsnmp_loc[] = { $!string-table file=pjsnmp.str # # NOTE: Status messages from pjsnmp are shown by lpq as Filter_Status # lines. The lpq program or equivalents are run not only on the print # server but on print clients too. # # Print clients are not required to use the same encoding as the print # server. Some Linux/Unix clients use UTF-8 encoding, others don't. # # CONCLUSION: When localizing this string file stick to the ASCII # character set. Avoid any characters requiring UTF-8 encoding, i.e. # german umlauts. # # # # 0 Status: Printer ready. # Printer ready. # # 1 Status: Printer warming up. # Printer warming up. # # 2 Status: Printing. # Printing. # # 3 Status: Waiting for printer to start printing. # Waiting for printer to start printing. # # 4 Status: Error on printer. # ERROR ON PRINTER, INVERVENTION REQUIRED! (OUT OF PAPER, PAPER JAM...) # # 5 Status: Printer in unknown state. # UNKNOWN PRINTER STATE. IF IN STANDBY, PLEASE WAKE UP MANUALLY. # # 6 Status: Printer does not respond. # PRINTER DOES NOT RESPOND! PLEASE POWER-ON PRINTER! # # 7 Status: Failed to create SNMP request. # ERROR: Failed to create SNMP request! # # 8 Status: Invalid SNMP response from printer! # ERROR: Invalid SNMP response from printer! # # 9 SNMP response: # SNMP response: # # 10 device=0 # device=0, # # 11 device=unknown # device=unknown (1), # # 12 device=running # device=running (2), # # 13 device=warning # device=warning (3), # # 14 device=testing # device=testing (4), # # 15 device=down # device=down (5), # # 16 printer=0 # printer=0. # # 17 printer=other # printer=other (1). # # 18 printer=unknown # printer=unknown (2). # # 19 printer=idle # printer=idle (3). # # 20 printer=printing # printer=printing (4). # # 21 printer=warmup # printer=warmup (5). # # 22 Error: Out of paper! # PRINTER ERROR CONDITION: OUT OF PAPER! # # 23 Information: Paper available. # Printer condition resolved: Paper now available. # # 24 Error: Paper low. # Printer warning condition: Paper low. # # 25 Information: No more paper low. # Printer condition resolved: No longer paper low. # # 26 Error: Out of toner! # PRINTER ERROR CONDITION: OUT OF TONER! # # 27 Information: Error condition resolved (no toner). # Printer condition solved: Toner available. # # 28 Warning condition: toner low. # Printer warning condition: Toner low. # # 29 Information: Printer conditions resolved (toner low). # Printer condition resolved: No longer toner low. # # 30 Error: Output tray full! # PRINTER ERROR CONDITION: OUTPUT TRAY FULL! # # 31 Information: Error condition resolved (output tray full). # Error condition resolved: Output tray no longer full. # # 32 Warning: Output tray nearly full! # Printer warning condition: Output tray nearly full! # # 33 Information: Warning condition resolved (output tray nearly full). # Error condition resolved: Output tray no longer nearly full. # # 34 Error: Door open! # PRINTER ERROR CONDITION: DOOR OPEN! # # 35 Information: Door now closed. # Error condition resolved: Door now closed. # # 36 Error: Paper jam! # PRINTER ERROR CONDITION: PAPER JAM! # # 37 Information: Error condition resolved (paper jam). # Error condition resolved: No longer paper jam. # # 38 Error: Printer offline! # PRINTER ERROR CONDITION: OFFLINE FOR SOME REASON! # # 39 Information: Error condition resolved (printer offline). # Error condition resolved: No longer offline. # # 40 Error: Service requested! # PRINTER ERROR CONDITION: SERVICE REQUESTED! # # 41 Information: Error condition resolved (service requested). # Error condition resolved: Service no longer requested. # # 42 Error: Input tray missing! # PRINTER ERROR CONDITION: INPUT TRAY MISSING! # # 43 Information: Error condition resolved (input tray missing). # Error condition resolved: Input tray now available. # # 44 Error: Output tray missing! # PRINTER ERROR CONDITION: OUTPUT TRAY MISSING! # # 45 Information: Error condition resolved (output tray missing). # Error condition resolved: Output tray now available. # # 46 ERROR: Marker supply missing! # PRINTER ERROR CONDITION: MARKER SUPPLY MISSING! # # 47 Information: Error condition resolved (marker supply missing). # Error condition resolved: Marker supply now available. # # 48 Error: Input tray empty! # PRINTER ERROR CONDITION: INPUT TRAY EMPTY! # # 49 Information: Error condition resolved (input tray empty). # Error condition resolved: Input tray no longer empty. # # 50 Error: Overdue preventive maintenance! # PRINTER ERROR CONDITION: OVERDUE PREVENTIVE MAINTENANCE! # # 51 Information: Error condition resolved (overdue preventive maintenance). # Error condition resolved: No longer overdue preventive maintenance. # # 52/53 Error: Configuration entry "..." needs an argument! # Error: Configuration entry " " needs an argument! # # 54 # Print job finished, errors occured! # # 55 # Print job finished successfully. # # 56/57 # Print job finished successfully, page(s). # # 58 # Print job cancelled by signal! # # 59 # ERROR: PRINTCAP_ENTRY environment variable not found! # # 60 # ERROR: Insufficient memory! # # 61 # ERROR: Missing host name for printer (not configured)! # # 62 # ERROR: Missing port number for printer (not configured)! # # 63/64 # ERROR: Illegal accounting style in configuration: " "! # # 65/66 # ERROR: Illegal SNMP version in configuration: " "! # # 67/68 # ERROR: Illegal port number in configuration (not a number): " "! # # 69/70 # ERROR: Illegal port number in configuration (overflow): " "! # # 71 # ERROR: Failed to save standard input to temporary file! # # 72 # ERROR: Failed to open SNMP session! # # 73 # Warning: Failed to open SNMP session! # # 74 # ERROR: Failed to open temporary data file! # # 75 # ERROR: Failed to open printer connection! # # 76 # ERROR: Attempt to send data failed! # # 77 # ERROR: Failed to create SNMP request PDU! Resources shortage! # # 78 # ERROR: Accounting request grows too large! # # 79 # ERROR: Failed to find user name for current print job! # # 80 # ERROR: Failed to find queue name for current print job! # # 81 # ERROR: Accounting file is not a UNIX domain socket! # # 82 # ERROR: No permission to use this printer! # # 83 # ERROR: Failed to connect to accounting socket! # # 84 # ERROR: Failed to send accounting data to socket! # # 85 # ERROR: Failed to shut down accounting socket! # # 86 # Page counter at job start: # # 87 # Page counter at job end: # # 88 # Printer reponse: # # 89 # ERROR: Failed to set up signal handling! $!end }; /** SNMP versions handled by pjsnmp. */ static char const * const pjsnmp_snmp_versions[] = { "1", "2c", "2p", "3", NULL }; /** Accounting styles supported by pjsnmp. */ static char const * const pjsnmp_accounting_styles[] = { "printqd", "lprng", NULL }; /** OID for device status. */ static oid pjsnmp_oid_ds[MAX_OID_LEN] = { (oid)1, (oid)3, (oid)6, (oid)1, (oid)2, (oid)1, (oid)25, (oid)3, (oid)2, (oid)1, (oid)5, (oid)1 }; /** Size of device status OID. */ static size_t const pjsnmp_sz_oid_ds = 12; /** OID for printer status. */ static oid pjsnmp_oid_ps[MAX_OID_LEN] = { (oid)1, (oid)3, (oid)6, (oid)1, (oid)2, (oid)1, (oid)25, (oid)3, (oid)5, (oid)1, (oid)1, (oid)1 }; /** Size of printer status OID. */ static size_t const pjsnmp_sz_oid_ps = 12; /** OID for pagecount value. */ static oid pjsnmp_oid_pc[MAX_OID_LEN] = { (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 }; /** Size of page counter OID. */ static size_t const pjsnmp_sz_oid_pc = 13; /** Printer error OID. */ static oid pjsnmp_oid_pe[MAX_OID_LEN] = { (oid)1, (oid)3, (oid)6, (oid)1, (oid)2, (oid)1, (oid)25, (oid)3, (oid)5, (oid)1, (oid)2, (oid)1 }; /** Size of printer error OID. */ static size_t const pjsnmp_sz_oid_pe = 12; /** Bits within a bit starting on the left side. */ static unsigned char const pjsnmp_bits[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; /** Error conditions to report as warning only (from hrPrinterDetectedErrorState). */ static unsigned long const pjsnmp_warning_only = DK3_SNMP_PRINTER_ERROR_LOW_PAPER | DK3_SNMP_PRINTER_ERROR_LOW_TONER | DK3_SNMP_PRINTER_ERROR_OUTPUT_NEAR ; /** Flag: SIGINT signal was found. */ static DK3_VOLATILE dk3_sig_atomic_t pjsnmp_sigint_received = 0; /** Read data from volatile variable. @param vp Adress of variable. @return Variable value. */ dk3_sig_atomic_t pjsnmp_read_vol(DK3_VOLATILE dk3_sig_atomic_t *vp) { return *vp; } /** Pass through address of volatile variable. @param vp Address of a volatile variable. @return Pointer vp. */ DK3_VOLATILE dk3_sig_atomic_t * pjsnmp_pass_vol_ptr(DK3_VOLATILE dk3_sig_atomic_t *vp) { return vp; } /** Handler for signal SIGINT. @param signo Signal number. */ static void pjsnmp_sigint_handler(int signo) { #if 0 pjsnmp_sigint_received = 1; #else *pjsnmp_pass_vol_ptr(&pjsnmp_sigint_received) = 1; #endif } /** Flag: SIGTERM signal was found. */ static DK3_VOLATILE dk3_sig_atomic_t pjsnmp_sigterm_received = 0; /** Handler for signal SIGTERM. @param signo Signal number. */ static void pjsnmp_sigterm_handler(int signo) { #if 0 pjsnmp_sigterm_received = 1; #else *pjsnmp_pass_vol_ptr(&pjsnmp_sigterm_received) = 1; #endif } /** Flag: SIGPIPE signal was found. */ static DK3_VOLATILE dk3_sig_atomic_t pjsnmp_sigpipe_received = 0; /** Handler for signal SIGPIPE. @param signo Signal number. */ static void pjsnmp_sigpipe_handler(int signo) { #if 0 pjsnmp_sigpipe_received = 1; #else *pjsnmp_pass_vol_ptr(&pjsnmp_sigpipe_received) = 1; #endif } /** Log a message consisting of multiple parts. @param job Job structure. @param msg Message texts array. @param nmsg Size of array. */ static void pjsnmp_log_multi(pjsnmp_job_t *job, char const * const *msg, size_t nmsg) { FILE *fipo; char const * const *ptr; struct tm *tm; size_t i; time_t ct; if(job->sf) { fipo = dk3sf_fopen_app(job->sf, pjsnmp_kw[(job->hl) ? 13 : 12 ], job->app); if(fipo) { time(&ct); if(ct != job->tl) { tm = localtime(&ct); if(tm) { job->tl = ct; fprintf( fipo, pjsnmp_kw[28], (1900 + tm->tm_year), (1 + tm->tm_mon), tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec ); } } ptr = msg; for(i = 0; i < nmsg; i++) { fputs(*(ptr++), fipo); } fputc('\n', fipo); fclose(fipo); job->hl = 1; } } } /** Log message consisting of 1 part. @param job Job structure. @param m1 Message part 1. */ static void pjsnmp_log_1(pjsnmp_job_t *job, char const *m1) { char const *msg[2]; msg[0] = m1; msg[1] = NULL; pjsnmp_log_multi(job, msg, 1); } /** Log message consisting of 2 parts. @param job Job structure. @param m1 Message part 1. @param m2 Message part 2. */ static void pjsnmp_log_2(pjsnmp_job_t *job, char const *m1, char const *m2) { char const *msg[3]; msg[0] = m1; msg[1] = m2; msg[2] = NULL; pjsnmp_log_multi(job, msg, 2); } /** Log message consisting of 3 parts. @param job Job structure. @param m1 Message part 1. @param m2 Message part 2. @param m3 Message part 3. */ static void pjsnmp_log_3(pjsnmp_job_t *job, char const *m1, char const *m2, char const *m3) { char const *msg[4]; msg[0] = m1; msg[1] = m2; msg[2] = m3; msg[3] = NULL; pjsnmp_log_multi(job, msg, 3); } /** Check whether or not we can continue. On signal or unrecoverable errors we give up. @param job Job structure. @param fe Flag: Take fe component into account. @return 1 to continue normally, 0 to exit. */ static int pjsnmp_cc(pjsnmp_job_t *job, int fe) { int back = 1; $? "+ pjsnmp_cc %d", fe if(pjsnmp_read_vol(&pjsnmp_sigterm_received)) { $? ". sigterm" back = 0; } else { if(pjsnmp_read_vol(&pjsnmp_sigint_received)) { $? ". sigint" back = 0; } else { if(job) { if((fe) && (job->fe)) { $? ". fe" back = 0; } } } } $? "- pjsnmp_cc %d", back return back; } /** Initialize printer state structure. @param s Structure to initialize. */ static void pjsnmp_state_init(pjsnmp_printer_state_t *s) { dk3mem_res((void *)s, sizeof(pjsnmp_printer_state_t)); s->pc = 0UL; s->ec = 0UL; s->ds = 0; s->ps = 0; s->st = 0; } /** Eat up input on errors, so no SIGPIPE signal is sent to LPRng. */ static void pjsnmp_eat_input(pjsnmp_job_t *job) { char bu[4096]; size_t sz; $? "+ pjsnmp_eat_input" do { sz = dk3sf_read_app(0, bu, sizeof(bu), NULL); } while((sz > 0) && (pjsnmp_cc(job,0))); $? "- pjsnmp_eat_input" } /** Initialize job structure. @param job Job structure to initialize. */ static void pjsnmp_job_init(pjsnmp_job_t *job) { $? "+ pjsnmp_job_init" dk3mem_res((void *)job, sizeof(pjsnmp_job_t)); job->ss = NULL; job->un = NULL; job->qn = NULL; job->jn = NULL; job->jt = NULL; job->app = NULL; job->lm = NULL; job->hn = NULL; job->af = NULL; job->sc = NULL; job->tf = NULL; job->sl = NULL; job->ti = (time_t)0UL; job->tl = (time_t)0UL; job->pc = 0UL; job->sf = NULL; job->ex = PJSNMP_EXIT_FAIL; job->ac = 0; job->as = 0; job->sv = 0; job->od = 0; job->fe = 0; job->pn = 9100; job->ct = 0; job->is = 0; job->wp = 0; job->hl = 0; job->pdes = 1; job->poid = 1; job->repr = 0; $? "- pjsnmp_job_init" } /** Clean up job structure. @param job Job structure to clean up. */ static void pjsnmp_job_cleanup(pjsnmp_job_t *job) { $? "+ pjsnmp_job_cleanup" if(job->sl) { $? ". Release log file name \"%s\"", job->sl dk3sf_c8_remove_file_app(job->sl, job->app); dk3_release(job->sl); } if(job->tf) { $? ". Release tmp file name \"%s\"", job->tf dk3sf_c8_remove_file_app(job->tf, job->app); dk3_release(job->tf); } if(job->app) { $? ". Close application" dk3app_close(job->app); job->app = NULL; } $? "- pjsnmp_job_cleanup" } /** Check two OIDs for equality. @param o1 Left OID. @param sz1 Left OID size. @param o2 Right OID. @param sz2 Right OID size. @return 1 if the OIDs are equal, 0 otherwise. */ static int pjsnmp_oids_equal(oid const *o1, size_t sz1, oid const *o2, size_t sz2) { oid const *p1; oid const *p2; size_t i; int back = 0; $? "+ pjsnmp_oids_equal" if(sz1 == sz2) { back = 1; p1 = o1; p2 = o2; for(i = 0; ((i < sz1) && (back)); i++) { if(*(p1++) != *(p2++)) { back = 0; } } } $? "- pjsnmp_oids_equal %d", back return back; } /** Apply command line arguments to job structure. @param job Job structure. */ static void pjsnmp_apply_command_line_arguments(pjsnmp_job_t *job) { char const *upper[26]; /* Upper case option arguments. */ char const *lower[26]; /* Lower case option arguments. */ char const * const *xargv; /* Command line arguments array. */ char const *ptr; /* Current argument. */ int i; /* Current argument index. */ int xargc; /* Number of command line arguments. */ $? "+ pjsnmp_apply_command_line_arguments" for(i = 0; i < 26; i++) { upper[i] = NULL; lower[i] = NULL; } xargc = dk3app_get_argc(job->app); xargv = dk3app_get_argv(job->app); for(i = 1; i < xargc; i++) { ptr = xargv[i]; $? ". argument \"%s\"", ptr if(ptr[0] == '-') { if(('a' <= ptr[1]) && ('z' >= ptr[1])) { lower[ptr[1] - 'a'] = &(ptr[2]); } else { if(('A' <= ptr[1]) && ('Z' >= ptr[1])) { upper[ptr[1] - 'A'] = &(ptr[2]); } } } } job->un = lower['n' - 'a']; if(!(job->un)) { job->un = upper['L' - 'A']; } job->qn = upper['P' - 'A']; if(!(job->qn)) { job->qn = upper['Q' - 'A']; } job->jn = upper[0]; if(!(job->jn)) { job->jn = lower['j' - 'a']; } if(!(job->jn)) { job->jn = lower['t' - 'a']; } if(!(job->jn)) { job->jn = upper['D' - 'A']; } job->jt = upper['J' - 'A']; if(!(job->jt)) { job->jt = lower['f' - 'a']; } if(!(job->jt)) { job->jt = upper['N' - 'A']; } if(!(job->jt)) { job->jt = job->jn; } job->sf = lower['s' - 'a']; $? ". un = \"%s\"", TR_STR(job->un) $? ". qn = \"%s\"", TR_STR(job->qn) $? ". jn = \"%s\"", TR_STR(job->jn) $? ". jt = \"%s\"", TR_STR(job->jt) $? ". sf = \"%s\"", TR_STR(job->sf) $? "- pjsnmp_apply_command_line_arguments" } /** Read standard input to temporary file. @param job Job structure. @return 1 on success, 0 on error. */ static int pjsnmp_read_standard_input(pjsnmp_job_t *job) { char bu[4096]; /* Buffer. */ FILE *fipo; /* Temporary file to write. */ size_t rb; /* Number of bytes read. */ int back = 0; $? "+ pjsnmp_read_standard_input" fipo = dk3sf_fopen_app(job->tf, pjsnmp_kw[22], job->app); if(fipo) { $? ". fipo" back = 1; do { rb = dk3sf_read_app(0, (void *)bu, sizeof(bu), job->app); if(0 < rb) { $? ". rb" if((back) && (pjsnmp_cc(job, 0))) { if(!dk3sf_fwrite_app(bu, 1, rb, fipo, job->app)) { $? "! fwrite" back = 0; } } } } while((0 < rb) && (pjsnmp_cc(job, 0))); if(!dk3sf_fclose_fn_app(fipo, job->tf, job->app)) { $? "! fclose" back = 0; } } else { $? "! fipo" pjsnmp_eat_input(job); } $? "- pjsnmp_read_standard_input %d", back return back; } /** Check whether the accounting file exists and is a socket. @param job Job structure. @return 1 if the accounting file is a UNIX domain socket, 0 otherwise. */ static int pjsnmp_is_socket(pjsnmp_job_t *job) { struct stat stb; int back = 0; $? "+ pjsnmp_is_socket" if(0 == stat(job->af, &stb)) { if(((stb.st_mode) & S_IFMT) == S_IFSOCK) { back = 1; } } $? "- pjsnmp_is_socket %d", back return back; } /** Run accounting dialog with server socket. @param job Job structure. @param rq Request to send to server. @param rs Response buffer. @param szrs Size of response buffer. @return 1 on success, 0 on error. */ static int pjsnmp_accounting_dialog( pjsnmp_job_t *job, char const *rq, char *rs, size_t szrs ) { char bu[PQD_OUTPUT_BUFFER_SIZE]; /* Response buffer. */ size_t sl; /* Request string length. */ dk3_socket_t sock; /* Socket. */ int wb; /* Bytes written/read. */ int back = 0; $? "+ pjsnmp_accounting_dialog" if(rs) { *rs = '\0'; } sl = strlen(rq); sock = dk3socket_un_stream_client(job->af, 0L, 0L, NULL, job->app); if(INVALID_SOCKET != sock) { $? ". socket" wb = dk3socket_send(sock, rq, sl, 0L, 0L, NULL, job->app); if(wb == (int)sl) { $? ". send" if(dk3socket_shutdown(sock, DK3_TCPIP_SHUTDOWN_WRITE, NULL, job->app)) { if(!(rs)) back = 1; do { wb = dk3socket_recv(sock, bu, (sizeof(bu)-1), 0L, 0L, NULL, job->app); if((rs) && (back == 0) && (wb > 0)) { bu[wb] = '\0'; strcpy(rs, bu); back = 1; } } while((wb > 0) && (pjsnmp_cc(job,0))); } else { /* ERROR: Failed to shut down accounting socket! */ pjsnmp_log_1(job, (job->lm)[85]); } } else { $? "! send" /* ERROR: Failed to send accounting data to socket! */ pjsnmp_log_1(job, (job->lm)[84]); } dk3socket_close(sock, NULL, job->app); } else { $? "! socket" /* ERROR: Failed to open accounting socket! */ pjsnmp_log_1(job, (job->lm)[83]); } $? "- pjsnmp_accounting_dialog %d", back return back; } /** Construct jobstart request. @param job Job structure. @param rq Request buffer. @param szrq Size of request buffer. @return 1 on success, 0 on error. */ static int pjsnmp_construct_achk_request(pjsnmp_job_t *job, char *rq, size_t szrq) { size_t sl; int back = 0; $? "+ pjsnmp_construct_achk_request" if(rq) *rq = '\0'; sl = strlen(pjsnmp_kw[(job->as) ? 4 : 3]); sl += strlen(job->un); sl += strlen(job->qn); sl += 4; if(job->as) { sl += 8; } if(sl < szrq) { $? ". sl" back = 1; strcpy(rq, pjsnmp_kw[(job->as) ? 4 : 3]); strcat(rq, pjsnmp_kw[5]); if(job->as) { strcat(rq, pjsnmp_kw[8]); } strcat(rq, job->qn); if(job->as) { strcat(rq, pjsnmp_kw[7]); } strcat(rq, pjsnmp_kw[5]); if(job->as) { strcat(rq, pjsnmp_kw[9]); } strcat(rq, job->un); if(job->as) { strcat(rq, pjsnmp_kw[7]); } strcat(rq, pjsnmp_kw[6]); } else { $? "! sl" } $? "- pjsnmp_construct_achk_request %d \"%s\"", back, TR_STR(rq) return back; } /** Check whether the user is allowed to print on the queue. @param job Job structure. @return 1 if printing is allowed, 0 otherwise. */ static int pjsnmp_achk(pjsnmp_job_t *job) { char rq[PQD_INPUT_BUFFER_SIZE]; /* Request buffer. */ char rs[PQD_OUTPUT_BUFFER_SIZE]; /* Response buffer. */ int back = 1; $? "+ pjsnmp_achk" if((job->af) && (job->ac)) { $? ". af, ac" back = 0; if(job->un) { $? ". un" if(job->qn) { $? ". qn" if(pjsnmp_is_socket(job)) { $? ". is socket" if(pjsnmp_construct_achk_request(job, rq, sizeof(rq))) { $? ". rq" if(pjsnmp_accounting_dialog(job, rq, rs, sizeof(rs))) { $? ". dia" dk3str_c8_delnl(rs); dk3str_c8_normalize(rs, NULL, ' '); switch(dk3str_c8_array_index(pjsnmp_achk_responses, rs, 0)) { case 0: { $? ". ACCEPT" back = 1; } break; case 1: { $? ". REMOVE" job->ex = PJSNMP_EXIT_REMOVE; /* ERROR: Printing not allowed! */ pjsnmp_log_1(job, (job->lm)[82]); } break; case 2: { $? ". HOLD" job->ex = PJSNMP_EXIT_HOLD; /* ERROR: Printing not allowed! */ pjsnmp_log_1(job, (job->lm)[82]); } break; } } else { $? "! dialog" } } else { $? "! rq" /* ERROR: Printer or user name too long! */ pjsnmp_log_1(job, (job->lm)[78]); } } else { $? "! no a socket" /* ERROR: Accounting file not a socket! */ pjsnmp_log_1(job, (job->lm)[81]); } } else { $? "! qn" /* ERROR: Queue name for printer unknown! */ pjsnmp_log_1(job, (job->lm)[80]); } } else { $? "! un" /* ERROR: User name for print job unknown! */ pjsnmp_log_1(job, (job->lm)[79]); } } $? "- pjsnmp_achk %d", back return back; } /** Open SNMP session. @param job Job structure. */ static void pjsnmp_open_session(pjsnmp_job_t *job) { $? "+ pjsnmp_open_session" snmp_sess_init(&(job->st)); (job->st).version = SNMP_VERSION_2c; switch(job->sv) { case DK3_SNMP_VERSION_1: { (job->st).version = SNMP_VERSION_1; } break; case DK3_SNMP_VERSION_2P: { (job->st).version = SNMP_VERSION_2p; } break; case DK3_SNMP_VERSION_3: { (job->st).version = SNMP_VERSION_3; } break; } (job->st).peername = job->hn; (job->st).community = (unsigned char *)(job->sc); if(!((job->st).community)) { (job->st).community = (unsigned char *)(pjsnmp_kw[11]); } (job->st).community_len = strlen((char *)((job->st).community)); job->ss = snmp_open(&(job->st)); $? "- pjsnmp_open_session" } $!trace-code static $!trace-code void $!trace-code pjsnmp_show_oid(pjsnmp_job_t *job, oid *oi, size_t szoi) $!trace-code { $!trace-code size_t i; $!trace-code if(dktrace_file()) { $!trace-code fprintf(dktrace_file(), "LGT=%u ", (unsigned)szoi); $!trace-code for(i = 0; i < szoi; i++) { $!trace-code if(i) { fprintf(dktrace_file(), "."); } $!trace-code fprintf(dktrace_file(), "%u", (unsigned)(oi[i])); $!trace-code } $!trace-code fprintf(dktrace_file(), "\n"); $!trace-code } $!trace-code } /** Retrieve int value from SNMP variable. @param job Job structure. @param dp Destination pointer. @param v SNMP variable to process. @return 1 on success, 0 on error. */ static int pjsnmp_get_int( pjsnmp_job_t *job, int *dp, struct variable_list *v ) { char bu[512]; /* Buffer for private copy. */ int back = 0; $? "+ pjsnmp_get_int" switch(v->type) { case ASN_OCTET_STR: { if((v->val_len >= 0) && (v->val_len < sizeof(bu))) { dk3mem_cpy((void *)bu, (void *)((v->val).string), v->val_len); bu[v->val_len] = '\0'; #if VERSION_BEFORE_20140716 if(1 == sscanf(bu, pjsnmp_kw[24], dp)) #else if (0 != dk3ma_i_from_c8_string(dp, bu, NULL)) #endif { back = 1; } } } break; case ASN_TIMETICKS: case ASN_GAUGE: case ASN_COUNTER: case ASN_INTEGER: { *dp = (int)(*(v->val).integer); back = 1; } break; } $? "- pjsnmp_get_int %d %d", back, *dp return back; } /** Retrieve unsigned long value from SNMP variable. @param job Job structure. @param dp Destination pointer. @param v SNMP variable to process. @return 1 on success, 0 on error. */ static int pjsnmp_get_ul( pjsnmp_job_t *job, unsigned long *dp, struct variable_list *v ) { char bu[512]; /* Buffer for private copy. */ int back = 0; $? "+ pjsnmp_get_ul" switch(v->type) { case ASN_OCTET_STR: { if((v->val_len >= 0) && (v->val_len < sizeof(bu))) { dk3mem_cpy((void *)bu, (void *)((v->val).string), v->val_len); bu[v->val_len] = '\0'; #if VERSION_BEFORE_20140716 if(1 == sscanf(bu, pjsnmp_kw[18], dp)) #else if (0 != dk3ma_ul_from_c8_string(dp, bu, NULL)) #endif { back = 1; } } } break; case ASN_TIMETICKS: case ASN_GAUGE: case ASN_COUNTER: case ASN_INTEGER: { *dp = (unsigned long)(*(v->val).integer); back = 1; } break; } $? "- pjsnmp_get_ul %d %lu", back, *dp return back; } /** Convert printer error condition from octets to unsigned long. @param job Job structure. @param dp Destination pointer. @param v Variable to use. @return 1 on success, 0 on error. */ static int pjsnmp_get_bytes_inverted( pjsnmp_job_t *job, unsigned long *dp, struct variable_list *v ) { unsigned char *ucp; /* Current byte pointer. */ unsigned long v1; /* Bit to set. */ unsigned long value = 0UL; size_t i; /* Current byte index. */ size_t j; /* Bit in byte. */ int back = 0; $? "+ pjsnmp_get_bytes_inverted" switch(v->type) { case ASN_OCTET_STR: { back = 1; v1 = 1UL; ucp = (v->val).string; for(i = 0; i < v->val_len; i++) { if(i < sizeof(unsigned long)) { for(j = 0; j < 8; j++) { if((*ucp) & (pjsnmp_bits[j])) { value |= v1; } v1 = v1 * 2UL; } } else { back = 0; } ucp++; } *dp = value; $? ". value = %lu", value } break; } $? "- pjsnmp_get_bytes_inverted %d", back return back; } /** Check whether device and printer status indicate printer is printing. @param ds Device status from SNMP request. @param ps Printer status from SNMP request. @return 1 if printer is printing, 0 otherwise. */ static int pjsnmp_is_printing(int ds, int ps) { int back = 0; if(PJSNMP_PRINTER_PRINTING == ps) { switch(ds) { case PJSNMP_DEVICE_RUNNING: case PJSNMP_DEVICE_WARNING: { back = 1; } break; } } $? "= pjsnmp_is_printing %d %d => %d", ds, ps, back return back; } /** Check whether device and printer status indicate printer is ready. @param ds Device status from SNMP response. @param ps Printer status from SNMP response. @return 1 if printer is ready, 0 otherwise. */ static int pjsnmp_is_ready(int ds, int ps) { int back = 0; if(PJSNMP_PRINTER_IDLE == ps) { switch(ds) { case PJSNMP_DEVICE_RUNNING: case PJSNMP_DEVICE_WARNING: { back = 1; } break; } } $? "= pjsnmp_is_ready %d %d => %d", ds, ps, back return back; } /** Check whether hrDeviceStatus and hrPrinterStatus indicate warmup. @param ds Device status. @param ps Printer status. @return 1 for warmup, 0 for other state. */ int pjsnmp_is_warming_up(int ds, int ps) { int back = 0; if(PJSNMP_PRINTER_WARMUP == ps) { switch(ds) { case PJSNMP_DEVICE_DOWN: case PJSNMP_DEVICE_WARNING: case PJSNMP_DEVICE_RUNNING: case PJSNMP_DEVICE_TESTING: { back = 1; } break; } } return back; } /** Check whether printer is in standby mode. @param job Job structure. @param ds Device status from SNMP request. @param ps Printer status from SNMP request. @param ec Error conditions from SNMP. @return 1 if printer is in standby, 0 otherwise. */ static int pjsnmp_is_standby(pjsnmp_job_t *job, int ds, int ps, unsigned long ec) { int back = 0; /* H. Edlund suggests the combination device=running/printer=other as sure to retrieve the pagecount in "Design and Implementation of Accounting and Status Notification for the LPRng Print Spooler" as this combintation indicates the standby state of the printer. On my printer this combination also occurs if the paper tray is opened or the toner door is open. May be we need to check other conditions additionally. */ #if 0 if(PJSNMP_DEVICE_RUNNING == ds) { if(PJSNMP_PRINTER_OTHER == ps) { back = 1; } } #endif if(PJSNMP_PRINTER_OTHER == ps) { switch(ds) { case PJSNMP_DEVICE_RUNNING: case PJSNMP_DEVICE_WARNING: { if(job->poid) { if(!(ec & (~(pjsnmp_warning_only)))) { back = 1; } } } break; } } $? "= pjsnmp_is_standby %d %d => %d", ds, ps, back return back; } /** Set summary for current device and printer state. @param job Job structure. @param stage Processing stage (0=start, 1=in job, 2=end). */ static void pjsnmp_set_summary_state(pjsnmp_job_t *job, int stage) { time_t ct; /* Current time. */ $? "+ pjsnmp_set_summary_state %d", stage if(pjsnmp_is_ready((job->cs).ds, (job->cs).ps)) { (job->cs).st = PJSNMP_STATE_READY; } else { if(pjsnmp_is_printing((job->cs).ds, (job->cs).ps)) { (job->cs).st = PJSNMP_STATE_PRINTING; switch(stage) { case 1: case 2: { job->wp = 1; } break; } } else { if(pjsnmp_is_standby(job, (job->cs).ds, (job->cs).ps, (job->cs).ec)) { (job->cs).st = PJSNMP_STATE_READY; } else { if(pjsnmp_is_warming_up((job->cs).ds, (job->cs).ps)) { (job->cs).st = PJSNMP_STATE_WARMUP; } else { if(PJSNMP_DEVICE_DOWN == (job->cs).ds) { (job->cs).st = PJSNMP_STATE_ERROR; } else { (job->cs).st = PJSNMP_STATE_OTHER; } } } } } /* For the end of job the page count should be higher than at the beginning of the job. We wait 15 seconds for start of printing, otherwise it might be a 0 pages print job or something the printer does not understand. */ if(PJSNMP_STATE_READY == (job->cs).st) { if(2 == stage) { if(!(job->wp)) { if(job->pc == (job->cs).pc) { time(&ct); if(ct < (job->ti + (time_t)15)) { (job->cs).st = PJSNMP_STATE_WAIT_START; } } } } } $? "- pjsnmp_set_summary_state %d", (job->cs).st } /** Apply SNMP response to current state in job. @param job job structure. @param rs SNMP response PDU. @param stage Processing stage (0=start, 1=in job, 2=end). @return 1 if all components were found, 0 otherwise. */ static int pjsnmp_apply_response(pjsnmp_job_t *job, struct snmp_pdu *rs, int stage) { struct variable_list *v; int have = 0; int back = 0; $? "+ pjsnmp_apply_response" v = rs->variables; while(v) { $!trace-code pjsnmp_show_oid(job, v->name, v->name_length); if(pjsnmp_oids_equal(v->name,v->name_length,pjsnmp_oid_ds,pjsnmp_sz_oid_ds)) { $? ". ds" if(pjsnmp_get_int(job, &((job->cs).ds), v)) { have |= 1; $? ". ds = %d", (job->cs).ds } } if(pjsnmp_oids_equal(v->name,v->name_length,pjsnmp_oid_ps,pjsnmp_sz_oid_ps)) { $? ". ps" if(pjsnmp_get_int(job, &((job->cs).ps), v)) { have |= 2; $? ". ps = %d", (job->cs).ps } } if(pjsnmp_oids_equal(v->name,v->name_length,pjsnmp_oid_pc,pjsnmp_sz_oid_pc)) { $? ". pc" if(pjsnmp_get_ul(job, &((job->cs).pc), v)) { have |= 4; $? ". pc = %lu", (job->cs).pc } } if(pjsnmp_oids_equal(v->name,v->name_length,pjsnmp_oid_pe,pjsnmp_sz_oid_pe)) { $? ". ec" if(pjsnmp_get_bytes_inverted(job, &((job->cs).ec), v)) { have |= 8; $? ". ec = %lu", (job->cs).ec } } v = v->next_variable; } if(7 == (have & 7)) { back = 1; pjsnmp_set_summary_state(job, stage); } $? "- pjsnmp_apply_response %d", back return back; } /** Retrieve information from printer using SNMP. Report state if changed and error conditions if changed. @param job Job structure. @param stage Processing stage (0=start, 1=in job, 2=end). @return 1 if the program can continue normally, 0 if there was no response or an error in the response, -1 for serious problems (failed to create request PDU). */ static int pjsnmp_run_snmp_request(pjsnmp_job_t *job, int stage) { struct snmp_pdu *rq; /* Request. */ struct snmp_pdu *rs; /* Response. */ int status; /* Status from SNMP request. */ int back = 0; $? "+ pjsnmp_run_snmp_request %d", stage pjsnmp_state_init(&(job->cs)); (job->cs).st = PJSNMP_STATE_UNREACHABLE; rq = snmp_pdu_create(SNMP_MSG_GET); rs = NULL; if(rq) { $? ". rq" snmp_add_null_var(rq, pjsnmp_oid_ds, pjsnmp_sz_oid_ds); snmp_add_null_var(rq, pjsnmp_oid_ps, pjsnmp_sz_oid_ps); snmp_add_null_var(rq, pjsnmp_oid_pc, pjsnmp_sz_oid_pc); if(job->pdes) { snmp_add_null_var(rq, pjsnmp_oid_pe, pjsnmp_sz_oid_pe); } status = snmp_synch_response(job->ss, rq, &rs); if(STAT_SUCCESS == status) { $? ". status" if(rs) { $? ". rs" if(SNMP_ERR_NOERROR == rs->errstat) { $? ". errstat" (job->cs).st = PJSNMP_STATE_OTHER; back = pjsnmp_apply_response(job, rs, stage); } else { $? "! errstat %d", rs->errstat /* ERROR in response */ (job->cs).st = PJSNMP_STATE_ERROR_IN_RESPONSE; } snmp_free_pdu(rs); rs = NULL; } else { $? "! rs" /* ERROR: No response! */ (job->cs).st = PJSNMP_STATE_UNREACHABLE; } } else { $? "! status" /* ERROR: No response! */ (job->cs).st = PJSNMP_STATE_UNREACHABLE; } } else { $? "! rq" back = -1; (job->cs).st = PJSNMP_STATE_REQUEST_FAILED; job->fe = 1; } $? "- pjsnmp_run_snmp_request %d state=%d", back, (job->cs).st return back; } /** Report current printer state. @param job Job structure. */ static void pjsnmp_report_printer_state(pjsnmp_job_t *job) { $? "+ pjsnmp_report_printer_state" switch((job->cs).st) { case PJSNMP_STATE_UNREACHABLE: { pjsnmp_log_1(job, (job->lm)[6]); } break; case PJSNMP_STATE_REQUEST_FAILED: { pjsnmp_log_1(job, (job->lm)[7]); } break; case PJSNMP_STATE_ERROR_IN_RESPONSE: { pjsnmp_log_1(job, (job->lm)[8]); } break; case PJSNMP_STATE_READY: { pjsnmp_log_1(job, (job->lm)[0]); } break; case PJSNMP_STATE_PRINTING: { pjsnmp_log_1(job, (job->lm)[2]); } break; case PJSNMP_STATE_WARMUP: { pjsnmp_log_1(job, (job->lm)[1]); } break; case PJSNMP_STATE_WAIT_START: { pjsnmp_log_1(job, (job->lm)[3]); } break; case PJSNMP_STATE_ERROR: { pjsnmp_log_1(job, (job->lm)[4]); } break; case PJSNMP_STATE_OTHER: { pjsnmp_log_1(job, (job->lm)[5]); } break; } $? "- pjsnmp_report_printer_state" } /** Report device status and printer status. @param job job structure. */ static void pjsnmp_report_device_printer_status(pjsnmp_job_t *job) { (job->cs).ds = PJSNMP_IN_RANGE(0,5,(job->cs).ds); (job->cs).ps = PJSNMP_IN_RANGE(0,5,(job->cs).ps); pjsnmp_log_3( job, (job->lm)[9], (job->lm)[10 + (job->cs).ds], (job->lm)[16 + (job->cs).ps] ); } /** Detect and report change in an error condition. @param job Job structure. @param ce Current error conditions. @param oe Old (previous) error conditions. @param c1 One condition to check. @param c1_on Index of text to print if condition changed to true. @param c1_off Index of text to print if condition changed to false. */ static void pjsnmp_rdec_one_condition( pjsnmp_job_t *job, unsigned long ce, unsigned long oe, unsigned long c1, size_t c1_on, size_t c1_off ) { if(ce & c1) { if(!(oe & c1)) { pjsnmp_log_1(job, (job->lm)[c1_on]); } } else { if(oe & c1) { pjsnmp_log_1(job, (job->lm)[c1_off]); } } } /** Detect and report change in two related error conditions. @param job Job structure. @param ce Current error conditions. @param oe Old (previous) error conditions. @param c1 Higher priorized condition to check. @param c2 Lower priorized condition to check. @param c1_on Index of text to print if c1 changed to true. @param c1_off Index of text to print if c1 changed to false. @param c2_on Text of print if c1=false and c2 changed to true. @param c2_off Text to print if c1=false and c2 changed to false. */ static void pjsnmp_rdec_two_conditions( pjsnmp_job_t *job, unsigned long ce, unsigned long oe, unsigned long c1, unsigned long c2, size_t c1_on, size_t c1_off, size_t c2_on, size_t c2_off ) { if(ce & c1) { if(!(oe & c1)) { /* Report c1_on */ pjsnmp_log_1(job, (job->lm)[c1_on]); } } else { if(ce & c2) { if(!(oe & c2)) { /* Report c2_on */ pjsnmp_log_1(job, (job->lm)[c2_on]); } } else { if(oe & c1) { /* Report c1_off */ pjsnmp_log_1(job, (job->lm)[c1_off]); } else { if(oe & c2) { /* Report c2_off */ pjsnmp_log_1(job, (job->lm)[c2_off]); } } } } } /** Report error condition indicated in hrPrinterDetectedErrorState. @param job Job structure. */ static void pjsnmp_report_detected_error_condition(pjsnmp_job_t *job) { unsigned long oe; /* Old (previous) error conditions. */ unsigned long ce; /* Current error conditions. */ oe = (job->os).ec; ce = (job->cs).ec; pjsnmp_rdec_two_conditions( job, ce, oe, DK3_SNMP_PRINTER_ERROR_NO_PAPER, DK3_SNMP_PRINTER_ERROR_LOW_PAPER, 22, 23, 24, 25 ); pjsnmp_rdec_one_condition( job, ce, oe, DK3_SNMP_PRINTER_ERROR_INPUT_EMPTY, 48, 49 ); pjsnmp_rdec_two_conditions( job, ce, oe, DK3_SNMP_PRINTER_ERROR_NO_TONER, DK3_SNMP_PRINTER_ERROR_LOW_TONER, 26, 27, 28, 29 ); pjsnmp_rdec_two_conditions( job, ce, oe, DK3_SNMP_PRINTER_ERROR_OUTPUT_FULL, DK3_SNMP_PRINTER_ERROR_OUTPUT_NEAR, 30, 31, 32, 33 ); pjsnmp_rdec_one_condition( job, ce, oe, DK3_SNMP_PRINTER_ERROR_JAMMED, 36, 37 ); pjsnmp_rdec_one_condition( job, ce, oe, DK3_SNMP_PRINTER_ERROR_DOOR_OPEN, 34, 35 ); pjsnmp_rdec_one_condition( job, ce, oe, DK3_SNMP_PRINTER_ERROR_OFFLINE, 38, 39 ); pjsnmp_rdec_one_condition( job, ce, oe, DK3_SNMP_PRINTER_ERROR_SERVICE_NEEDED, 40, 41 ); pjsnmp_rdec_one_condition( job, ce, oe, DK3_SNMP_PRINTER_ERROR_INPUT_TRAY, 42, 43 ); pjsnmp_rdec_one_condition( job, ce, oe, DK3_SNMP_PRINTER_ERROR_OUTPUT_TRAY, 44, 45 ); pjsnmp_rdec_one_condition( job, ce, oe, DK3_SNMP_PRINTER_ERROR_MARKER_SUPPLY, 46, 47 ); pjsnmp_rdec_one_condition( job, ce, oe, DK3_SNMP_PRINTER_ERROR_MAINTENANCE, 50, 51 ); } /** Run SNMP request and report the results to status file if necessary. @param job Job structure. @param stage Processing stage (0=start, 1=in job, 2=end). @return 1 on success, 0 for no response or error in response, -1 for serious problem (failed to create request PDU). */ static int pjsnmp_run_request_and_report(pjsnmp_job_t *job, int stage) { int back = 0; $? "+ pjsnmp_run_request_and_report %d", stage back = pjsnmp_run_snmp_request(job, stage); if((1 == back) || (0 == back)) { /* Compare against old state, report changes if any. */ if(((job->cs).ds != (job->os).ds) || ((job->cs).ps != (job->os).ps)) { pjsnmp_report_device_printer_status(job); } if((job->cs).ec != (job->os).ec) { pjsnmp_report_detected_error_condition(job); } if((job->cs).st != (job->os).st) { pjsnmp_report_printer_state(job); } /* This passes current state is the old state in the next pass. */ dk3mem_cpy( (void *)(&(job->os)), (void *)(&(job->cs)), sizeof(pjsnmp_printer_state_t) ); } $? "- pjsnmp_run_request_and_report %d", back return back; } /** Construct accounting request. @param job Job structure. @param stage Processing stage, 0=start, 2=end. @param rq Destination buffer for request. @param szrq Size of rq. @return 1 on success, 0 on error. */ static int pjsnmp_construct_accounting_request( pjsnmp_job_t *job, int stage, char *rq, size_t szrq ) { char bu[32]; char *corpos; char const *kw1; size_t sl; int back = 0; corpos = NULL; if(job->as) { /* LPRng */ kw1 = pjsnmp_kw[(stage) ? 15 : 14]; } else { /* printqd */ kw1 = pjsnmp_kw[(stage) ? 17 : 16]; } sprintf(bu, pjsnmp_kw[18], (job->cs).pc); if((job->qn) && (job->un) && (job->jn) && (job->jt)) { sl = strlen(kw1); sl += strlen(job->qn); sl += strlen(job->un); sl += strlen(bu); sl += strlen(job->jn); sl += strlen(job->jt); sl += 7; if(job->as) { sl += 20; } if(sl < szrq) { strcpy(rq, kw1); strcat(rq, pjsnmp_kw[5]); if(1 == job->as) { strcat(rq, pjsnmp_kw[8]); } strcat(rq, job->qn); if(1 == job->as) { strcat(rq, pjsnmp_kw[7]); } strcat(rq, pjsnmp_kw[5]); if(1 == job->as) { strcat(rq, pjsnmp_kw[9]); } strcat(rq, job->un); if(1 == job->as) { strcat(rq, pjsnmp_kw[7]); } strcat(rq, pjsnmp_kw[5]); if(1 == job->as) { strcat(rq, pjsnmp_kw[19]); } strcat(rq, bu); if(1 == job->as) { strcat(rq, pjsnmp_kw[7]); } strcat(rq, pjsnmp_kw[5]); if(1 == job->as) { strcat(rq, pjsnmp_kw[20]); } strcat(rq, job->jn); if(1 == job->as) { strcat(rq, pjsnmp_kw[7]); } strcat(rq, pjsnmp_kw[5]); if(1 == job->as) { strcat(rq, pjsnmp_kw[21]); corpos = rq; while(*corpos) { corpos++; } } strcat(rq, job->jt); if(1 == job->as) { while(*corpos) { switch(*corpos) { case '\'': case ' ': case '\t': { *corpos = '_'; } break; case '\\': { *corpos = '/'; } break; } corpos++; } strcat(rq, pjsnmp_kw[7]); } strcat(rq, pjsnmp_kw[6]); back = 1; } } return back; } /** Pass page counter value to accounting system. @param job Job structure. @param stage Processing stage, 0=start, 2=end. */ static void pjsnmp_send_pagecount_to_accounting(pjsnmp_job_t *job, int stage) { char rq[PQD_INPUT_BUFFER_SIZE]; FILE *fipo; if(job->af) { if(pjsnmp_construct_accounting_request(job, stage, rq, sizeof(rq))) { if(pjsnmp_is_socket(job)) { $? ". connect to socket" (void)pjsnmp_accounting_dialog(job, rq, NULL, 0); } else { $? ". write to file" fipo = dk3sf_fopen_app(job->af, pjsnmp_kw[13], job->app); if(fipo) { fputs(rq, fipo); fclose(fipo); } } } else { /* ERROR: Accounting request grows too long! */ pjsnmp_log_1(job, (job->lm)[78]); } } } /** Retrieve page count before and after job, report to accounting system. @param job Job structure. @param stage Processing state (0=start, 1=in job, 2=end). */ static void pjsnmp_process_pagecount(pjsnmp_job_t *job, int stage) { char bu[32]; int cc = 1; int success = 0; int res; $? "+ pjsnmp_process_pagecount %d", stage if(job->ss) { while((0 == success) && (cc) && (pjsnmp_cc(job,1))) { $? ". loop" res = pjsnmp_run_request_and_report(job, stage); $? ". snmp=%d", res switch(res) { case 1: { $? ". SNMP answer ok" switch((job->cs).st) { case PJSNMP_STATE_READY: { success = 1; cc = 0; } break; case PJSNMP_STATE_REQUEST_FAILED: { cc = 0; job->fe = 1; /* ERROR: Resources shortage, giving up! */ pjsnmp_log_1(job, (job->lm)[77]); } break; } } break; case 0: { $? "! not reached or error in response" } break; case -1: { $? "! failed to create request PDU" cc = 0; /* ERROR: Resources shortage, giving up! */ pjsnmp_log_1(job, (job->lm)[77]); job->fe = 1; } break; } /* When not yet done, wait a second to avoid 100 percent CPU usage. */ if((0 == success) && (cc) && (pjsnmp_cc(job,1)) && (!(job->fe))) { sleep(1); } $? ". end loop" } if(success) { /* Report page count to accounting system. */ pjsnmp_send_pagecount_to_accounting(job, stage); sprintf(bu, pjsnmp_kw[18], (job->cs).pc); switch(stage) { case 0: { pjsnmp_log_2(job, (job->lm)[86], bu); } break; case 2: { pjsnmp_log_2(job, (job->lm)[87], bu); } break; } } } else { } $? "- pjsnmp_process_pagecount" } /** Open temporary file and network connection, transfer data. @param job Job structure. */ static void pjsnmp_process_data(pjsnmp_job_t *job) { char bu[1460]; /* Buffer. */ fd_set rfds; /* File descriptor set for response. */ fd_set wfds; /* File descriptor set for select. */ struct timeval to; /* Timeout for select. */ FILE *fipo; /* Input file. */ dk3_socket_t sock; /* Socket for printer connection. */ size_t rb; /* Number of bytes read. */ int cc; /* Flag: Can continue. */ int wb; /* Number of bytes sent. */ int cw; /* Flag: Can write. */ int res; /* Shutdown result. */ int se; /* Flag: Error during socket ops. */ $? "+ pjsnmp_process_data" se = 0; fipo = dk3sf_fopen_app(job->tf, pjsnmp_kw[23], job->app); if(fipo) { $? ". fopen" sock = dk3socket_open_net_stream_client( job->hn, job->pn, 0, 0L, 0L, NULL, job->app ); if(INVALID_SOCKET != sock) { $? ". socket" /* Socket opened successfully, transfer data now. */ job->ex = PJSNMP_EXIT_SUCCESS; cc = 1; while((cc) && (pjsnmp_cc(job,0))) { cc = 0; rb = dk3sf_fread_app((void *)bu, 1, sizeof(bu), fipo, job->app); if(rb > 0) { $? ". data read" /* We have data to send. */ cc = 1; /* Check whether socket is ready to send. If not ready to send, run an SNMP status check on printer. */ if((job->ct) && (job->ss)) { $? ". check during send" cw = 0; while((cc) && (!(cw)) && (pjsnmp_cc(job,0))) { FD_ZERO(&wfds); FD_SET(sock,&wfds); to.tv_sec = 0L; to.tv_usec = 0L; if(0 < select((sock+1), NULL, &wfds, NULL, &to)) { $? ". 1" if(FD_ISSET(sock,&wfds)) { $? ". 2" cw = 1; $? ". socket ready" } else { $? "! FD_ISSET" } } else { $? "! select" } if(!(cw)) { $? ". socket not ready" (void)pjsnmp_run_request_and_report(job, 1); } } } /* Send data to printer. */ if((cc) && (pjsnmp_cc(job, 0))) { $? ". attempt to send" wb = dk3socket_send(sock, (void *)bu, rb, 0L, 0L, NULL, job->app); if(wb != (int)rb) { $? "! sending failed" cc = 0; se = 1; /* ERROR While sending data! */ pjsnmp_log_1(job, (job->lm)[76]); job->ex = PJSNMP_EXIT_REMOVE; } } /* Check for response from printer. */ FD_ZERO(&rfds); FD_SET(sock,&rfds); to.tv_sec = 0L; to.tv_usec = 0L; if(0 < select((sock + 1), &rfds, NULL, NULL, &to)) { if(FD_ISSET(sock,&rfds)) { int oldmode; oldmode = fcntl(sock, F_GETFL); fcntl(sock, F_SETFL, (oldmode | O_NONBLOCK)); rb = dk3socket_recv( sock, (void *)bu, sizeof(bu), 0L, 0L, NULL, job->app ); if(rb > 0) { if(job->repr) { /* Write printer response to status file. */ if(job->sf) { fipo = dk3sf_fopen_app( job->sf, pjsnmp_kw[(job->hl) ? 13 : 12 ], job->app ); if(fipo) { fputs((job->lm)[88], fipo); fputc('\n', fipo); (void)dk3sf_fwrite_app(bu, 1, (size_t)rb, fipo, job->app); if('\n' != bu[rb - 1]) { fputc('\n', fipo); } job->hl = 1; fclose(fipo); } } } } fcntl(sock, F_SETFL, oldmode); } } } else { $? ". no more data" } } /* Orderly release if configured to do so. */ if((!(se)) && (job->od) && (pjsnmp_cc(job, 0))) { res = dk3socket_shutdown(sock,DK3_TCPIP_SHUTDOWN_WRITE,NULL,job->app); if(res) { cc = 1; while((cc) && (pjsnmp_cc(job, 0))) { cc = 0; wb = dk3socket_recv(sock, bu, sizeof(bu), 0L, 0L, NULL, job->app); if(wb > 0) { cc = 1; } } } } /* Close socket. */ dk3socket_close(sock, NULL, job->app); } else { $? "! socket" /* ERROR: Failed to connect to printer! */ pjsnmp_log_1(job, (job->lm)[75]); } fclose(fipo); } else { $? "! fopen" /* ERROR: Failed to open file! */ pjsnmp_log_1(job, (job->lm)[74]); } $? "- pjsnmp_process_data" } /** Do pagecount at job start, transfer data, and do pagecount at job end. @param job Job structure. */ static void pjsnmp_pagecount_transfer_pagecount(pjsnmp_job_t *job) { time_t ct; $? "+ pjsnmp_pagecount_transfer_pagecount" pjsnmp_state_init(&(job->os)); pjsnmp_state_init(&(job->cs)); /* Retrieve page count before job. */ $? ". (1)" pjsnmp_process_pagecount(job, 0); $? ". (2)" job->pc = (job->cs).pc; $? ". (3)" if(pjsnmp_cc(job,0)) { $? ". can continue" /* Transfer print data. */ pjsnmp_process_data(job); $? ". transfer" if(pjsnmp_cc(job,0)) { $? ". cc" /* Keep start time for beginning of page count 2. */ time(&ct); job->ti = ct; /* Retrieve page count after job. */ pjsnmp_process_pagecount(job, 2); $? ". (4)" } } $? "- pjsnmp_pagecount_transfer_pagecount" } /** Continue after signal handlers are set. @param job Job structure. */ static void pjsnmp_continue_with_signal_handlers(pjsnmp_job_t *job) { #if DK3_HAVE_NETSNMP_ENABLE_FILELOG netsnmp_log_handler *logh = NULL; /* Log handler. */ #endif $? "+ pjsnmp_continue_with_signal_handlers" if(pjsnmp_read_standard_input(job)) { pjsnmp_apply_command_line_arguments(job); if(pjsnmp_achk(job)) { #if DK3_HAVE_NETSNMP_ENABLE_FILELOG logh = netsnmp_register_loghandler(NETSNMP_LOGHANDLER_FILE, 5); if(logh) { logh->pri_max = 5; logh->token = job->sl; netsnmp_enable_filelog(logh, 0); } #endif init_snmp(pjsnmp_kw[10]); pjsnmp_open_session(job); if((job->ss) || (job->is)) { if(!(job->ss)) { /* WARNING: Failed to open SNMP session! */ pjsnmp_log_1(job, (job->lm)[73]); } pjsnmp_pagecount_transfer_pagecount(job); if(job->ss) { snmp_close(job->ss); job->ss = NULL; } } else { /* ERROR: Failed to open SNMP session! */ pjsnmp_log_1(job, (job->lm)[72]); } #if DK3_HAVE_NETSNMP_ENABLE_FILELOG if(logh) { snmp_disable_filelog(); netsnmp_remove_loghandler(logh); logh->token = NULL; free(logh); } #endif } } else { /* ERROR: Failed to save standard input to file! */ pjsnmp_log_1(job, (job->lm)[71]); } $? "- pjsnmp_continue_with_signal_handlers" } /** Find file names for temporary files. @param job Job structure. */ static void pjsnmp_find_file_names(pjsnmp_job_t *job) { char bu[DK3_MAX_PATH]; sprintf(bu, pjsnmp_kw[26], (unsigned long)getpid()); job->tf = dk3str_c8_dup_app(bu, job->app); sprintf(bu, pjsnmp_kw[27], (unsigned long)getpid()); job->sl = dk3str_c8_dup_app(bu, job->app); $? "= pjsnmp_find_file_names \"%s\" \"%s\"", TR_STR(job->sl), TR_STR(job->tf) } /** Run after environment variable is applied. @param job Job structure. */ static void pjsnmp_run_with_applied_environment(pjsnmp_job_t *job) { #if DK3_HAVE_SIGACTION struct sigaction sanewint; /* Install new handler for INT */ struct sigaction sanewterm; /* Install new handler for TERM */ struct sigaction sanewpipe; /* Install new handler for PIPE */ struct sigaction sabckint; /* Keep setup for INT */ struct sigaction sabckterm; /* Keep setup for TERM */ struct sigaction sabckpipe; /* Kepp setup for PIPE */ int sigok = 0; /* Flag: All signal setup ok */ $? "+ pjsnmp_run_with_applied_environment" pjsnmp_find_file_names(job); if((job->tf) && (job->sl)) { $? ". tf && sl" sanewint.sa_handler = pjsnmp_sigint_handler; sanewterm.sa_handler = pjsnmp_sigterm_handler; sanewpipe.sa_handler = pjsnmp_sigpipe_handler; sanewint.sa_flags = 0; sanewterm.sa_flags = 0; sanewpipe.sa_flags = 0; if (sigemptyset(&(sanewint.sa_mask)) == 0) { if (sigaddset(&(sanewint.sa_mask), SIGINT) == 0) { if (sigaction(SIGINT, &sanewint, &sabckint) == 0) { if (sigemptyset(&(sanewterm.sa_mask)) == 0) { if (sigaddset(&(sanewterm.sa_mask), SIGTERM) == 0) { if (sigaction(SIGTERM, &sanewterm, &sabckterm) == 0) { if (sigemptyset(&(sanewpipe.sa_mask)) == 0) { if (sigaddset(&(sanewpipe.sa_mask), SIGPIPE) == 0) { if (sigaction(SIGPIPE, &sanewpipe, &sabckpipe) == 0) { /* All signal setup ok. */ sigok = 1; /* Continue processing. */ pjsnmp_continue_with_signal_handlers(job); /* Remove temporary files. */ if(job->sl) { dk3sf_c8_remove_file_app(job->sl, job->app); } if(job->tf) { dk3sf_c8_remove_file_app(job->tf, job->app); } (void)sigaction(SIGPIPE, &sabckpipe, NULL); } } } (void)sigaction(SIGTERM, &sabckterm, NULL); } } } (void)sigaction(SIGINT, &sabckint, NULL); } } } if (0 == sigok) { /* ERROR: Signal setup failed! */ pjsnmp_log_1(job, (job->lm)[89]); pjsnmp_eat_input(job); } } else { $? "! tf && sl" /* ERROR: Memory */ pjsnmp_log_1(job, (job->lm)[60]); pjsnmp_eat_input(job); } $? "- pjsnmp_run_with_applied_environment" #else dk3_signal_disp_t disp_int; dk3_signal_disp_t disp_term; dk3_signal_disp_t disp_pipe; $? "+ pjsnmp_run_with_applied_environment" pjsnmp_find_file_names(job); if((job->tf) && (job->sl)) { $? ". tf && sl" /* Save signal dispositions. */ disp_int = sigset(SIGINT, pjsnmp_sigint_handler); disp_term = sigset(SIGTERM, pjsnmp_sigterm_handler); disp_pipe = sigset(SIGPIPE, pjsnmp_sigpipe_handler); /* Continue processing. */ pjsnmp_continue_with_signal_handlers(job); /* Remove temporary files. */ if(job->sl) { dk3sf_c8_remove_file_app(job->sl, job->app); } if(job->tf) { dk3sf_c8_remove_file_app(job->tf, job->app); } /* Restore signal dispositions. */ if(disp_pipe) { sigset(SIGPIPE, disp_pipe); } if(disp_term) { sigset(SIGTERM, disp_term); } if(disp_int) { sigset(SIGINT, disp_int); } } else { $? "! tf && sl" /* ERROR: Memory */ pjsnmp_log_1(job, (job->lm)[60]); pjsnmp_eat_input(job); } $? "- pjsnmp_run_with_applied_environment" #endif } /** Error messages for :pjsnmpxxxx configuration entries without value. @param job Job structure. @param kwi Keyword index in pjsnmp_printcap_entries. */ static void pjsnmp_config_entry_needs_value(pjsnmp_job_t *job, size_t kwi) { /* ERROR: Configuration keyword ... needs value! */ pjsnmp_log_3(job, (job->lm)[52], pjsnmp_printcap_entries[kwi], (job->lm)[53]); } /** Apply one configuration entry. @param job Job structure. @param pc Current key. @param pv Current value, may be NULL. @param in Flag: Inverse entry. @return 1 on success, 0 on error. */ static int pjsnmp_apply_configuration_entry( pjsnmp_job_t *job, char *pc, char *pv, int in ) { int back = 1; $? "+ pjsnmp_apply_configuration_entry \"%s\"=\"%s\" %d", pc, TR_STR(pv), in switch(dk3str_array_index(pjsnmp_printcap_entries,pc,0)) { case 0: { /* af */ if(pv) { job->af = pv; } else { pjsnmp_config_entry_needs_value(job, 0); back = 0; } } break; case 1: { /* achk */ job->ac = ((in) ? 0 : 1); } break; case 2: { /* host */ if(pv) { job->hn = pv; } else { pjsnmp_config_entry_needs_value(job, 2); back = 0; } } break; case 3: { /* port */ if(pv) { unsigned u; #if VERSION_BEFORE_20140716 if(1 == sscanf(pv, pjsnmp_kw[25], &u)) #else if (0 != dk3ma_ui_from_c8_string(&u, pv, NULL)) #endif { job->pn = (unsigned short)u; #if VERSION_BEFORE_20140809 if((unsigned)(job->pn) != u) #else if (u > (unsigned)(DK3_US_MAX)) #endif { /* ERROR: Numeric overflow! */ pjsnmp_log_3(job, (job->lm)[69], pv, (job->lm) [70]); back = 0; } } else { /* ERROR: Not a number! */ pjsnmp_log_3(job, (job->lm)[67], pv, (job->lm) [68]); back = 0; } } else { pjsnmp_config_entry_needs_value(job, 3); back = 0; } } break; case 4: { $? ". version \"%s\"", TR_STR(pv) if(pv) { job->sv = dk3str_array_index(pjsnmp_snmp_versions, pv, 0); $? ". %d", job->sv if(-1 < job->sv) { $? ". version ok" job->sv += 1; } else { $? "! version" job->sv = 0; /* ERROR: Illegal SNMP version! */ pjsnmp_log_3(job, (job->lm)[65], pv, (job->lm)[66]); back= 0; } } else { pjsnmp_config_entry_needs_value(job, 4); back = 0; } } break; case 5: { /* comm */ if(pv) { job->sc = pv; } else { pjsnmp_config_entry_needs_value(job, 5); back = 0; } } break; case 6: { $? ". acst \"%s\"", TR_STR(pv) job->as = dk3str_array_index(pjsnmp_accounting_styles, pv, 0); $? ". as = %d", job->as if(0 > job->as) { job->as = 0; /* ERROR: Illegal accounting style! */ pjsnmp_log_3(job, (job->lm)[63], pv, (job->lm)[64]); back = 0; } } break; case 7: { /* ordr */ job->od = ((in) ? 0 : 1); } break; case 8: { /* check during transfer */ job->ct = ((in) ? 0 : 1); } break; case 9: { /* ignore SNMP failure */ job->is = ((in) ? 0 : 1); } break; case 10: { /* Printer detected error state */ job->pdes = ((in) ? 0 : 1); } break; case 11: { /* Warning/Running + Other indicates standby. */ job->poid = ((in) ? 0 : 1); } break; case 12: { /* Report response. */ job->repr = ((in) ? 0 : 1); } break; } $? "- pjsnmp_apply_configuration_entry %d", back return back; } /** Configure job from environment entry. @param job Job structure. @param pcep PRINTCAP_ENTRY environment entry. @return 1 on success, 0 on error. */ static int pjsnmp_configure_from_environment(pjsnmp_job_t *job, char *pcep) { char *pc; /* Current string. */ char *pn; /* Next string. */ char *pv; /* Value in current string. */ int in; /* Flag: Inverse entry. */ int back = 1; $? "+ pjsnmp_configure_from_environment" pc = dk3str_c8_chr(pcep, ':'); if(pc) { *(pc++) = '\0'; pc = dk3str_c8_start(pc, NULL); } while(pc) { in = 0; pn = dk3str_c8_chr(pc, ':'); if(pn) { *(pn++) = '\0'; pn = dk3str_c8_start(pn, NULL); } dk3str_c8_delnl(pc); pv = dk3str_c8_chr(pc, '='); if(pv) { *(pv++) = '\0'; pv = dk3str_c8_start(pv, NULL); if(pv) { dk3str_c8_delnl(pv); dk3str_c8_normalize(pv, NULL, ' '); } } else { pv = dk3str_c8_chr(pc, '@'); if(pv) { *pv = '\0'; in = 1; } } if(!pjsnmp_apply_configuration_entry(job, pc, pv, in)) { back = 0; } pc = pn; } $? "- pjsnmp_configure_from_environment %d", back return back; } /** Run with a given environment entry for configuration. @param job Job structure. @param pcep PRINTCAP_ENTRY environment entry. */ static void pjsnmp_run_with_environment(pjsnmp_job_t *job, char *pcep) { $? "+ pjsnmp_run_with_environment" if(pjsnmp_configure_from_environment(job, pcep)) { $? ". ok" if(job->hn) { $? ". hn" if(job->pn) { $? ". pn" pjsnmp_run_with_applied_environment(job); } else { $? "! pn" /* ERROR: Missing port number! */ pjsnmp_log_1(job, (job->lm)[62]); pjsnmp_eat_input(job); } } else { $? "! hn" /* ERROR: Missing host name! */ pjsnmp_log_1(job, (job->lm)[61]); pjsnmp_eat_input(job); } } else { $? "! pjsnmp_configure_from_environment" pjsnmp_eat_input(job); } $? "- pjsnmp_run_with_environment" } /** Program entry point. @param argc Number of command line arguments. @param argv Command line arguments array. @return 0 on success, any other value indicates an error. */ DK3_MAIN { pjsnmp_job_t job; /* Job structure. */ char bu[32]; /* Buffer to print number of pages. */ char *pcep; /* Copy of PRINTCAP_ENTRY env. */ int exval; /* Exit status code. */ $!trace-init /tmp/pjsnmp.deb $? "+ main" #if DK3_HAVE_TZSET tzset(); #endif pjsnmp_job_init(&job); job.app = dk3app_open_silent(argc,(dkChar const * const *)argv,pjsnmp_kw[0]); if(job.app) { $? ". app" job.lm = dk3app_messages(job.app, pjsnmp_kw[1], pjsnmp_loc); pcep = getenv(pjsnmp_kw[2]); if(pcep) { $? ". pcep" pcep = dk3str_dup_app(pcep, job.app); if(pcep) { $? ". pcep" pjsnmp_run_with_environment(&job, pcep); dk3_delete(pcep); } else { $? "! pcep" /* ERROR: Memory */ pjsnmp_log_1(&job, (job.lm)[60]); pjsnmp_eat_input(&job); } } else { $? "! pcep" /* ERROR: Printcap entry not found! */ pjsnmp_log_1(&job, (job.lm)[59]); pjsnmp_eat_input(&job); } if((pjsnmp_read_vol(&pjsnmp_sigterm_received)) || (pjsnmp_read_vol(&pjsnmp_sigint_received))) { /* SUMMARY: Job cancelled by signal! */ pjsnmp_log_1(&job, (job.lm)[58]); } else { if(PJSNMP_EXIT_SUCCESS == job.ex) { if(job.cs.pc > job.pc) { /* SUMMARY: Job finished successfully, xxx pages. */ sprintf(bu, pjsnmp_kw[18], (job.cs.pc - job.pc)); pjsnmp_log_3(&job, (job.lm)[56], bu, (job.lm)[57]); } else { /* SUMMARY: Job finished successfully. */ pjsnmp_log_1(&job, (job.lm)[55]); } } else { /* SUMMARY: Errors occured during print jobs. */ pjsnmp_log_1(&job, (job.lm)[54]); } } } else { $? "! app" /* ERROR: Memory */ fputs("pjsnmp: ERROR: Not enough memory!\n", stderr); fflush(stderr); pjsnmp_eat_input(&job); } exval = job.ex; pjsnmp_job_cleanup(&job); $? "- main %d", exval $!trace-end fflush(stdout); exit(exval); return exval; } #else /* ERROR: No libnetsnmp */ int main(int argc, char *argv[] ) { printf("No net-snmp!\n"); } #endif #else /* ERROR: No sigset */ int main(int argc, char *argv[] ) { printf("No sigset()!\n"); } #endif #else /* Incorrect character size. */ int main(int argc, char *argv[] ) { printf("Incorrect character size %d!\n", (int)DK3_CHAR_SIZE); } #endif