/* Copyright (c) 2010, Dirk Krause All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above opyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Dirk Krause nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @file fsnmp.c The fsnmp module in the fsnmp program. */ /** Inside the fsnmp module. */ #define FSNMP_C 1 #include "fsnmp.h" #include "dktools-version.h" $(trace-include) #if DK_HAVE_SYS_TIME_H #include #endif /** Ctrl-D character. */ #define CTRL_D (0x04) /** Normal filter: read input and save contents to temporary file. @param fc Fsnmp job. */ static void filter_input DK_P1(FC *,fc) { FILE *fipo; ssize_t s1; size_t s2; $? "+ filter_input" if(fsnmp_cc(fc)) { fipo = dksf_fopen(fc->temp_file, "wb"); do { s1 = read(0, fc->buffer_in, fc->sz_buffer_in); if(s1 > 0) { $? ". %lu bytes were read", (unsigned long)s1 if(fipo) { s2 = fwrite(fc->buffer_in, 1, s1, fipo); $? ". %lu bytes were written", (unsigned long)s2 if(s2 < s1) { fc->exit_code = EXIT_FAILED; fsnmplog(fc, PRIO_ERROR, fsnmp_kw[47]); } } else { s2 = s1; } } } while((s1 > 0) && (s2 == s1) && fsnmp_cc(fc)); $? ". finished reading input" if(fipo) { fclose(fipo); fipo = NULL; } else { fc->exit_code = EXIT_FAILED; fsnmplog(fc, PRIO_ERROR, fsnmp_kw[48]); } } $? "- filter_input" } /** Application name. */ char fsnmp_application_name[] = { "fsnmp" }; /** OID for printer status. */ static char oid_printer_status[] = { ".1.3.6.1.2.1.25.3.5.1.1.1" }; /** OID for device status. */ static char oid_device_status[] = { "1.3.6.1.2.1.25.3.2.1.5.1" }; /** OID for pagecount value. */ static char oid_pagecount[] = { ".1.3.6.1.2.1.43.10.2.1.4.1.1" }; /** Prepare for one SNMP request. @param fc Fsnmp job. */ static void initialize_snmp DK_P1(FC *,fc) { $? "+ initialize_snmp" if(fsnmp_cc(fc)) { init_snmp(fsnmp_application_name); read_objid(oid_device_status, fc->oid_ds, &(fc->sz_oid_ds)); read_objid(oid_printer_status, fc->oid_ps, &(fc->sz_oid_ps)); read_objid(oid_pagecount, fc->oid_pc, &(fc->sz_oid_pc)); } $? "- initialize_snmp" } /** Compare two OIDs. @param o1 Left OID @param l1 Number of parts in o1. @param o2 Right OID. @param l2 Number of parts in o2 @return Comparison result (1=equal, 0=unequal). */ static int oids_equal DK_P4(oid *,o1,size_t,l1,oid *,o2,size_t,l2) { int back = 0; size_t i; $? "+ oids_equal %u %u", (unsigned)l1, (unsigned)l2 if(l1 == l2) { back = 1; for(i = 0; i < l1; i++) { if(o1[i] != o2[i]) { back = 0; } } } $? "- oids_equal %d", back return back; } /** Get int value from SNMP response PDU. @param fc Fsnmp job. @param v One variable from response PDU. @param dest Pointer to result variable. @return 1 on success, 0 on error. */ static int get_int DK_P3(FC *,fc, struct variable_list *,v, int *,dest) { int back = 0; int value = 0; int i; char mybuffer[512]; $? "+ get_int" switch(v->type) { case (int)(ASN_OCTET_STR): { if(v->val_len < sizeof(mybuffer)) { char *sptr, *dptr; sptr = (char *)((v->val).string); dptr = mybuffer; for(i = 0; i < v->val_len; i++) { *(dptr++) = *(sptr++); } *dptr = '\0'; if(sscanf(mybuffer, "%d", &i) == 1) { value = i; back = 1; } } } break; case (int)(ASN_TIMETICKS): case (int)(ASN_GAUGE): case (int)(ASN_COUNTER): case (int)(ASN_INTEGER): { value = *(v->val).integer; back = 1; } break; } $? "- get_int %d", back if(back) { *dest = value; } return back; } /** Get unsigned long value from SNMP response PDU. @param fc Fsnmp job. @param v One variable from response PDU. @param dest Pointer to result variable. @return 1 on success, 0 on error. */ static int get_ul DK_P3(FC *,fc, struct variable_list *,v, unsigned long *,dest) { unsigned long back = 0UL, u; int value = 0; int i; char mybuffer[512]; $? "+ get_ul" switch(v->type) { case (int)(ASN_OCTET_STR): { if(v->val_len < sizeof(mybuffer)) { char *sptr, *dptr; sptr = (char *)((v->val).string); dptr = mybuffer; for(i = 0; i < v->val_len; i++) { *(dptr++) = *(sptr++); } *dptr = '\0'; if(sscanf(mybuffer, "%lu", &u) == 1) { value = u; back = 1; } } } break; case (int)(ASN_TIMETICKS): case (int)(ASN_GAUGE): case (int)(ASN_COUNTER): case (int)(ASN_INTEGER): { value = (unsigned long)(*(v->val).integer); back = 1; } break; } $? "- get_ul %d", back if(back) { *dest = value; } return back; } /** Apply response PDU to state description. @param fc Fsnmp job. @param cs State description. @param rs Response PDU. */ static int apply_response_to_state DK_P3(FC *,fc, SUMMARY *,cs, struct snmp_pdu *,rs) { int back = 1; struct variable_list *v; v = rs->variables; while(v) { if(oids_equal(v->name, v->name_length, fc->oid_ds, fc->sz_oid_ds)) { if(!get_int(fc, v, &(cs->ds))) back = 0; } if(oids_equal(v->name, v->name_length, fc->oid_ps, fc->sz_oid_ps)) { if(!get_int(fc, v, &(cs->ps))) back = 0; } if(oids_equal(v->name, v->name_length, fc->oid_pc, fc->sz_oid_pc)) { if(!get_ul(fc, v, &(cs->pc))) back = 0; } v = v->next_variable; } return back; } /** Intervals for logging. */ static int log_intervals[] = { 1, 2, 5, 10, 15, 30, 60, 120, 300, 600, 1800, 3600 }; /** Number of entries in log_intervals array. */ static size_t no_log_intervals = sizeof(log_intervals)/sizeof(int); /** Keywords for accounting lines. */ static char *begin_kw[] = { "filestart", "fileend", "start", "end", NULL }; /** Add option. @param fc Fsnmp job. @param k Option to add. */ static void add_opt DK_P2(FC *,fc, char,k) { char *v; size_t sz; char buffer[5]; v = fsnmpcmd_get_argv(fc, k); if(v) { sz = 6 + strlen(v); if((sz + strlen(fc->buffer_out)) < fc->sz_buffer_out) { buffer[0] = ' '; buffer[1] = '\''; buffer[2] = '-'; buffer[3] = k; buffer[4] = '\0'; strcat(fc->buffer_out, buffer); strcat(fc->buffer_out, v); buffer[0] = '\''; buffer[1] = '\0'; strcat(fc->buffer_out, buffer); } } } /** Add option if it does not yet exist. @param fc Fsnmp job. @param k Character for option to add. */ static void add_if_not_done DK_P2(FC *,fc, char,k) { switch(k) { case 'H': case 'n': case 'P': case 'b': case 't': case 'C': case 'J': { } break; default: { add_opt(fc, k); } break; } } /** Bind local address. @param fc Fsnmp job. @param sock Socket fd. @param pmin Minimum local port number. @param pmax Maximum local port number. @return 1 on success, 0 on error. */ static int bind_local_address DK_P4(FC *,fc, int,sock, unsigned,pmin, unsigned,pmax) { int back = 1; #if USE_SETSOCKOPT int yes = 1; #endif struct sockaddr_in sain; unsigned u; $? "+ bind_local_address %d %u %u", sock, pmin, pmax if(fsnmp_cc(fc)) { #if USE_SETSOCKOPT if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { fsnmplog(fc, PRIO_ERROR, fsnmp_kw[81]); } #endif } if(!((pmin == 0) && (pmax == 0))) { back = 0; u = pmin; while((u <= pmax) && (!back) && fsnmp_cc(fc)) { sain.sin_family = AF_INET; sain.sin_port = htons(u); sain.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(sock, (struct sockaddr *)(&sain), SZSOIN) == 0) { back = 1; } else { u++; } } if(back) { fsnmplog(fc, PRIO_INFO, fsnmp_kw[76], u); } else { if(fsnmp_cc(fc)) { fsnmplog(fc, PRIO_ERROR, fsnmp_kw[77], pmin, pmax); fc->exit_code = EXIT_FAILED; } } } $? "- bind_local_address %d", back return back; } /** Timeout for socket operations. */ typedef struct timeval TV; /** Size of timeval. */ #define SZTV sizeof(TV) /** Pass pagecount to accounting system. @param fc Fsnmp job. @param isof Flag: Is OF filter. @param passno Pass (0=before, 1=after data transfer). @param pc Page count. */ static void do_accounting DK_P4(FC *,fc, int,isof, int,passno, unsigned long,pc) { int back = 0; char c; char *fn; char *ptr, *optr; FILE *fipo; unsigned long ipaddr; int sock, res; struct sockaddr_in sain; unsigned u; struct timeval tv; fd_set rfds; size_t sz; sprintf(fc->buffer_out, "%s '-p%lu'", begin_kw[2*isof+passno], pc); $? "+ do_accounting \"%s\"", fc->buffer_out add_opt(fc, 'H'); add_opt(fc, 'n'); add_opt(fc, 'P'); add_opt(fc, 'b'); add_opt(fc, 't'); add_opt(fc, 'C'); add_opt(fc, 'J'); for(c = 'a'; c <= 'z'; c++) add_if_not_done(fc, c); for(c = 'A'; c <= 'Z'; c++) add_if_not_done(fc, c); $? ". do_accounting \"%s\"", fc->buffer_out fn = fc->a_acctdest; if(!fn) { fn = fsnmpcmd_get_argv(fc, 'a'); } if(fn) { fn = dkstr_start(fn, NULL); if(fn) { dkstr_chomp(fn, NULL); fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[61], fn); if(*fn == '|') { $? ". pipe" fn++; fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[85], fn); fipo = popen(fn, "w"); if(fipo) { fputs(fc->buffer_out, fipo); fputc('\n', fipo); fflush(fipo); back = 1; pclose(fipo); fipo = NULL; fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[84], fn); } else { fsnmplog(fc, PRIO_ERROR, fsnmp_kw[65]); fc->exit_code = EXIT_ABORT; } } else { ptr = dkstr_chr(fn, '%'); if(ptr) { $? ". network connection" optr = ptr; *(ptr++) = '\0'; ipaddr = fsnmp_dotted_string_to_ip(fn); if(ipaddr) { ipaddr = htonl(ipaddr); } else { ipaddr = fsnmp_lookup_host(fn); } if(ipaddr) { if(sscanf(ptr, "%u", &u) == 1) { sain.sin_family = AF_INET; sain.sin_port = htons(u); sain.sin_addr.s_addr = ipaddr; sock = socket(AF_INET, SOCK_STREAM, 0); if(sock > -1) { if(bind_local_address(fc, sock, fc->ac_pmin, fc->ac_pmax)) { res = connect(sock, (struct sockaddr *)(&sain), SZSOIN); if(res == 0) { sz = strlen(fc->buffer_out); res = send(sock, fc->buffer_out, sz, 0); if(res == sz) { back = 1; if((fc->flags) & FSNMP_SHUTDOWN_PAGECOUNT) { fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[72]); if(shutdown(sock, SHUT_WR) == 0) { res = 1; fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[73]); while((res > 0) && fsnmp_cc(fc)) { res = 0; if((fc->flags) & FSNMP_TIMEOUT_PAGECOUNT) { dkmem_cpy((void *)(&tv), (void *)(&(fc->shp)), SZTV); FD_ZERO(&rfds); FD_SET(sock, &rfds); if(select((1 + sock), &rfds, NULL, NULL, &tv) > 0) { if(FD_ISSET(sock, &rfds)) { res = recv(sock, fc->buffer_in, fc->sz_buffer_in, 0); } } } else { res = recv(sock, fc->buffer_in, fc->sz_buffer_in, 0); } } } else { fsnmplog(fc, PRIO_ERROR, fsnmp_kw[71]); fc->exit_code = EXIT_FAILED; } } } else { fsnmplog(fc, PRIO_ERROR, fsnmp_kw[70]); fc->exit_code = EXIT_FAILED; } } else { fsnmplog(fc, PRIO_ERROR, fsnmp_kw[69], fn); fc->exit_code = EXIT_FAILED; } } else { fc->exit_code = EXIT_FAILED; } fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[74]); close(sock); sock = -1; } else { fsnmplog(fc, PRIO_ERROR, fsnmp_kw[68]); fc->exit_code = EXIT_FAILED; } } else { fsnmplog(fc, PRIO_ERROR, fsnmp_kw[67], ptr); fc->exit_code = EXIT_ABORT; } } else { fsnmplog(fc, PRIO_ERROR, fsnmp_kw[66], fn); fc->exit_code = EXIT_ABORT; } *optr = '%'; } else { $? ". file" fipo = dksf_fopen(fn, "a"); if(fipo) { fputs(fc->buffer_out, fipo); fputc('\n', fipo); fflush(fipo); back = 1; fclose(fipo); fipo = NULL; } else { fsnmplog(fc, PRIO_ERROR, fsnmp_kw[64]); fc->exit_code = EXIT_ABORT; } } } if(back) { fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[62]); } else { fsnmplog(fc, PRIO_ERROR, fsnmp_kw[63]); } } else { $? ". empty accounting file name" fsnmplog(fc, PRIO_ERROR, fsnmp_kw[60]); fc->exit_code = EXIT_ABORT; } } else { $? ". no accounting file specified" fsnmplog(fc, PRIO_ERROR, fsnmp_kw[59]); } $? "- do_accounting" } /** Obtain pagecount from printer, pass pagecount to the accounting system. @param fc Fsnmp job. @param isof Flag: Is OF filter. @param passno Which pass (0=before, 1=after data transfer). */ static void do_pagecount DK_P3(FC *,fc, int,isof, int,passno) { char ipnamebuffer[32]; unsigned long ipaddr; struct snmp_session session, *ss = NULL; struct snmp_pdu *rq, *rs = NULL; int cc; int log_reason, last_log_reason, status; int next_log_index, must_log, have_pagecount; time_t next_log_time, current_time; SUMMARY cs, os; $? "+ do_pagecount" fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[13], fsnmp_kw[passno ? 15 : 14]); ipaddr = (fc->sain).sin_addr.s_addr; ipaddr = ntohl(ipaddr); sprintf(ipnamebuffer, "%lu.%lu.%lu.%lu", ((ipaddr >> 24) & 0xFFUL), ((ipaddr >> 16) & 0xFFUL), ((ipaddr >> 8) & 0xFFUL), ( ipaddr & 0xFFUL) ); $? ". IP=%s", ipnamebuffer if(fc->a_community) { $? ". SNMP community ok" time(&(fc->snmp_start_time)); snmp_sess_init(&session); session.version = fc->snmp_vers; session.peername = ipnamebuffer; session.community = (unsigned char *)(fc->a_community); session.community_len = strlen(fc->a_community); ss = snmp_open(&session); if(ss) { $? ". session opened" next_log_time = 0; next_log_index = 0; last_log_reason = 0; os.ds = -1; os.ps = -1; os.pc = 0UL; have_pagecount = 0; do { $? ". start of do loop" time(¤t_time); log_reason = cc = 0; rq = rs = NULL; // attempt to get pagecount cs.ds = -1; cs.ps = -1; cs.pc = 0UL; rq = snmp_pdu_create(SNMP_MSG_GET); if(rq) { $? ". request PDU ok" snmp_add_null_var(rq, fc->oid_ds, fc->sz_oid_ds); snmp_add_null_var(rq, fc->oid_ps, fc->sz_oid_ps); snmp_add_null_var(rq, fc->oid_pc, fc->sz_oid_pc); rs = NULL; status = snmp_synch_response(ss, rq, &rs); time(¤t_time); if(status == STAT_SUCCESS) { $? ". SNMP status ok" if(rs) { $? ". have response PDU" if(rs->errstat == SNMP_ERR_NOERROR) { $? ". response PDU ok" if(apply_response_to_state(fc, &cs, rs)) { $? ". response useful" if((cs.ds != -1) && (cs.ps != -1)) { $? ". device and printer status known" log_reason = FSNMP_LOG_HAVE_SNMP; if(((cs.ds == DEVICE_RUNNING) || (cs.ds == DEVICE_WARNING)) && ((cs.ps == PRINTER_IDLE) || (cs.ps == PRINTER_OTHER))) { $? ". device and printer status ok for pagecounting" log_reason = FSNMP_LOG_PRINTER_READY; have_pagecount = 1; if(passno) { $? ". second pass" if(cs.pc > fc->pc1) { $? ". pagecount changed" fc->pc2 = cs.pc; } else { $? ". pagecount unchanged" if(current_time > fc->snmp_start_time + fc->mintimepc) { fc->pc2 = cs.pc; $? ". enough time gone by" } else { $? ". wait for printer to start job" log_reason = FSNMP_LOG_NOT_YET_PRINTED; cc = 1; have_pagecount = 0; } } } else { $? ". first pass" fc->pc1 = cs.pc; } } else { $? ". state does not allow pagecounting ds=%d ps=%d", cs.ds, cs.ps if(cs.ps == PRINTER_PRINTING) { log_reason = FSNMP_LOG_PRINTER_BUSY; } cc = 1; } } else { $? "! either device and/or printer status unknown" cc = 1; log_reason = FSNMP_LOG_ERROR_RETRIEVING; } } else { $? "! error while retrieving data from response" cc = 1; log_reason = FSNMP_LOG_ERROR_RETRIEVING; } } else { $? "! error in response PDU" log_reason = FSNMP_LOG_ERROR_IN_RESPONSE; cc = 1; } snmp_free_pdu(rs); rs = NULL; } else { $? "! no response PDU" log_reason = FSNMP_LOG_NO_RESPONSE; cc = 1; } } else { $? "! wrong SNMP status" // snmp_sess_perror(fsnmp_application_name, ss); log_reason = FSNMP_LOG_NO_RESPONSE; cc = 1; } } else { $? "! failed to create request PDU" fc->exit_code = EXIT_FAILED; fsnmplog(fc, PRIO_ERROR, fsnmp_kw[49]); } // log must_log = 0; if(log_reason != last_log_reason) { $? ". condition changed, must log" must_log = 1; time(&next_log_time); next_log_time += 1; next_log_index = 0; } else { $? ". condition did not change" if(current_time >= next_log_time) { $? ". too much time, log message again" next_log_index++; if(next_log_index >= no_log_intervals) next_log_index = no_log_intervals - 1; next_log_time = current_time + log_intervals[next_log_index]; must_log = 1; } } if(must_log) { switch(log_reason) { case FSNMP_LOG_NO_RESPONSE: { fsnmplog(fc, PRIO_ERROR, fsnmp_kw[20]); } break; case FSNMP_LOG_ERROR_IN_RESPONSE: { fsnmplog(fc, PRIO_ERROR, fsnmp_kw[21]); } break; case FSNMP_LOG_PRINTER_BUSY: { fsnmplog(fc, PRIO_INFO, fsnmp_kw[22]); } break; case FSNMP_LOG_PRINTER_READY: { fsnmplog(fc, PRIO_INFO, fsnmp_kw[23]); } break; case FSNMP_LOG_ERROR_RETRIEVING: { fsnmplog(fc, PRIO_ERROR, fsnmp_kw[24]); } break; case FSNMP_LOG_PRINTER_PROBLEM: case FSNMP_LOG_HAVE_SNMP: { if(cs.ds < 1) cs.ds = 1; if(cs.ds > 5) cs.ds = 5; if(cs.ps < 1) cs.ps = 2; if(cs.ps > 5) cs.ps = 5; fsnmplog( fc, PRIO_INFO, fsnmp_kw[25], fsnmp_kw[25+cs.ds], cs.ds, fsnmp_kw[30+cs.ps], cs.ps ); if((cs.ds == DEVICE_WARNING) || (cs.ds == DEVICE_DOWN) || (cs.ds == DEVICE_UNKNOWN)) { fsnmplog(fc, PRIO_ERROR, fsnmp_kw[36]); } } break; case FSNMP_LOG_NOT_YET_PRINTED: { fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[37]); } break; } } last_log_reason = log_reason; // sleep if(cc && fsnmp_cc(fc)) { $? ". sleep a moment" switch(log_reason) { case FSNMP_LOG_PRINTER_BUSY: { $? ". short sleep (100ms)" fd_set rfds; struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 100000; FD_ZERO(&rfds); (void)select(0,&rfds,NULL,NULL,&tv); } break; default: { $? ". long sleep (1s)" sleep(1); } break; } } $? ". end of do loop" } while((cc) && fsnmp_cc(fc)); snmp_close(ss); ss = NULL; if(fsnmp_cc(fc)) { if(have_pagecount) { do_accounting(fc, isof, passno, cs.pc); fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[19], cs.pc); } else { fsnmplog(fc, PRIO_DEBUG, fsnmp_kw[18]); } } } else { $? "! failed to open SNMP session" fc->exit_code = EXIT_FAILED; fsnmplog(fc, PRIO_ERROR, fsnmp_kw[17]); } } else { $? "! no SNMP community" fc->exit_code = EXIT_ABORT; fsnmplog(fc, PRIO_ERROR, fsnmp_kw[16]); } $? "- do_pagecount" } /** Normal filter: Prepare output, pagecount before print job. @param fc Fsnmp job. */ static void filter_start DK_P1(FC *,fc) { $? "+ filter_start" if(fsnmp_cc(fc)) { initialize_snmp(fc); do_pagecount(fc, 0, 0); } $? "- filter_start" } /** Normal filter: Output finished, pagecount after print job. @param fc Fsnmp job. */ static void filter_end DK_P1(FC *,fc) { $? "+ filter_end" if(fsnmp_cc(fc)) { do_pagecount(fc, 0, 1); } $? "- filter_end" } /** Normal filter: process response from printer. @param fc Fsnmp job. @param s Number of bytes in printer response. */ static void printer_response DK_P2(FC *,fc, size_t,s) { char *ptr; size_t i; ptr = fc->buffer_in; for(i = 0; i < s; i++) { if(!((isascii(*ptr)) && (isprint(*ptr)))) { *ptr = ' '; } ptr++; } fsnmplog_response(fc, s); } /** Normal filter: flush output buffer to printer. @param fc Fsnmp job. */ static void flush_output DK_P1(FC *,fc) { ssize_t s1; fd_set rfds; struct timeval tv; if(fc->sz_bo_used && fsnmp_cc(fc)) { if((fc->flags) & FSNMP_FLAG_STDOUT) { s1 = write(fc->os, fc->buffer_out, fc->sz_bo_used); } else { s1 = send(fc->os, fc->buffer_out, fc->sz_bo_used, 0); } if(s1 < fc->sz_bo_used) { fc->exit_code = EXIT_FAILED; fsnmplog(fc, PRIO_ERROR, fsnmp_kw[50]); } if(fsnmp_cc(fc)) { FD_ZERO(&rfds); FD_SET(fc->os, &rfds); tv.tv_sec = 0; tv.tv_usec = 0; if(select((1 + fc->os), &rfds, NULL, NULL, &tv) > 0) { if(FD_ISSET(fc->os, &rfds)) { if((fc->flags) & FSNMP_FLAG_STDOUT) { s1 = read(fc->os, fc->buffer_in, fc->sz_buffer_in); } else { s1 = recv(fc->os, fc->buffer_in, fc->sz_buffer_in, 0); } if(s1 > 0) { printer_response(fc, s1); } } } } } fc->sz_bo_used = 0; } /** Normal filter: add output character to output buffer, flush buffer if necessary. @param fc Fsnmp job. @param c Character (byte) to add. */ static void add_out_char DK_P2(FC *,fc, char,c) { (fc->buffer_out)[fc->sz_bo_used] = c; fc->sz_bo_used += 1; if(fc->sz_bo_used >= fc->sz_buffer_out) { flush_output(fc); } } /** Normal filter: Transfer contents of temporary file (created by filter_input()) to printer. @param fc Fsnmp job. */ static void write_temp_file DK_P1(FC *,fc) { FILE *fipo; ssize_t s1 = 0, i = 0; int is_first = 1, last_was_ctrld = 0, had_data = 0; char *ptr; if(fsnmp_cc(fc)) { fipo = dksf_fopen(fc->temp_file, "rb"); if(fipo) { fc->sz_bo_used = 0; do { s1 = fread(fc->buffer_in, 1, fc->sz_buffer_in, fipo); if(s1 > 0) { had_data = 1; if(is_first) { is_first = 0; if((fc->flags) & FSNMP_FLAG_CTRL_D_START) { if((fc->buffer_in)[0] != CTRL_D) { add_out_char(fc, CTRL_D); } } } ptr = fc->buffer_in; for(i = 0; i < s1; i++) { add_out_char(fc, *(ptr++)); } if((fc->buffer_in)[s1 - 1] == CTRL_D) { last_was_ctrld = 1; } else { last_was_ctrld = 0; } } } while(fsnmp_cc(fc) && (s1 > 0)); if(fsnmp_cc(fc)) { if((fc->flags) & FSNMP_FLAG_CTRL_D_END) { if(!last_was_ctrld) { if(had_data) { add_out_char(fc, CTRL_D); } } } flush_output(fc); } fclose(fipo); fipo = NULL; } else { fc->exit_code = EXIT_FAILED; fsnmplog(fc, PRIO_ERROR, fsnmp_kw[51]); } } } /** Find host name and port number. @param fc Fsnmp job. */ static void find_host_and_port DK_P1(FC *,fc) { $? "+ find_host_and_port" $? "- find_host_and_port" } /** Normal filter: Send print job data to output. @param fc Fsnmp job. */ static void filter_output DK_P1(FC *,fc) { struct sockaddr_in sain; struct timeval tv; fd_set rfds; int res; $? "+ filter_output" if(fsnmp_cc(fc)) { if((fc->flags) & FSNMP_FLAG_STDOUT) { fc->os = 1; fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[40]); write_temp_file(fc); if(fsnmp_cc(fc)) { fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[41]); } } else { find_host_and_port(fc); if(fsnmp_cc(fc)) { fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[38]); fc->os = socket(AF_INET, SOCK_STREAM, 0); if(fc->os > -1) { $? ". socket opened" if(bind_local_address(fc, fc->os, fc->dt_pmin, fc->dt_pmax)) { dkmem_cpy((void *)(&sain), (void *)(&(fc->sain)), SZSOIN); res = connect(fc->os, (struct sockaddr *)(&sain), SZSOIN); if(res == 0) { $? ". socket connected" fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[39]); write_temp_file(fc); if(fsnmp_cc(fc)) { fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[41]); } if((fc->flags) & FSNMP_SHUTDOWN_TRANSFER) { fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[42]); if(shutdown(fc->os, SHUT_WR) == 0) { fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[43]); res = 1; while((res > 0) && fsnmp_cc(fc)) { res = 0; if((fc->flags) & FSNMP_TIMEOUT_TRANSFER) { dkmem_cpy((void *)(&tv), (void *)(&(fc->shd)), SZTV); FD_ZERO(&rfds); FD_SET(fc->os, &rfds); if(select((1 + fc->os), &rfds, NULL, NULL, &tv) > 0) { if(FD_ISSET(fc->os, &rfds)) { res = recv(fc->os, fc->buffer_in, fc->sz_buffer_in, 0); if(res > 0) { printer_response(fc, res); } } } } else { res = recv(fc->os, fc->buffer_in, fc->sz_buffer_in, 0); if(res > 0) { printer_response(fc, res); } } } } else { fsnmplog(fc, PRIO_ERROR, fsnmp_kw[80]); fc->exit_code = EXIT_FAILED; } } } else { $? "! failed to connect" fsnmplog(fc, PRIO_ERROR, fsnmp_kw[78]); fc->exit_code = EXIT_FAILED; } } else { $? "! failed to bind local address } close(fc->os); fc->os = -1; fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[44]); } else { $? "! failed to open socket" fc->exit_code = EXIT_FAILED; fsnmplog(fc, PRIO_ERROR, fsnmp_kw[79]); } } } } $? "- filter_output" } /** Normal filter: Run filter processing. @param fc Fsnmp job. */ void fsnmp_filter DK_P1(FC *,fc) { $? "+ fsnmp_filter" filter_input(fc); filter_start(fc); filter_output(fc); filter_end(fc); $? "- fsnmp_filter" } /** Of filter: Start processing (pagecount before job). @param fc Fsnmp job. */ static void of_start DK_P1(FC *,fc) { $? "+ of_start" if(fsnmp_cc(fc)) { initialize_snmp(fc); do_pagecount(fc, 1, 0); } $? "- of_start" } /** Of filter: Finish processing (pagecount after job). @param fc Fsnmp job. */ static void of_end DK_P1(FC *,fc) { $? "+ of_end" if(fsnmp_cc(fc)) { do_pagecount(fc, 1, 1); } $? "- of_end" } /** Of filter: Input sequence to suspend filter. */ static char filter_stop[] = { "\031\001" }; /** Of filter: Transfer standard input to standard output, suspend itself if sequence is detected. @param fc Fsnmp job. */ static void of_transfer DK_P1(FC *,fc) { int state = 0; char c; $? "+ of_transfer" while((state < 2) && fsnmp_cc(fc)) { c = getchar(); if(c == EOF) { $? ". end of file" if(state == 1) { putchar(filter_stop[0]); } state = 2; } else { $? ". found %o %c", c, c switch(state) { case 0: { if(c == filter_stop[0]) { state = 1; } else { putchar(c); } } break; case 1: { if(c == filter_stop[1]) { fflush(stdout); state = 0; $? ". process suspending" fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[45]); kill(getpid(), SIGSTOP); $? ". process woke up" fsnmplog(fc, PRIO_PROGRESS, fsnmp_kw[46]); state = 0; } else { if(c == filter_stop[0]) { putchar(filter_stop[0]); } else { putchar(filter_stop[0]); putchar(c); state = 0; } } } break; } } } fflush(stdout); $? "- of_transfer" } /** Of filter: Filter processing. @param fc Fsnmp job. */ void fsnmp_of DK_P1(FC *,fc) { of_start(fc); of_transfer(fc); of_end(fc); } /** Check whether or not standard output is already connected to a socket. Retrieve peer name to be used as hostname for SNMP. @param fc Fsnmp job. */ void fsnmp_check_peer DK_P1(FC *,fc) { size_t szsain; szsain = SZSOIN; if(getpeername(1, (struct sockaddr *)(&(fc->sain)), &szsain) == 0) { if(szsain == SZSOIN) { fc->flags |= FSNMP_FLAG_STDOUT; fsnmplog(fc, PRIO_INFO, fsnmp_kw[9]); } } }