#include "config.h" #include #include #include #include #include //#include #include #include #include #include #include #define HAVE_DECL_GETOPT 1 #ifdef HAVE_GETOPT_H #include #else #include "getopt.h" #endif #include "compat.h" #include "structfill.h" #include "argflags.h" #include "npaoids.h" #include "npaconsts.h" #include "npastructs.h" #include "npahelpers.h" #include "printmib.h" #include "snmpsock.h" #define MAXTHREADS 100 #define ERR_UNKNOWN_OPT 1 #define ERR_BAD_PORT 2 #define ERR_MISSING_PARAMETER 3 #define ERR_SNMP_OID_NOT_FOUND 4 //hardcoded in session.C #define ERR_NO_HOSTMIB_SUPPORT 5 #define ERR_NO_PRINTMIB_SUPPORT 6 #define ERR_NO_HOST_SPECIFIED 7 // ERR_NO_RESPONSE 8 defined in npahelpers.C // ERR_SOCK_ERROR 9 defined in npahelpers.C // ERR_NOT_PRINTER 10 defined in npahelpers.C // ERR_UNKNOWN_DEVICE 11 defined in npahelpers.C extern char *optarg; struct thr_dat{ SNMP_session *session; char **optparams; unsigned long operflags; unsigned long argflags[3]; ConnectionInfoRequest *cir; }; struct privmib_hdlr{ const char *oid; void (*hfunct)(SNMP_session &,unsigned long *,BerBase *); }; char *progname; //lookey my one global variable. unsigned int num_sessions=0; unsigned int act_sessions=0; unsigned int thr_reaped=0; pthread_mutex_t act_sessions_m=PTHREAD_MUTEX_INITIALIZER; void *do_reqs(void*); void *join_threads(void*); int main(int argc, char **argv){ int retval=0; // don't change the order of these without changing the order in npaconsts.h static const struct option options[]= { {"version", no_argument,&retval,VERSION_FLAG}, {"all", no_argument,&retval,ALL_FLAG}, {"debugsnmp", no_argument,&retval,DEBUGSNMP_FLAG}, {"name", no_argument,&retval,NAME_FLAG}, {"vendor", no_argument,&retval,VENDOR_FLAG}, {"model", no_argument,&retval,MODEL_FLAG}, {"contact", no_argument,&retval,CONTACT_FLAG}, {"netconfig", no_argument,&retval,NETCONFIG_FLAG}, {"printmib", no_argument,&retval,PRINTMIB_FLAG}, {"hostmib", no_argument,&retval,HOSTMIB_FLAG}, {"memory", no_argument,&retval,MEMORY_FLAG}, {"storage", no_argument,&retval,STORAGE_FLAG}, {"status", no_argument,&retval,STATUS_FLAG}, {"mediapath", no_argument,&retval,MEDIAPATH_FLAG}, {"maxpapersize",no_argument,&retval,MAXPAPERSIZE_FLAG}, {"enginespeed", no_argument,&retval,ENGINESPEED_FLAG}, {"duplex", no_argument,&retval,DUPLEX_FLAG}, {"minpapersize",no_argument,&retval,MINPAPERSIZE_FLAG}, {"inputtray", no_argument,&retval,INPUTTRAY_FLAG}, {"tabloid", no_argument,&retval,TABLOID_FLAG}, {"a4", no_argument,&retval,A4_FLAG}, {"b4", no_argument,&retval,B4_FLAG}, {"executive", no_argument,&retval,EXECUTIVE_FLAG}, {"a3", no_argument,&retval,A3_FLAG}, {"b5", no_argument,&retval,B5_FLAG}, {"letter", no_argument,&retval,LETTER_FLAG}, {"legal", no_argument,&retval,LEGAL_FLAG}, {"display", no_argument,&retval,DISPLAY_FLAG}, {"covers", no_argument,&retval,COVER_FLAG}, // From here down These all get an extra bit to indicate that they belong // to argval[1] rather than argval[0] {"languages", no_argument,&retval,LANGUAGES_FLAG+1}, {"pjl", no_argument,&retval,PJL_FLAG+1}, {"hpgl", no_argument,&retval,HPGL_FLAG+1}, {"psprinter", no_argument,&retval,PSPRINTER_FLAG+1}, {"autolang", no_argument,&retval,AUTOLANG_FLAG+1}, {"pcl", no_argument,&retval,PCL_FLAG+1}, {"postscript", no_argument,&retval,POSTSCRIPT_FLAG+1}, {"marker", no_argument,&retval,MARKER_FLAG+1}, {"pagecount", no_argument,&retval,PAGECOUNT_FLAG+1}, {"colors", no_argument,&retval,COLORS_FLAG+1}, {"resolution", no_argument,&retval,RESOLUTION_FLAG+1}, {"minmargin", no_argument,&retval,MINMARGIN_FLAG+1}, {"protocol", no_argument,&retval,PROTOCOL_FLAG+1}, {"appletalk", no_argument,&retval,APPLETALK_FLAG+1}, {"lpd", no_argument,&retval,LPD_FLAG+1}, {"netware", no_argument,&retval,NETWARE_FLAG+1}, {"port9100", no_argument,&retval,PORT9100_FLAG+1}, {"supplies", no_argument,&retval,SUPPLIES_FLAG+1}, {"cfgsrc", no_argument,&retval,CFGSRC_FLAG+1}, {"alerts", no_argument,&retval,ALERTS_FLAG+1}, {"reboot", no_argument,&retval,REBOOT_FLAG+1}, // From here down These all get an extra bit 2 to indicate that they belong // to argval[2] rather than argval[0] or argval[1] {"maxproc",required_argument,&retval,MAXPROC_FLAG+2}, {"setcontact",required_argument,&retval,SETCONTACT_FLAG+2}, {"setlocation",required_argument,&retval,SETLOCATION_FLAG+2}, {"updatefirmware",required_argument,&retval,UPDATEFIRM_FLAG+2}, {"community",required_argument,NULL, 'c'}, {"timeout",required_argument,NULL, 't'}, {"connection",required_argument,NULL, 'n'}, {"help",no_argument,NULL, 'h'}, // {"bootp",no_argument,NULL, 'B'}, // {"dhcp",no_argument,NULL, 'B'}, //yes exactly the same // {"setaccesslist",required_argument,NULL,'C'}, // {"disable",required_argument,NULL, 'D'}, // {"enable",required_argument,NULL, 'E'}, // {"default",required_argument,NULL, 'F'}, // {"formatterver",no_argument,NULL, 't'}, // {"serialnum",no_argument,NULL, 'u'}, // {"jamrecovery",no_argument,NULL, 'j'}, // {"panellocked",no_argument,NULL, 'k'}, // {"errors",no_argument,NULL, 'e'}, // {"firmwarever",no_argument,NULL, 'f'}, // {"serviceperson",required_argument,NULL,'I'}, // {"lockkeypad",no_argument,NULL, ''}, // {"operator",required_argument,NULL, 'O'}, // {"location",required_argument,NULL, ''}, // {"reset",no_argument,NULL, 'R'}, // {"staticip",no_argument,NULL, 'S'}, // {"accesslist",no_argument,NULL, ''}, // {"answer",no_argument,&answer_flag,1}, // {"adam"}, // {"html"}, // {"human"}, // {"verbose"}, // {"reallyverbose"}, // {"debugsnmp"}, // {"isthisaprinter"}, // {"shorttimeout"} // {"alive"} // also add support for multiple hosts. {0,0,0,0} }; /* you can have multiple Connection Info requests and so this is the head linked list of those requests. */ ConnectionInfoRequest *cir=NULL; char *community=NULL; int timeout=50; int optval; int error=0; unsigned long argflags[3]={0,0,0}; unsigned long operflags=0; char *optparams[MAXOPTWPARAMS]; while((optval=getopt_long(argc,argv,"+cn:",options,NULL))!=EOF){ long int port; switch(optval){ case '?': error=ERR_UNKNOWN_OPT; break; case ':': error=ERR_MISSING_PARAMETER; break; case 'c': community=optarg; break; case 't': timeout=strtoul(optarg,NULL,0); break; case 'n': port=strtol(optarg,NULL,10); // problem converting assert(!(errno==ERANGE && (port==LONG_MIN || port==LONG_MAX))); // value out of bounds if(port<=0 || port>USHRT_MAX){ fprintf(stderr, "%s: port number is out of range.\n", argv[0]); exit(ERR_BAD_PORT); } cir=new ConnectionInfoRequest(port,cir); break; case 'h': fprintf(stderr,"npadmin [OPTIONS] printername\n\n" "\t-c {COMMUNITY}\t\tSet the community name to do request\n" "\t--community {COMMUNITY}\n" "\t-n {PORT}\t\tCommunicate on a port other than the standard" " SNMP port UDP 161\n" "\t--version\tPrint out the version info.\n" "\t--vendor\tPrint out the printer's vendor\n" "\t--model\tPrint out the printer's model\n" "\t--contact\tPrint out the location and contact\n" "\t--netconfig\tPrint out the network configuration\n" "\t--printmib\tPrint out whether the printer supports the" " printer MIB\n" "\t--hostmib\tPrint out whether the printer supports the host" " MIB\n" "\t--memory\tPrint out the amount of memory that the printer" " has in it.\n" "\t--storage\tPrint out the storage table\n" "\t--status\tPrint out the printer status\n" "\t--mediapath\tPrint out the media path table\n" "\t--maxpapersize\tPrint out the maximum paper size\n" "\t--enginespeed\tPrint out the print engine speed\n" "\t--duplex\tPrint out whether the printer supports duplex" " printing.\n" "\t--minpapersize\tPrint out the minimum paper size\n" "\t--inputtray\tPrint out the input tray table\n" "\t--tabloid\tPrint out an esitmate of the number of tabloid" " size sheets in the printer\n" "\t--a4\tPrint out an esitmate of the number of a4" " size sheets in the printer\n" "\t--b4\tPrint out an esitmate of the number of b4" " size sheets in the printer\n" "\t--executive\tPrint out an esitmate of the number of executive" " size sheets in the printer\n" "\t--a3\tPrint out an esitmate of the number of a3" " size sheets in the printer\n" "\t--b5\tPrint out an esitmate of the number of b5" " size sheets in the printer\n" "\t--letter\tPrint out an esitmate of the number of letter" " size sheets in the printer\n" "\t--legal\tPrint out an esitmate of the number of legal" " size sheets in the printer\n" "\t--protocol\tPrint out the ways of getting print data into" " a printer\n" "\t--appletalk\tPrint out if the printer supports appletalk\n" "\t--lpd\tPrint out if the printer supports the LPD protocol\n" "\t--netware\tPrint out if the printer is capable of acting like" " a netware server\n" "\t--port9100\t prints out if the printer admits to having a " "bidirectional TCP/IP port\n" "\t--languages\t\n" "\t--pjl\t\n" "\t--hpgl\t\n" "\t--psprinter\t\n" "\t--autolang\t\n" "\t--pcl\t\n" "\t--postscript\t\n" "\t--marker\t\n" "\t--pagecount\t\n" "\t--colors\t\n" "\t--resolution\t\n" "\t--minmargin\t\n" "\t--supplies\t\n" "\t--alerts\t\n" "\t--display\t\n" "\t--covers\t\n" "\t--community community \t\n" "\t--connection port\t\n" "\t--debugsnmp\t\n" "\t--reboot\t\n" "\t--timeout\t\n" ); exit(0); break; case 0: // all these long options assert((retval&0x7)<=2);/*right now we only need three words for the options */ argflags[retval&0x7]|=retval&0xFFFFFFF8ul; if((retval&0x07)==2){ /* word 2 is reserved for items that need a parameter */ // count how far over the bit is unsigned long tmpval=retval&0xfffffff8ul; int i; for(i=0;tmpval!=1;tmpval/=2,i++); optparams[i-3]=optarg; } break; default: assert(0); // huh -- never should get here. } } progname=argv[0]; if(error) { usage(); exit(error); } if(CK_VERSION_FLAG){ printf("npadmin version %s\n",VERSION); exit(0); } if(CK_HOSTMIB_FLAGS){ operflags|=NEED_HOSTMIB_FLAG; } if(CK_PRINTMIB_FLAGS){ operflags|=NEED_PRINTMIB_FLAG; } if(CK_PRIVATEMIB_FLAGS){ operflags|=NEED_VENDOR_FLAG; } if(optind>=argc){ // the user didn't specify a hostname fprintf(stderr,"npadmin: no printer name specified.\n"); exit(ERR_NO_HOST_SPECIFIED); } /* ------------------- Real code begins --------------------- */ SNMP_socket sock(2,timeout/2); SNMP_session *sessions=NULL; for(;optindnext=sessions; sessions=newone; } } else if(strchr(argv[optind],'/')){ // the network case unsigned int val[8]; unsigned int baseaddr, topaddr, mask; int read=sscanf(argv[optind],"%u.%u.%u.%u/%u.%u.%u.%u",val,val+1,val+2, val+3,val+4,val+5,val+6,val+7); assert((read==5||read==8) && val[0]<256 && val[1]<256 && val[2]<256 && val[3]<256); if(read==5){ assert(val[4]<30); mask=0xffffffffu << (32-val[4]); }else{ assert(val[4]<256 && val[5]<256 && val[6]<256 && val[7]<256); mask=(val[4]<<24)|(val[5]<<16)|(val[6]<<8)|val[7]; } baseaddr=((val[0]<<24)|(val[1]<<16)|(val[2]<<8)|val[3]) & mask; topaddr=baseaddr|~mask; // loop through all the addrs skipping the network and broadcast for(baseaddr++;baseaddr>24, (baseaddr&0xff0000)>>16,(baseaddr&0xff00)>>8,baseaddr&0xff); SNMP_session *newone=new SNMP_session(&sock,buf,cmty); assert(newone); num_sessions++; newone->next=sessions; sessions=newone; } } else { // the single ip case SNMP_session *newone=new SNMP_session(&sock,argv[optind],cmty); assert(newone); num_sessions++; newone->next=sessions; sessions=newone; } }else{ // a hostname SNMP_session *newone=new SNMP_session(&sock,argv[optind],cmty); assert(newone); num_sessions++; newone->next=sessions; sessions=newone; } } if(CK_DEBUGSNMP_FLAG) for(SNMP_session *cur=sessions;cur!=NULL;cur=cur->next) cur->setDebug(); if(error) exit(error); if(num_sessions>1) SET_NAME_FLAG; pthread_t thrds[num_sessions+1]; pthread_t joiner; assert(pthread_create(&joiner,NULL,join_threads,thrds)==0); thr_dat thrdat[num_sessions]; unsigned int thr_idx=0; for(SNMP_session *cur=sessions;cur!=NULL;cur=cur->next){ thrdat[thr_idx].session=cur; thrdat[thr_idx].optparams=optparams; thrdat[thr_idx].cir=cir; thrdat[thr_idx].operflags=operflags; for(char i=0;i<3;i++) thrdat[thr_idx].argflags[i]=argflags[i]; assert(pthread_create(thrds+thr_idx,NULL,do_reqs,thrdat+thr_idx)==0); pthread_mutex_lock(&act_sessions_m); act_sessions++; pthread_mutex_unlock(&act_sessions_m); if(thr_idx-thr_reaped>MAXTHREADS) sleep(2); thr_idx++; } int *retval2; pthread_join(joiner,(void**)&retval2); SNMP_session::end(); exit(*retval2); } /* This function runs a thread whose whole purpose is to clean up the various threads as they complete. It gathers the error values and then trys to come up with some semblence of an exit value for the whole process. */ void *join_threads(void *thrds_raw){ pthread_t *thrds=(pthread_t*)thrds_raw; int *error=NULL; for(thr_reaped=0;thr_reaped=act_sessions){ pthread_mutex_unlock(&act_sessions_m); sleep(1); pthread_mutex_lock(&act_sessions_m); } pthread_mutex_unlock(&act_sessions_m); int *retval; pthread_join(thrds[thr_reaped],(void**)&retval); if(!error || *retval) error=retval; } return error; } /* This function runs a thread for a particular printer. There is basically one instance of this function running in a thread per printer specified on the commmand line. It basically does the work of calling out to the functions that actually do the snmp queries to the printers. */ void *do_reqs(void *raw_dat_in){ static const privmib_hdlr hp_priv[]={ {HPNPCFGSOURCE,hpcfgsrc} }; static const privmib_hdlr *privmibtab[]={hp_priv,NULL,NULL,NULL,NULL}; thr_dat *dat_in=(thr_dat*)raw_dat_in; SNMP_session &session=*dat_in->session; unsigned long &operflags=dat_in->operflags; unsigned long *argflags=dat_in->argflags; /* Don't modify any of the values in optparams. It will mess up the other threads */ char **optparams=dat_in->optparams; ConnectionInfoRequest *cir=dat_in->cir; int *errorp=new int; assert(errorp); int &error=*errorp; error=0; operflags&=~NEED_HOSTMIB_FLAG; if(CK_HOSTMIB_FLAGS){ operflags|=NEED_HOSTMIB_FLAG; } operflags&=~NEED_PRINTMIB_FLAG; if(CK_PRINTMIB_FLAGS){ operflags|=NEED_PRINTMIB_FLAG; } operflags&=~NEED_VENDOR_FLAG; if(CK_PRIVATEMIB_FLAGS || CK_UPDATEFIRM_FLAG){ operflags|=NEED_VENDOR_FLAG; } /* ---- stage the general requests ---- */ if(CK_GENERALMIB_FLAGS || operflags&(NEED_HOSTMIB_FLAG | NEED_PRINTMIB_FLAG | NEED_VENDOR_FLAG)){ // fputs("doing general get\n",stderr); do_general_get(session,argflags,operflags,optparams); } /* Deal with the generalmib sets */ if(CK_SGENERALMIB_FLAGS){ OidSeq gensets; if(CK_SETCONTACT_FLAG){ gensets.append(SYSCONTACT,STRING_TAG,optparams[SETCONTACT_PARAM], strlen(optparams[SETCONTACT_PARAM])); } if(CK_SETLOCATION_FLAG){ gensets.append(SYSLOCATION,STRING_TAG,optparams[SETLOCATION_PARAM], strlen(optparams[SETLOCATION_PARAM])); } OidSeq *response=session.set(&gensets); } /* ---- Connection information ------------------------------------------- */ if(cir){ do_connections(argflags,session,cir); } // ends the general section /* ------------------------------------------------------------------------ get the things out of the hostmib ------------------------------------------------------------------------*/ if(operflags&NEED_HOSTMIB_FLAG){ if(!(operflags&HAS_HOSTMIB_FLAG)){ if(!(CK_ALL_FLAG)){ error=ERR_NO_HOSTMIB_SUPPORT; if(CK_MEMORY_FLAG) not_sup("memory",session.Hostname()); if(CK_STORAGE_FLAG) not_sup("storage",session.Hostname()); if(CK_STATUS_FLAG) not_sup("status",session.Hostname()); } } else do_hostmib_get(session,argflags,operflags); } /* ------------------------------------------------------------------------- get things out of printmib ------------------------------------------------------------------------*/ if(operflags&NEED_PRINTMIB_FLAG){ if(!(operflags&HAS_PRINTMIB_FLAG)){ if(!(CK_ALL_FLAG)){ if(CK_MEDIAPATH_FLAG) not_sup("mediapath",session.Hostname()); if(CK_MAXPAPERSIZE_FLAG) not_sup("maxpapersize",session.Hostname()); if(CK_MINPAPERSIZE_FLAG) not_sup("minpapersize",session.Hostname()); if(CK_ENGINESPEED_FLAG) not_sup("enginespeed",session.Hostname()); if(CK_DUPLEX_FLAG) not_sup("duplex",session.Hostname()); if(CK_DISPLAY_FLAG) not_sup("display",session.Hostname()); if(CK_INPUTTRAY_FLAG) not_sup("inputtray",session.Hostname()); if(CK_LETTER_FLAG) not_sup("letter",session.Hostname()); if(CK_LEGAL_FLAG) not_sup("legal",session.Hostname()); if(CK_TABLOID_FLAG) not_sup("tabloid",session.Hostname()); if(CK_EXECUTIVE_FLAG) not_sup("executive",session.Hostname()); if(CK_A3_FLAG) not_sup("a3",session.Hostname()); if(CK_A4_FLAG) not_sup("a4",session.Hostname()); if(CK_B4_FLAG) not_sup("b4",session.Hostname()); if(CK_B5_FLAG) not_sup("b5",session.Hostname()); if(CK_COVER_FLAG) not_sup("cover",session.Hostname()); if(CK_ALERTS_FLAG) not_sup("alerts",session.Hostname()); if(CK_LANGUAGES_FLAG) not_sup("languages",session.Hostname()); if(CK_PCL_FLAG) not_sup("pcl",session.Hostname()); if(CK_PJL_FLAG) not_sup("pjl",session.Hostname()); if(CK_HPGL_FLAG) not_sup("hpgl",session.Hostname()); if(CK_POSTSCRIPT_FLAG) not_sup("postscript",session.Hostname()); if(CK_PSPRINTER_FLAG) not_sup("psprinter",session.Hostname()); if(CK_AUTOLANG_FLAG) not_sup("autolang",session.Hostname()); if(CK_PORT9100_FLAG) not_sup("port9100",session.Hostname()); if(CK_APPLETALK_FLAG) not_sup("appletalk",session.Hostname()); if(CK_LPD_FLAG) not_sup("lpd",session.Hostname()); if(CK_NETWARE_FLAG) not_sup("netware",session.Hostname()); if(CK_PROTOCOL_FLAG) not_sup("protocol",session.Hostname()); if(CK_PAGECOUNT_FLAG) not_sup("pagecount",session.Hostname()); if(CK_MARKER_FLAG) not_sup("marker",session.Hostname()); if(CK_RESOLUTION_FLAG) not_sup("resolution",session.Hostname()); if(CK_COLORS_FLAG) not_sup("color",session.Hostname()); if(CK_MINMARGIN_FLAG) not_sup("minmargin",session.Hostname()); if(CK_SUPPLIES_FLAG) not_sup("supplies",session.Hostname()); if(CK_REBOOT_FLAG) not_sup("reboot",session.Hostname()); error=ERR_NO_PRINTMIB_SUPPORT; } pthread_exit(&error); } if(CK_PRINTMIB_RFLAGS) do_printmib_get(session,argflags); if(CK_PRINTMIB_SFLAGS) do_printmib_set(session,argflags); } // ends printmib processing if(CK_CFGSRC_FLAG){ const privmib_hdlr *curprivhdlr; /* Right now this works fine because there is only one thing that we get out of the private mib and so there is no potential for multiple items in the private area to be set. However, as soon as we have one more item out of the private mib that needs to be set, then we will have to iterate through the bits rather than simply test one of the bits */ curprivhdlr=privmibtab[((operflags&VENDOR_BITS)>>VENDOR_OFFSET)-1]; if(curprivhdlr==NULL || curprivhdlr[(CK_PRIVATEMIB_FLAGS)>>PRIVATEMIB_OFFSET].oid==NULL){ if(CK_CFGSRC_FLAG) { not_sup("cfgsrc",session.Hostname()); error=-1; pthread_exit(&error); } }else{ curprivhdlr+=((CK_PRIVATEMIB_FLAGS)>>PRIVATEMIB_OFFSET)-1; OidSeq privreq(1,curprivhdlr->oid); OidSeq *privresp; try{ privresp=session.get(&privreq); } catch (SNMP_error_unrecoverable &error){ handle_unrecoverable(progname, session.ConnHost(),error); } assert(privresp); BerBase *privinfo=privresp->value(curprivhdlr->oid); assert(privinfo); (curprivhdlr->hfunct)(session,argflags,privinfo); } } // end of private mib processing return errorp; }