#include #include #include #include #include #include #include #include #include #include #include "netadm.h" #include #define ALARM_TIME 5 static char alarm_detected=0; static void slave_alarm (int) { alarm_detected = 1; signal (SIGALRM,slave_alarm); alarm (ALARM_TIME); } PUBLIC SLAVE_FCTL::SLAVE_FCTL(int _fdin) { fdin = _fdin; accum[0] = '\0'; nbacc = 0; } /* Read like fread (several read may be done to pack one slave_read). Return 0 if ok, -1 if error An alarm is an error condition */ PUBLIC int SLAVE_FCTL::readlen (void *buf, int size, bool linemode) { int ret = -1; int nbalarm = 0; while (1){ char *pt; if (nbacc >= size){ memcpy (buf,accum,size); int newsize = nbacc - size; if (newsize > 0){ memmove (accum,accum+size,newsize); } nbacc = newsize; ret = size; break; }else if (linemode && (pt=strchr(accum,'\n'))!=NULL){ int len = (int)(pt-accum)+1; memcpy (buf,accum,len); int newsize = nbacc - len; if (newsize > 0){ memmove (accum,accum+len,newsize); } nbacc = newsize; ((char*)buf)[len] = '\0'; // Strip all control character from the end of the line int last = len - 1; while (last >= 0 && ((char*)buf)[last] < ' '){ ((char*)buf)[last] = '\0'; last--; } ret = len; break; }else{ int len = read (fdin,accum+nbacc,sizeof(accum)-nbacc); if (len <= 0){ if (alarm_detected){ nbalarm++; alarm_detected = 0; if (nbalarm > 1){ printf ("*** %d seconds without a transaction\n" ,nbalarm*ALARM_TIME); } if (nbalarm == 24) break; }else{ break; } }else{ nbacc += len; accum[nbacc] = '\0'; // To help line mode nbalarm=0; } } } accum[nbacc] = '\0'; return ret; } /* Read like fread (several read may be done to pack one slave_read). Return 0 if ok, -1 if error An alarm is an error condition */ PUBLIC int SLAVE_FCTL::readlen (void *buf, int size) { return readlen (buf,size,false); } /* Read like fread (several read may be done to pack one slave_read). Return 0 if ok, -1 if error An alarm is an error condition */ PUBLIC int SLAVE_FCTL::readok (void *buf, int size) { return readlen (buf,size) == size ? 0 : -1; } /* Read like fgets (several read may be done to pack one slave_gets). Return 0 if ok, -1 if error An alarm is an error condition */ PUBLIC int SLAVE_FCTL::gets (char *buf, int size) { buf[0] = '\0'; size--; // Makes sure there is room for the '\0' return readlen (buf,size,true) > 0 ? 0 : -1; } static int slave_getarg ( SLAVE_FCTL &ctl, FILE *fout, const char *msg, const char *expect, char value[100]) { int ret = -1; fprintf (fout,"%d %s\n",PROTO_C_PROMPT,msg); fflush (fout); char line[100]; if (ctl.gets (line,sizeof(line))!=-1){ char words[10][100]; if (str_splitline (line,' ',words,10)==2 && strcmp(words[0],expect)==0){ strcpy (value,words[1]); ret = 0; }else{ fprintf (fout,"%d Expecting: %s value\n",PROTO_C_ERROR,expect); } } return ret; } static bool slave_checkversion (SLAVE_FCTL &ctl, FILE *fout) { bool ret = false; char version[100]; if (slave_getarg (ctl,fout,"send protocol version","version" ,version) != -1){ if (strcmp(version,PROTO_VERSION)==0){ fprintf (fout,"%d protocol version ok\n",PROTO_C_ACK); ret = true; }else{ fprintf (fout,"%d incompatible version protocol: expecting %s\n" ,PROTO_C_ERROR,PROTO_VERSION); } } return ret; } static bool slave_checkpass (SLAVE_FCTL &ctl, FILE *fout) { bool ret = false; char pass[100]; if (slave_getarg (ctl,fout,"Send password","password",pass)!=-1){ if (perm_validpass ("root",pass)){ fprintf (fout,"%d Password ok\n",PROTO_C_ACK); ret = true; }else{ fprintf (fout,"%d Invalid password\n",PROTO_C_ERROR); } } return ret; } /* Create the directory which will contain this file if needed */ static int slave_mkdir (const char *fname) { int ret = -1; // We must create the directories as needed char dir[PATH_MAX]; strcpy (dir,fname); char *pt = strrchr(dir,'/'); if (pt != NULL){ *pt = '\0'; ret = file_mkdirp (dir,0,0,0700); } return ret; } /* Receive a file */ static int slave_putfile (SLAVE_FCTL &ctl, FILE *fout, const char *fname) { int ret = -1; char dimension[100]; if (slave_getarg (ctl,fout,"Send dimension","dimension",dimension)!=-1){ int dim = atoi(dimension); fprintf (fout,"%d send %d bytes in a row\n",PROTO_C_ACK,dim); fflush (fout); slave_mkdir (fname); FILE *fdata = fopen (fname,"w"); // Even if we can't open the file, we must accept the bytes // to keep the protocol happy if (dim > 0 && dim < 10000000){ while (dim > 0){ char buf[30000]; int len = 30000; if (dim < 30000) len = dim; if (ctl.readok(buf,len) != -1){ if (fdata != NULL) fwrite (buf,1,len,fdata); }else{ break; } dim -= len; } } if (fdata != NULL){ ret = fclose (fdata); if (ret == 0){ fprintf (fout,"%d Copy ok\n",PROTO_C_ACK); }else{ fprintf (fout,"%d Copy failed\n",PROTO_C_ERROR); } }else if (fdata == NULL){ fprintf (fout,"%d Can't open file\n%s\n(%s)\n",PROTO_C_ERROR ,fname,strerror(errno)); } } return ret; } /* Simple stream which add a "100 " in front of every line */ class SSTREAM_DATA: public SSTREAM_FILE{ /*~PROTOBEG~ SSTREAM_DATA */ public: SSTREAM_DATA (FILE *fout); void puts (const char *s); /*~PROTOEND~ SSTREAM_DATA */ }; PUBLIC SSTREAM_DATA::SSTREAM_DATA(FILE *fout) : SSTREAM_FILE (fout) { } PUBLIC void SSTREAM_DATA::puts (const char *s) { char buf[200]; snprintf (buf,sizeof(buf)-1,"%d %s",PROTO_C_DATA,s); SSTREAM_FILE::puts (buf); } int slave_proto (int fdin, int fdout) { int ret = -1; net_setshowmode (false); dialog_setmode (DIALOG_SILENT); FILE *fout = fdopen (fdout,"w"); signal (SIGALRM,slave_alarm); //alarm (ALARM_TIME); SLAVE_FCTL ctl (fdin); if (slave_checkversion(ctl,fout) && slave_checkpass(ctl,fout)){ ret = 0; while (1){ fprintf (fout,"%d Send command\n",PROTO_C_PROMPT); fflush (fout); char line[2*PATH_MAX]; if (ctl.gets (line,sizeof(line))==-1){ break; }else{ SSTRINGS tbwords; int nb = str_splitline (line,' ',tbwords); const char *words[nb+1]; for (int i=0; iget(); words[nb] = NULL; if (strcmp(words[0],"putfile")==0 && nb == 2){ if (slave_putfile (ctl,fout,words[1])==-1) break; }else if (strcmp(words[0],"import")==0 && nb == 2){ int ret = netadm_import (words[1]); if (ret == -1){ fprintf (fout,"%d import failed\n",PROTO_C_ERROR); }else{ fprintf (fout,"%d import ok\n",PROTO_C_ACK); } }else if (strcmp(words[0],"md5sum")==0 && nb == 2){ SSTREAM_DATA ss (fout); SSTRINGS tb; tb.add (new SSTRING (words[1])); configf_md5sum (tb,ss); }else if (strcmp(words[0],"status")==0 && nb == 1){ netconf_status(); }else if (strcmp(words[0],"update")==0 && nb == 1){ netconf_update(); if (net_getnberr()>0){ fprintf (fout,"%d There were some errors while updating\n",PROTO_C_ERROR); }else{ fprintf (fout,"%d update done\n",PROTO_C_ACK); } }else if (strcmp(words[0],"nop")==0 && nb >= 1){ fprintf (fout,"%d ok\n",PROTO_C_ACK); }else if (strcmp(words[0],"api")==0 && nb >= 4){ MODULE_API_SERIAL *api = module_get_api_serial(words[1] ,atoi(words[2]),"netadm"); if (api == NULL){ fprintf (fout,"%d Not such api or version\n",PROTO_C_ERROR); }else{ fprintf (fout,"%d executing\n",PROTO_C_COMMENT); fflush (fout); SSTRINGS res; int ret = api->exec (words[3],nb-4,words+4,res); fprintf (fout,"%d executing 2\n",PROTO_C_COMMENT); fflush (fout); for (int i=0; iget()); } fprintf (fout,"%d %d\n",PROTO_C_ACK,ret); delete api; } }else if (strcmp(words[0],"quit")==0 && nb == 1){ fprintf (fout,"%d bye\n",PROTO_C_COMMENT); break; }else if (strcmp(words[0],"help")==0 && nb == 1){ fprintf (fout,"%d putfile path\n",PROTO_C_COMMENT); fprintf (fout,"%d import subsys\n",PROTO_C_COMMENT); fprintf (fout,"%d md5sum subsys\n",PROTO_C_COMMENT); fprintf (fout,"%d status\n",PROTO_C_COMMENT); fprintf (fout,"%d update\n",PROTO_C_COMMENT); fprintf (fout,"%d quit\n",PROTO_C_COMMENT); }else{ fprintf (fout,"%d Invalid command\n",PROTO_C_ERROR); break; } } } } fflush (fout); fclose (fout); sleep(2); return ret; }