#include #include #include #include #include #include #include #include "mrtg.h" #include "mrtg.m" #include #include #include static const char subsys_mrtg[]="mrtg"; static LINUXCONF_SUBSYS subb (subsys_mrtg,P_MSG_R(M_MRTG)); static HELP_FILE help_mrtg ("mrtg","mrtg"); static CONFIG_FILE f_cfg ("/home/httpd/html/mrtg/mrtg.cfg" ,help_mrtg,CONFIGF_MANAGED|CONFIGF_OPTIONAL ,"root","root",0644 ,subsys_mrtg); static PRIVILEGE p_mrtg ("mrtgadmin" ,P_MSG_U(T_PRIVMRTG,"MRTG administrator") ,P_MSG_U(T_PMISC,"9-Miscellaneous")); struct SNMPINFO{ SSTRING community; SSTRING key; SSTRING host; }; class MRTG_CONF: public ARRAY_OBJ{ public: SSTRING id; CSSTRING target; SSTRING maxbytes_comment; int maxbytes; CSSTRING maxbytes1; CSSTRING maxbytes2; CSSTRING title; CSSTRING pagetop; CSSTRING options; CSSTRING ylegend; /*~PROTOBEG~ MRTG_CONF */ public: MRTG_CONF (const char *_id); MRTG_CONF (void); int edit (void); private: void init (void); public: bool is_snmp (void); void target2command (SSTRING&command); void target2snmp (SNMPINFO&sn); /*~PROTOEND~ MRTG_CONF */ }; PRIVATE void MRTG_CONF::init() { maxbytes = 1250000; ylegend.setfrom ("Bytes per Second"); } PUBLIC MRTG_CONF::MRTG_CONF(const char *_id) { id.setfrom (_id); init(); } PUBLIC MRTG_CONF::MRTG_CONF() { init(); } PUBLIC bool MRTG_CONF::is_snmp() { const char *pt = target.get(); return *pt != '`'; } PUBLIC void MRTG_CONF::target2snmp(SNMPINFO &sn) { char tmp[target.getlen()+1]; target.copy (tmp); char *pt = strchr(tmp,':'); if (pt != NULL){ *pt++ = '\0'; sn.key.setfrom (tmp); char *start = pt; pt = strchr (pt,'@'); if (pt != NULL){ *pt++ = '\0'; sn.community.setfrom (start); sn.host.setfrom (pt); } } } PUBLIC void MRTG_CONF::target2command(SSTRING &command) { int len = target.getlen(); char tmp[len+1]; target.copy (tmp); if (tmp[len-1] == '`') tmp[len-1] = '\0'; int start = 0; if (tmp[0] == '`') start = 1; command.setfrom (tmp+start); } class MRTG_CONFS: public ARRAY{ SSTRING workdir_comment; SSTRING workdir; SSTRING last_comment; /*~PROTOBEG~ MRTG_CONFS */ public: MRTG_CONFS (void); int edit (void); MRTG_CONF *getitem (const char *id)const; MRTG_CONF *getitem (int no)const; int write (void); /*~PROTOEND~ MRTG_CONFS */ }; PUBLIC MRTG_CONF *MRTG_CONFS::getitem (int no) const { return (MRTG_CONF*)ARRAY::getitem (no); } PUBLIC MRTG_CONF *MRTG_CONFS::getitem (const char *id) const { MRTG_CONF *ret = NULL; int n = getnb(); for (int i=0; iid.cmp(id)==0){ ret = one; break; } } return ret; } /* Interpret the special lines of the mrtg config file the format is keyword[id]: value or keyword: value */ static int mrtg_split ( const char *line, char *keyw, char *id, char *value) { int ret = -1; keyw[0] = id[0] = value[0] = '\0'; line = str_skip(line); while (*line != '\0' && *line != ':' && *line != '['){ *keyw++ = *line++; } *keyw = '\0'; if (*line == '['){ line++; line = str_skip(line); while (*line != '\0' && *line != ']'){ *id++ = *line++; } *id = '\0'; if (*line == ']') line++; } while (isspace(*line) && *line != ':') line++; if (*line == ':'){ line = str_skip(line+1); strcpy (value,line); ret = 0; } strip_end (keyw); strip_end (id); strip_end (value); return ret; } PUBLIC MRTG_CONFS::MRTG_CONFS() { FILE_CFG *fin = f_cfg.fopen ("r"); if (fin != NULL){ char buf[1000]; SSTRING comments; SSTRING *lastval = NULL; while (fgets_comments (buf,sizeof(buf)-1,fin,comments,'#')!=NULL){ strip_end (buf); char keyw[200]; char id[200]; char value[PATH_MAX+200]; if (isspace (buf[0]) && lastval != NULL){ lastval->append ("\n"); lastval->append (buf); }else if (mrtg_split (buf,keyw,id,value)==-1){ xconf_error (MSG_U(E_IVLDDIREC,"Invalid directive: %s\n") ,buf); }else if (strcmp(keyw,"WorkDir")==0){ workdir_comment.setfrom (comments); workdir.setfrom (value); }else if (id[0] != '\0'){ MRTG_CONF *one = getitem (id); if (one == NULL){ one = new MRTG_CONF (id); add (one); } lastval = NULL; if (strcmp(keyw,"Target")==0){ one->target.setcomment (comments); lastval = &one->target; }else if (strcmp(keyw,"MaxBytes")==0){ one->maxbytes_comment.setfrom (comments); one->maxbytes = atoi (value); }else if (strcmp(keyw,"MaxBytes1")==0){ one->maxbytes1.setcomment (comments); lastval = &one->maxbytes1; }else if (strcmp(keyw,"MaxBytes2")==0){ one->maxbytes2.setcomment (comments); lastval = &one->maxbytes2; }else if (strcmp(keyw,"Title")==0){ one->title.setcomment (comments); lastval = &one->title; }else if (strcmp(keyw,"PageTop")==0){ one->pagetop.setcomment (comments); lastval = &one->pagetop; }else if (strcmp(keyw,"Options")==0){ one->options.setcomment (comments); lastval = &one->options; }else if (strcmp(keyw,"Ylegend")==0){ one->ylegend.setcomment (comments); lastval = &one->ylegend; }else{ xconf_error (MSG_R(E_IVLDDIREC),buf); } if (lastval != NULL) lastval->setfrom (value); }else{ xconf_error (MSG_R(E_IVLDDIREC),buf); } comments.setfrom(""); } last_comment.setfrom (comments); fclose (fin); } } /* Check if the string contains a valid positive integer or is empty Return true if ok */ static bool mrtg_validnum (const SSTRING &s) { const char *pt = s.get(); bool ret = true; while (*pt != '\0'){ if (!isdigit(*pt)){ xconf_error (MSG_U(E_IVLDOPTNUM,"Invalid number %s.\n" "Only positive number are accepted.\n" "This field is optional"),s.get()); break; } pt++; } return ret; } PUBLIC int MRTG_CONF::edit() { DIALOG dia; dia.newf_str (MSG_U(F_ID,"Graph ID"),id); if (!id.is_empty()){ dia.set_lastreadonly(); } dia.newf_str (MSG_U(F_TITLE,"Graph title"),title); dia.newf_str (MSG_U(F_TOP,"HTML heading"),pagetop); dia.newf_num (MSG_U(F_MAXBYTES,"Maximum rate"),maxbytes); int field_maxbyte1 = dia.getnb(); dia.newf_str (MSG_U(F_MAXBYTES1,"Max rate for graph1 (opt)"),maxbytes1); int field_maxbyte2 = dia.getnb(); dia.newf_str (MSG_U(F_MAXBYTES2,"Max rate for graph2 (opt)"),maxbytes2); SNMPINFO sn; SSTRING command; if (is_snmp()){ target2snmp (sn); }else{ target2command (command); } dia.newf_title ("",MSG_U(T_SNMPREQ,"SNMP request")); dia.newf_str (MSG_U(F_HOST,"Host"),sn.host); if (sn.community.is_empty() && command.is_empty()){ sn.community.setfrom ("public"); } dia.newf_str (MSG_U(F_COMMUN,"SNMP community"),sn.community); FIELD_COMBO *comb = dia.newf_combo (MSG_U(F_FEATURE,"SNMP feature"),sn.key); { static const char *tb[][2]={ {"0", MSG_U(I_ADAPTOR0,"Network adaptor 0")}, {"1", MSG_U(I_ADAPTOR1,"Network adaptor 1")}, {"2", MSG_U(I_ADAPTOR2,"Network adaptor 2")}, {"/x.y.z.w", MSG_U(I_BYIPNUMBER,"Network adaptor by IP number")}, {NULL,NULL}, }; for (int i=0; tb[i][0] != NULL; i++){ comb->addopt (tb[i][0],tb[i][1]); } } dia.newf_title ("",""); comb = dia.newf_combo (MSG_U(F_SPCCOMMAND,"Command"),command); { static const char *tb[][2]={ {"/usr/lib/linuxconf/lib/mrtg_loadavg", MSG_U(I_LOADAVG,"Load average")}, {"/usr/lib/linuxconf/lib/mrtg_memory", MSG_U(I_MEMORY,"Memory & swap")}, {"/usr/lib/linuxconf/lib/mrtg_processes", MSG_U(I_PROCESSES,"Context switch and processes")}, {"/usr/lib/linuxconf/lib/mrtg_nbproc", MSG_U(I_NBPROC,"Running processes/User processes")}, {NULL,NULL}, }; for (int i=0; tb[i][0] != NULL; i++){ comb->addopt (tb[i][0],tb[i][1]); } } comb = dia.newf_combo (MSG_U(F_OPTIONS,"Options"),options); { comb->addopt ("gauge",MSG_U(I_GAUGE,"Gauge type data source")); } dia.newf_str (MSG_U(F_YLEGEND,"Y legend"),ylegend); int nof = 0; dia.delwhat (MSG_U(I_DELRECORD,"Select [Del] to delete this record")); int ret = -1; while (1){ MENU_STATUS code = dia.edit (MSG_U(T_GRAPH,"Graph specification") ,"" ,help_mrtg ,nof,MENUBUT_CANCEL|MENUBUT_DEL|MENUBUT_ACCEPT); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else if (code == MENU_DEL){ if (xconf_delok()){ ret = 1; break; } }else if (id.is_empty()){ nof = 0; xconf_error (MSG_U(E_IDEMPTY,"You must provide an ID")); }else if (id.strchr(' ')!=NULL){ nof = 0; xconf_error (MSG_U(E_NOSPACEID,"No space allowed in the ID")); }else if (!mrtg_validnum (maxbytes1)){ nof = field_maxbyte1; }else if (!mrtg_validnum (maxbytes2)){ nof = field_maxbyte2; }else{ int snmpfilled = 0; if (!sn.host.is_empty()) snmpfilled++; if (!sn.community.is_empty()) snmpfilled++; if (!sn.key.is_empty()) snmpfilled++; if (!command.is_empty()){ if (snmpfilled > 0){ xconf_error (MSG_U(E_AMBIGIOUS ,"Can't use special command and SNMP request\n" "at the same time")); }else{ target.setfrom ("`"); target.append (command.get()); target.append ("`"); ret = 0; break; } }else if (snmpfilled < 3){ xconf_error (MSG_U(E_ATLEASTREQ ,"You must specify either\n" "a special data collection command\n" "or an SNMP request (All three fields)\n")); }else{ target.setfrom (sn.key.get()); target.append (":"); target.append (sn.community.get()); target.append ("@"); target.append (sn.host.get()); ret = 0; break; } } } return ret; } PUBLIC int MRTG_CONFS::write() { int ret = -1; FILE_CFG *fout = f_cfg.fopen (&p_mrtg,"w"); if (fout != NULL){ comment_write (workdir_comment,fout); if (!workdir.is_empty()){ fprintf (fout,"WorkDir: %s\n",workdir.get()); } for (int i=0; iid.get(); comment_write (one->target.comment,fout); fprintf (fout,"Target[%s]: %s\n",id,one->target.get()); comment_write (one->maxbytes_comment,fout); fprintf (fout,"MaxBytes[%s]: %d\n",id,one->maxbytes); comment_write (one->maxbytes1.comment,fout); if (!one->maxbytes1.is_empty()){ fprintf (fout,"MaxBytes1[%s]: %s\n",id,one->maxbytes1.get()); } comment_write (one->maxbytes2.comment,fout); if (!one->maxbytes2.is_empty()){ fprintf (fout,"MaxBytes2[%s]: %s\n",id,one->maxbytes2.get()); } comment_write (one->title.comment,fout); fprintf (fout,"Title[%s]: %s\n",id,one->title.get()); comment_write (one->pagetop.comment,fout); fprintf (fout,"PageTop[%s]: %s\n",id,one->pagetop.get()); comment_write (one->options.comment,fout); fprintf (fout,"Options[%s]: %s\n",id,one->options.get()); comment_write (one->ylegend.comment,fout); fprintf (fout,"Ylegend[%s]: %s\n",id,one->ylegend.get()); } comment_write (last_comment,fout); ret = fclose (fout); } return ret; } PUBLIC int MRTG_CONFS::edit() { DIALOG_LISTE dia; int nof = 0; while (1){ if (dia.getnb() == 0){ dia.newf_head ("",MSG_U(H_MRTG,"Host\tFeature")); for (int i=0; iis_snmp()){ SNMPINFO sn; one->target2snmp (sn); dia.new_menuitem (sn.host.get(),sn.key.get()); }else{ SSTRING command; one->target2command (command); dia.new_menuitem ("",command.get()); } } dia.addwhat (MSG_U(I_ADDCONF,"Select [Add] to add a new configuration")); } MENU_STATUS code = dia.editmenu (MSG_U(T_GRAPHS,"Graphs list") ,MSG_U(I_GRAPHS,"Here is the list of all graphs collected\n" "by MRTG") ,help_mrtg ,nof,0); bool mustdelete=false; if (code == MENU_QUIT || code == MENU_ESCAPE){ break; }else if (code == MENU_ADD){ MRTG_CONF *one = new MRTG_CONF; mustdelete = editone (one)==0; }else{ mustdelete = editone (nof)!=-1; } if (mustdelete){ dia.remove_all(); } } return 0; } void mrtg_edit () { MRTG_CONFS confs; confs.edit(); }