#include #include #include #include #include #include #include #include "netadm.h" #include "netadm.m" #include "../paths.h" #include "internal.h" #include static const char K_MEMBERCMD[]="membercmd"; static NETADM_HELP_FILE help_export ("export"); static bool debug=false; static void n_debug (const char *ctl, ...) { if (debug){ va_list list; va_start (list,ctl); vfprintf (stderr,ctl,list); va_end (list); } } /* Read and display errors from the transport command. Return -1 if there was any errors. */ static int export_readerr (POPEN &pop) { SSTRING s; char line[200]; while (pop.readerr(line,sizeof(line)-1)!=-1){ s.appendf ("%s\n",line); } int ret = 0; if (!s.is_empty()){ xconf_error (MSG_U(E_ERRTRANSPORT,"Error from transport command\n\n%s") ,s.get()); ret = -1; } return ret; } /* Establish the connection with the remote end. At this point, we have a link. Now we must send the password and verify the protocol version */ static int export_connect (POPEN &pop, const char *pass) { int ret = -1; bool sentver = false; bool sentpass = false; bool expectack = false; FILE *fout = pop.getfout(); bool done = false; while (!done && pop.wait(100)>0){ export_readerr (pop); char line[200]; while (pop.readout(line,sizeof(line)-1)!=-1){ int code = atoi(line); if (code == PROTO_C_PROMPT){ if (expectack){ xconf_error (MSG_U(E_OUTOFSYNC,"Protocol out of sync")); done = true; break; }else if (!sentver){ n_debug("Sending protocol version: %s\n",PROTO_VERSION); fprintf (fout,"version %s\n",PROTO_VERSION); expectack = true; sentver = true; }else if (!sentpass){ n_debug ("Sending password\n"); fprintf (fout,"password %s\n",pass); expectack = true; sentpass = true; } }else if (code == PROTO_C_ERROR){ line[60] = '\0'; xconf_error (MSG_U(E_EXPORTERR ,"Export error\n" "%s") ,line); done = true; break; }else if (code == PROTO_C_COMMENT){ dialog_consout ("%s",line); }else if (code == PROTO_C_ACK){ if (!expectack){ xconf_error (MSG_R(E_OUTOFSYNC)); done = true; break; }else{ n_debug ("Receiving ack: %s",line); expectack = false; if (sentpass){ ret = 0; done = true; break; } } }else{ n_debug ("(Connect)Receiving junk: %s",line); break; } } } n_debug ("Initial connection: %s\n",ret==-1 ? "failure" : "success"); return ret; } /* Read the list of file to export and the MD5 sum of each Return -1 if any error */ static int export_readfiles( const char *group, SSTRINGS &files, SSTRINGS &sums) { int ret = -1; char indexsum[PATH_MAX]; snprintf (indexsum,PATH_MAX-1,"%s/%s/index.sum" ,ETC_LINUXCONF_GROUPS,group); FILE *fin = xconf_fopen (indexsum,"r"); if (fin != NULL){ char line[2*PATH_MAX]; while (fgets(line,sizeof(line)-1,fin)!=NULL){ strip_end (line); char path[PATH_MAX],sum[PATH_MAX]; if (sscanf (line,"%s %s",path,sum)==2){ files.add (new SSTRING (path)); sums.add (new SSTRING (sum)); } } fclose (fin); ret = 0; } return ret; } /* Send all file needed to the member of the admin group */ static int export_send (POPEN &pop, const char *group) { int ret = -1; SSTRINGS subsyss; SSTRINGS files; SSTRINGS sums; if (netadm_readsubsyss(group,subsyss)!=-1 && export_readfiles(group,files,sums)!=-1){ files.add (new SSTRING("/index.subsys")); FILE *fout = pop.getfout(); bool expectack = false; bool sentput = false; bool sentdim = false; bool sentfile = false; int nofile = 0; char filepath[PATH_MAX]; bool done = false; while (!done){ export_readerr (pop); char line[200]; while (pop.readout(line,sizeof(line)-1)!=-1){ int code = atoi(line); if (code == PROTO_C_PROMPT){ if (expectack){ xconf_error (MSG_R(E_OUTOFSYNC)); done = true; break; }else if (!sentput){ //expectack = true; const char *f = files.getitem(nofile)->get(); dialog_consout ("%s\n",f); sprintf (filepath,"%s/%s%s" ,ETC_LINUXCONF_GROUPS,group ,f); nofile++; n_debug ("Sending file %s\n",filepath); fprintf (fout,"putfile %s\n",filepath); sentput = true; }else if (!sentdim){ struct stat st; if (stat(filepath,&st)==-1){ xconf_error (MSG_U(E_STAT,"Can't stat file\n%s") ,filepath); done = true; break; }else{ n_debug ("File dimension: %ld\n",st.st_size); fprintf (fout,"dimension %ld\n",st.st_size); expectack = true; sentdim = true; } } }else if (code == PROTO_C_ERROR){ xconf_error (MSG_R(E_EXPORTERR),line); done = true; break; }else if (code == PROTO_C_COMMENT){ dialog_consout ("%s",line); }else if (code == PROTO_C_ACK){ if (!expectack){ xconf_error (MSG_R(E_OUTOFSYNC)); done = true; break; }else if (sentfile){ n_debug ("Receing ack for sendfile\n"); expectack = false; sentfile = false; if (nofile == files.getnb()){ ret = 0; done = true; break; } }else if (sentdim){ n_debug ("Receing ack for send dimension, now sending data\n"); // expectack = false; // At the end of the copy, another acknoledge will come FILE *fdata = xconf_fopen (filepath,"r"); if (fdata == NULL){ done = true; break; }else{ char buf[30000]; int len; while ((len=fread(buf,1,sizeof(buf),fdata))>0){ fwrite (buf,1,len,fout); } fclose (fdata); sentdim = false; sentput = false; sentfile = true; } }else{ n_debug ("File received: %s",line); expectack = false; } }else{ n_debug ("(Send)Receiving junk: %s",line); break; } } if (done || pop.wait(100)<=0) break; } } n_debug ("Sending files status: %s\n",ret==-1 ? "failure" : "success"); return ret; } /* Send one command by respecting the PROMPT and ACK protocol */ static int export_sendcmd (POPEN &pop, const char *cmd) { int ret = -1; FILE *fout = pop.getfout(); bool expectack = false; bool done = false; while (!done){ char line[200]; export_readerr (pop); while (pop.readout(line,sizeof(line)-1)!=-1){ int code = atoi(line); if (code == PROTO_C_PROMPT){ if (expectack){ xconf_error (MSG_R(E_OUTOFSYNC)); done = true; break; }else{ n_debug ("Sending command: %s\n",cmd); fprintf (fout,"%s\n",cmd); expectack = true; } }else if (code == PROTO_C_ERROR){ xconf_error (MSG_R(E_EXPORTERR),line); done = true; break; }else if (code == PROTO_C_COMMENT){ dialog_consout ("%s",line); }else if (code == PROTO_C_ACK){ if (!expectack){ xconf_error (MSG_R(E_OUTOFSYNC)); }else{ n_debug ("Receing ack for sendcmd\n"); ret = 0; } done = true; break; }else{ n_debug ("(sendcmd)Receiving junk: %s",line); } if (done || pop.wait(100)<=0) break; } } return ret; } /* Replace a token in a string by a value */ static void export_replace (SSTRING &s, const char *token, const char *val) { int lenval = strlen(val); int lentoken = strlen(token); while (1){ const char *pts = s.get(); const char *pt = strstr(pts,token); if (pt == NULL) break; char tmp[s.getlen()+lenval+1]; s.copy (tmp); int skip = (int)(pt-pts); strcpy (tmp+skip,val); skip += lenval; strcpy (tmp+skip,pt+lentoken); s.setfrom (tmp); } } /* Export the various subsystem to one member of the admin group Return -1 if any error. */ PRIVATE int CLUSTER::exportone( const char *member, const char *pass, const char *cmd) { int ret = -1; /* #Specification: export protocol / transport command Linuxconf can use known transport command (defined in netadm.daemons) such as ssh, or may use any user supplied command with argument. If the supplied command has no argument and no path, linuxconf assumes the command is taken from netadm.daemons. The complete command line (with arguments) is taken from there. In all case, the command may contain the %h sequence. It is replaced by the target of the export (member of the admin group). */ SSTRING fullcmd; if (cmd[0] != '/' && strchr(cmd,' ')==NULL){ // Ok, only the command name is provided, so we rely on DAEMON_INTERNAL *dae = daemon_find (cmd); if (dae != NULL){ fullcmd.setfromf ("%s %s",dae->getpath(),dae->getargs()); } }else{ fullcmd.setfrom (cmd); } export_replace (fullcmd,"%h",member); if (fullcmd.is_empty()){ xconf_error (MSG_U(E_NOCMDTOEXP,"No command to export to member %s") ,member); }else{ n_debug ("Executing: %s\n",fullcmd.get()); POPEN pop (fullcmd.get()); if (pop.isok()){ FILE *fout = pop.getfout(); if (export_connect(pop,pass)!=-1 && export_send (pop,id.get())!=-1){ char buf[1000]; snprintf (buf,sizeof(buf)-1 ,MSG_U(I_EXPORTOK ,"The administration group %s\n" "has been successfully copied to machine\n" "%s.\n" "Do you want to install the various new config files\n" "immediatly and activate the changes ?") ,id.get(),member); if (dialog_yesno(MSG_U(T_EXPORTOK,"Export completed") ,buf ,help_export)==MENU_YES){ char import_cmd[PATH_MAX]; sprintf (import_cmd,"import %s",id.get()); if (export_sendcmd (pop,import_cmd)!=-1 && export_sendcmd (pop,"update")!=-1){ ret = 0; xconf_notice (MSG_U(N_UPDATED,"Station %s updated"),member); } }else{ ret = 0; } fprintf (fout,"quit\n"); } pop.close(); }else{ xconf_error (MSG_U(E_CANTEXEC,"Can't execute %s"),fullcmd.get()); } } return ret; } /* Set the list of transport commands for the export protocol. */ void export_setcommands (FIELD_COMBO *comb) { comb->addopt ("ssh",MSG_U(I_SSH,"Secure shell")); comb->addopt ("rlogin",MSG_U(I_RLOGIN,"Remote login (clear text)")); } /* Export all sub-system of an admin group to one member */ PUBLIC int CLUSTER::exportall(const char *defcmd) { int ret = -1; bool cmd_changed = false; for (int i=0; iget(); DIALOG dia; SSTRING pass; dia.newf_pass (MSG_U(F_ROOTPASS,"root password"),pass); SSTRING key; key.setfromf ("%s-%s",K_MEMBERCMD,id.get()); SSTRING cmd (linuxconf_getval(key.get(),m,defcmd)); SSTRING oldcmd (cmd); FIELD_COMBO *comb = dia.newf_combo (MSG_U(F_COMMAND,"Transport command") ,cmd); export_setcommands (comb); char do_debug=0; dia.newf_chk ("",do_debug,MSG_U(I_DEBUG,"Enable protocol debugging")); char buf[200]; snprintf (buf,sizeof(buf)-1 ,MSG_U(I_EXPORTINGTO,"Exporting to %s\n" "\n" "Please enter root password so linuxconf\n" "can connect to the netadm special account\n" "using ssh (secure shell)") ,m); if (dia.edit(m,buf,help_export)==MENU_ACCEPT){ debug = do_debug != 0; if (cmd.cmp(oldcmd)!=0){ if (cmd.cmp(defcmd)==0){ linuxconf_removeall (key.get(),m); }else{ linuxconf_replace (key.get(),m,cmd); } cmd_changed = true; } ret |= exportone (m,pass.get(),cmd.get()); } } linuxconf_save(); return ret; }