#pragma implementation #include #include #include #include #include #include #include #include #include "updatemon.h" #include "updatemon.m" #include #include #include #include #include #include static HELP_FILE help_updatewin ("updatemon","updatewin"); static HELP_FILE help_commandwin ("updatemon","commandwin"); static HELP_FILE help_updatemon ("updatemon","updatemon"); static CONFIG_FILE f_updated ("/var/log/filesupd.log",help_updatemon ,CONFIGF_OPTIONAL|CONFIGF_MANAGED|CONFIGF_FIXEDBASE|CONFIGF_NOARCH ,"root","root",0600); MODULE_DEFINE_VERSION(updatemon); static bool file_is_running = false; static bool cmd_is_running = false; static SSTRING logctx; static const char K_UPDATEMON[]="updatemon"; static const char K_LOGWIN[]="logwin"; static const char K_CMDWIN[]="cmdwin"; static const char K_DIFFOPT[]="diffopt"; static const char K_LENLOG[]="lenlog"; static PRIVATE_MESSAGE update_msg; class UPDFILE: public ARRAY_OBJ{ public: SSTRING file; SSTRING admin; // Who has performed the update time_t date; }; static ARRAY_OBJS files; static time_t last_revision; /* Read the list of updated configuration file and remove the old entries. Keep only the last 500 */ static void updatemon_readlog() { struct stat st; time_t revision = last_revision; if (stat(f_updated.getpath(),&st)!=-1) revision = st.st_mtime; if (files.getnb()==0 || revision != last_revision){ files.remove_all(); (f_updated.getpath(),true); UPDFILE *u = new UPDFILE; u->date = atoi(line); line = str_skipdig(line); line = u->admin.copyword(line); u->file.copyword(line); files.add (u); return 0; last_revision = revision; } } /* Update the filesupd.log. The files is always rewritten. Older entries are sometime removed from the start of the file this way */ static void updatemon_appendlog(const char *mon_file) { CONTEXT_LOCK lock ("updatemon_appendlog"); if (lock.isok(10)){ updatemon_readlog(); // In case another process has changed it UPDFILE *u = new UPDFILE; u->date = time(NULL); int uid = perm_getadminuid(); struct passwd *p = getpwuid(uid); u->admin.setfrom (p != NULL ? p->pw_name : "root"); u->file.setfrom(mon_file); files.add (u); /* #Specification: filesupd.log / cleanup Whenever the file reached 500 entries, the older entries are removed */ if (files.getnb()>500){ while (files.getnb() > 500){ files.remove_del(0); } } xconf_fopencfg_bypass (true); (f_updated,false); for (int i=0; idate,u->admin.get(),u->file.get()); } return 0; xconf_fopencfg_bypass (false); struct stat st; if (stat(f_updated.getpath(),&st)!=-1) last_revision = st.st_mtime; } } static void update_edittext ( const char *txt, // Text to edit const char *file) // File to update { SSTRING text; DIALOG tdia; tdia.setcontext (""); glocal.text.setfrom (txt); tdia.newf_textarea (NULL,glocal.text,80,25); int nof = 0; while (1){ MENU_STATUS code = tdia.edit (file,"",help_nil,nof); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else{ (file,false); fputs (glocal.text.get(),fout); return 0; break; } } } static void update_editfile(const char *file) { SSTRING text; (file,false); glocal.text.append (line); return 0; update_edittext (glocal.text.get(),file); } static void update_str2lines( const char *buf, SSTRINGS &lines) { char line[strlen(buf)+1]; char *pt = line; while (*buf != '\0'){ if (*buf == '\n'){ *pt = '\0'; lines.add (new SSTRING(line)); pt = line; }else{ *pt++ = *buf; } buf++; } if (pt != line){ *pt = '\0'; lines.add (new SSTRING(line)); } } static const char *updatemon_getdiffopt() { return linuxconf_getval (K_UPDATEMON,K_DIFFOPT,"-c"); } static void update_archivecmd ( const char *fname, const char *cmd, const char *opt); static void update_archivecmd ( const char *fname, const char *cmd, // Archive command const char *opt) { SSTRINGS lines; SSTRINGS revisions; const char *fname; glocal.fname = fname; SSTRINGS t; t.add (new SSTRING(fname)); SSTREAM_BUF ss; SSTRING tmp; tmp.setfromf ("%s --diffopt %s %s",opt,updatemon_getdiffopt(),cmd); if (perm_rootaccess(MSG_U(P_VIEWUPDATES,"view file updates")) && configf_archive (t,"cfgarchive",tmp.get(),ss,false) != -1){ update_str2lines (ss.getbuf(),glocal.lines); if (strcmp(cmd,"--hist")==0){ SSTRING title; title.setfromf (MSG_U(T_HISTORY,"History of file %s"),fname); (title.get(),"",help_nil); newf_head (MSG_U(H_HISTORY,"Revision\tDate\tHour")); settype (DIATYPE_POPUP); sortable (); sortpolicy ("aaa"); glocal.revisions.remove_all(); for (int i=0; iget(); SSTRINGS tb; int n = str_splitline (l,' ',tb); if (n == 3){ const char *rev = tb.getitem(0)->get(); glocal.revisions.add (new SSTRING(rev)); new_menuitemf (rev,"%s\t%s" ,tb.getitem(1)->get(),tb.getitem(2)->get()); } } const char *rev = glocal.revisions.getitem(no)->get(); DIALOG_MENUPOPUP dia; dia.new_menuitem (MSG_R(M_SHOW),MSG_R(M_SHOWDIFF)); dia.new_menuitem (MSG_R(M_EDIT),MSG_U(M_REVISION,"the revision")); int nof = 0; if (dia.editmenu ("",nof)==MENU_OK){ SSTRING opt; opt.setfromf ("--rcsopt \"-r%s\"",rev); if (nof == 0){ update_archivecmd (glocal.fname,"--diff",opt.get()); }else if (nof == 1){ update_archivecmd (glocal.fname,"--extr",opt.get()); } } }else if (strcmp(cmd,"--diff")==0){ if (glocal.lines.getnb()==0){ xconf_notice (MSG_U(N_SAME,"The archived copy is identical")); }else{ dialog_textbox (MSG_U(T_ARCHIVELOG,"Archive log"),glocal.lines); } }else if (strcmp(cmd,"--extr")==0){ const char *text = ss.getbuf(); // Skip the first line, which indicate if the file exist in the archive text = strchr(text,'\n'); if (text == NULL){ text = ss.getbuf(); }else{ text++; } update_edittext (text,fname); } } } static void update_fct(const char *gui_context); static void update_fct_null() { update_fct(NULL); } /* Put the pane at the bottom showing updated files or as an independent dialog. */ static void update_fct(const char *gui_context) { updatemon_readlog(); PRIVATE_MESSAGE timer; const char *gui_context; glocal.gui_context = gui_context; dialog_settimer (glocal.timer,3,true); (MSG_U(T_UPDATELOG,"File updates"),"",help_updatewin); newf_head (MSG_U(H_FILEPATH,"Date\tAdmin.\tUpdated configuration files")); setcontext (glocal.gui_context); waitfor (update_msg); waitfor (glocal.timer); int nb = files.getnb()-1; if (nb > 0) setcursor (nb-1); sortable(); sortpolicy("aaa"); int nb = files.getnb(); for (int i=0; idate); char tmp[20]; sprintf (tmp,"%04d/%02d/%02d %02d:%02d:%02d" ,t->tm_year+1900,t->tm_mon+1,t->tm_mday ,t->tm_hour,t->tm_min,t->tm_sec); new_menuitemf (tmp,"%-10s\t%-59s" ,u->admin.get(),u->file.get()); } char empty[20]; memset (empty,' ',20); empty[19] = '\0'; for (int i=nb; i<4; i++){ new_menuitemf (empty,"%10s\t%59s","",""); } if (no < files.getnb()){ DIALOG_MENUPOPUP dia; dia.new_menuitem (MSG_U(M_SHOW,"Show"),MSG_U(M_UPDHISTORY,"updates history")); dia.new_menuitem (MSG_R(M_SHOW),MSG_U(M_SHOWDIFF,"differences")); dia.new_menuitem (MSG_U(M_EXTRACT,"Extract"),MSG_U(M_LASTVERSION,"last version")); dia.new_menuitem (MSG_U(M_EDIT,"Edit"),MSG_U(M_THEFILE,"the file")); int nof = 0; if (dia.editmenu ("",nof)==MENU_OK){ if (perm_rootaccess(MSG_R(P_VIEWUPDATES))){ const char *fname = files.getitem(no)->file.get(); if (nof == 0){ update_archivecmd (fname,"--hist",""); }else if (nof == 1){ update_archivecmd (fname,"--diff",""); }else if (nof == 2){ SSTRINGS t; t.add (new SSTRING(fname)); configf_extract ("/",t,"cfgarchive","--extr"); }else if (nof == 3){ update_editfile (fname); } } } } setvsize (glocal.gui_context != NULL ? 4 : 25); newf_clist(); gui_passthrough (P_Form,"form $hexpand=0"); new_button_icon (1,"xquit",""); if (glocal.gui_context != NULL){ newline(); new_button_icon (2,"xbox",""); } newline(); new_button_help (); newline(); gui_passthrough (P_End,""); nobutton(); if (id == 1){ endedit(); }else if (id == 2){ uithread (update_fct_null); } if (dialog_testmessage(glocal.timer)){ updatemon_readlog(); }else if (dialog_testmessage(update_msg)){ setcursor (files.getnb()-1); } dialog_deltimer (glocal.timer); } /* Put the pane at the bottom showing updated files */ static void update_fct() { file_is_running = true; update_fct (logctx.get()); file_is_running = false; } class COMMANDITEM: public ARRAY_OBJ{ public: SSTRING line; time_t date; COMMANDITEM(const char *_line){ line.setfrom(_line); line.strip_end(); date = time(NULL); } }; static ARRAY_OBJS commands; static PRIVATE_MESSAGE command_msg; /* Put the pane at the bottom showing the command log */ static void command_fct() { cmd_is_running = true; (MSG_U(T_COMMANDLOG,"Actions"),"",help_commandwin); newf_head (MSG_U(H_COMMANDS,"Date\tActions")); setcontext (logctx.get()); waitfor (command_msg); sortable(); sortpolicy("aa"); int nb = commands.getnb(); for (int i=0; idate); char tmp[20]; sprintf (tmp,"%02d:%02d:%02d" ,t->tm_hour,t->tm_min,t->tm_sec); new_menuitemf (tmp,"%-79.79s",c->line.get()); } // Make sure we have 4 lines in the pane for (int i=nb; i<4; i++){ new_menuitemf (" ","%79s","",""); } setcursor (nb == 0 ? 0 : nb - 1); setvsize (4); newf_clist(); gui_passthrough (P_Form,"form $hexpand=0"); new_button_icon (1,"xquit",""); newline(); new_button_help (); newline(); gui_passthrough (P_End,""); nobutton(); if (id == 1){ endedit(); } cmd_is_running = false; } static int update_win (bool force) { int ret = -1; if (force || linuxconf_getvalnum (K_UPDATEMON,K_LOGWIN,1)!=0){ if (!file_is_running){ uithread (update_fct); } ret = 0; } return ret; } static int command_win (bool force) { int ret = -1; if (force || linuxconf_getvalnum (K_UPDATEMON,K_CMDWIN,1)!=0){ if (!cmd_is_running){ uithread (command_fct); } ret = 0; } return ret; } static bool mon_running = false; static bool mon_file_accepted = false; // The admin accepted the update static const char *mon_file=NULL,*mon_file_new=NULL; static PRIVATE_MESSAGE mon_file_msg; static PRIVATE_MESSAGE mon_ret_msg; static void update_title ( DIALOG &dia, const char *dc) { SSTRING tmp; char tmp1[1000]; diagui_sendcmd (P_Setval,"%s.panel idupd %s $dc=%s\n" ,dia.setguiname(tmp) ,diagui_quote (MSG_R(T_UPDATING),tmp1) ,dc); } static void update_monitor () { SSTRING diff; mon_running = true; DIALOG dia; dia.setcontext (""); PRIVATE_MESSAGE msgsignal; dia.waitfor (msgsignal); int nbsignal=0; static const char *dcblack=NULL; static const char *dcblue=NULL; if (dcblue == NULL){ const char *font = guiid_setfont (24,GFONT_ID_MODERN,GFONT_STYLE_DEFAULT,GFONT_WEIGHT_BOLD,false); const char *pen = guiid_setpen ("blue"); const char *penblack = guiid_setpen ("black"); const char *brush = guiid_setbrush ("blue"); dcblue = guiid_setdc (font,pen,brush); dcblack = guiid_setdc (font,penblack,brush); } { char tmp[1000]; dia.gui_passthrough (P_Label,"%s $dc=%s $id=idupd\n" ,diagui_quote (MSG_U(T_UPDATING,"Updating one file"),tmp) ,dcblack); dia.gui_dispolast (GUI_H_CENTER,3,GUI_V_TOP,1); } dia.newline(); SSTRING file,newfile; dia.newf_str (MSG_U(F_FILE,"Configuration file"),file,60); dia.set_lastreadonly(); dia.newline(); dia.newf_str (MSG_U(F_NEWFILE,"Proposed update"),newfile,60); dia.set_lastreadonly(); PRIVATE_MESSAGE msgedit; dia.new_button (MSG_U(B_EDIT,"Edit"),MSG_U(I_EDIT,"Edit the update"),msgedit); dia.newline(); dia.gui_passthrough (P_Label,"\"%s\"",MSG_U(F_DIFF,"Differences")); dia.newline(); dia.newf_textarea (NULL,glocal.diff,80,10); dia.gui_dispolast (GUI_H_LEFT,3,GUI_V_TOP,1); dia.newline(); int nof = 0; bool done = true; dia.waitfor (mon_file_msg); dia.setbutinfo (MENU_USR1,MSG_U(B_ACCEPT,"Accept changes"),MSG_R(B_ACCEPT)); dia.setbutinfo (MENU_USR2,MSG_U(B_REJECT,"Reject update"),MSG_R(B_REJECT)); while (1){ MENU_STATUS code = dia.edit (MSG_U(T_UPDATEMON,"Updates monitor") ,MSG_U(I_UPDATEMON,"Each configuration file modified by linuxconf\n" "is presented here. You are allowed to accept or reject\n" "the change") ,help_updatemon ,nof,MENUBUT_CANCEL|MENUBUT_USR1|MENUBUT_USR2); if (code == MENU_ESCAPE || code == MENU_CANCEL){ mon_file_accepted = false; dialog_sendmessage (mon_ret_msg); break; }else if (code == MENU_MESSAGE){ if (dialog_testmessage(msgedit)){ if (done){ xconf_notice (MSG_R(N_ALREADY)); }else{ update_editfile (newfile.get()); } }else if (dialog_testmessage(mon_file_msg)){ file.setfrom (mon_file); newfile.setfrom (mon_file_new); glocal.diff.setfrom (""); SSTRING opt; opt.setfromf ("%s %s %s",updatemon_getdiffopt() ,mon_file,mon_file_new); ("diff",opt.get(),10); glocal.diff.appendf ("%s\n",line); return 0; update_title (dia,dcblue); dia.reload(); done = false; nbsignal = 9; dialog_settimer (msgsignal,1,true); }else if (dialog_testmessage(msgsignal)){ nbsignal--; if (nbsignal == 0){ dialog_deltimer (msgsignal); }else{ update_title (dia,(nbsignal & 1) ? dcblue : dcblack); } } }else if (done){ xconf_notice (MSG_U(N_ALREADY,"File update already confirmed/rejected")); }else{ dialog_deltimer (msgsignal); done = true; if (code == MENU_USR1){ mon_file_accepted = true; dialog_sendmessage (mon_ret_msg); }else if (code == MENU_USR2){ mon_file_accepted = false; dialog_sendmessage (mon_ret_msg); } update_title (dia,dcblack); } } mon_running = false; } static const char *keymenu=NULL; static const char *keyupdate=NULL; ("updatemon",PACKAGE_REV); tb.add (new SSTRING(MSG_U(T_USAGE ,"linuxconf --modulemain updatemon usage\n" "\n" " updatemon --option ...\n"))); if (context == MENU_LOGS){ keymenu = MSG_U(M_updatemon,"File updates history"); keyupdate = MSG_R(M_INTERACTIVE); dia.new_menuitem ("updatemon","",keymenu); dia.new_menuitem ("updatemon","",keyupdate); } if (context == MENU_LOGS){ if (key == keymenu){ update_win (true); }else if (key == keyupdate){ if (!mon_running) uithread (update_monitor); } } return 0; int ret = LNCF_NOT_APPLICABLE; extern CONFIG_FILE f_treemenu; // No need to talk about treemenu.cache if (strcmp(msg,"updatefile")==0 && strcmp(argv[0],f_treemenu.getpath())!=0 && strcmp(argv[0],f_updated.getpath())!=0){ ret = 0; mon_file = argv[0]; mon_file_new = argv[1]; if (mon_running){ dialog_sendmessage (mon_file_msg); dialog_waitformessage (mon_ret_msg); ret = mon_file_accepted ? 0 : -1; } if (ret == 0){ updatemon_appendlog(mon_file); dialog_sendmessage (update_msg); } }else if (strcmp(msg,"netconf.log")==0){ SSTRINGS tb; int n = str_cnv2lines (argv[0],tb); for (int i=0; iget())); } dialog_sendmessage (command_msg); }else if (strcmp(msg,"tree_is_up")==0){ logctx.setfrom (argv[1]); if (update_win(false) != -1) ret = 0; if (command_win(false) != -1) ret = 0; }else if (strcmp(msg,"build-menubar")==0){ ret = 0; diagui_sendcmd (P_Submenu,"\"%s\"\n",MSG_U(M_UPDLOG,"Updates")); diagui_sendcmd (P_Menuentry,"200 \"%s\"\n",MSG_U(M_LOGWIN,"File updates log")); diagui_sendcmd (P_Menuentry,"201 \"%s\"\n",MSG_U(M_INTERACTIVE,"Interactive updates")); diagui_sendcmd (P_Menuentry,"202 \"%s\"\n",MSG_U(M_CMDWIN,"Linuxconf actions log")); diagui_sendcmd (P_End,"\n"); }else if (strcmp(msg,"build-helpmenu")==0){ diagui_sendcmd (P_Submenu,"\"%s\"\n",MSG_R(M_UPDLOG)); diagui_sendcmd (P_Menuentry,"210 \"%s\"\n",MSG_R(M_LOGWIN)); diagui_sendcmd (P_Menuentry,"211 \"%s\"\n",MSG_R(M_INTERACTIVE)); diagui_sendcmd (P_Menuentry,"212 \"%s\"\n",MSG_R(M_CMDWIN)); diagui_sendcmd (P_End,"\n"); }else if (strcmp(msg,"menubar")==0){ ret = 0; int sel = atoi(argv[0]); if (sel == 200){ update_win (true); }else if (sel == 201){ if (!mon_running) uithread (update_monitor); }else if (sel == 202){ command_win(true); }else if (sel == 210){ diagui_showhelp(help_updatewin); }else if (sel == 211){ diagui_showhelp(help_updatemon); }else if (sel == 212){ diagui_showhelp(help_commandwin); } } return ret; int ret = LNCF_NOT_APPLICABLE; #if 0 // ### Check the variable key to provide your own html hook ret = 0; #endif return ret; int ret = -1; if (argc == 1){ // ### Place here a call to the main menu }else{ printusage(); } return ret; class UPDATEMON_COMNG: public USERACCT_COMNG{ char logwin; char cmdwin; SSTRING diffopt; int lenlog; // Number of lines in the log file /*~PROTOBEG~ UPDATEMON_COMNG */ public: UPDATEMON_COMNG (DICTIONARY&_dict); int save (PRIVILEGE *priv); void setupdia (DIALOG&dia); int validate (DIALOG&, int &nof); /*~PROTOEND~ UPDATEMON_COMNG */ }; PUBLIC UPDATEMON_COMNG::UPDATEMON_COMNG( DICTIONARY &_dict) : USERACCT_COMNG (_dict) { logwin = linuxconf_getvalnum (K_UPDATEMON,K_LOGWIN,1); cmdwin = linuxconf_getvalnum (K_UPDATEMON,K_CMDWIN,1); diffopt.setfrom (linuxconf_getval (K_UPDATEMON,K_DIFFOPT,"-c")); lenlog = linuxconf_getvalnum (K_UPDATEMON,K_LENLOG,500); } PUBLIC void UPDATEMON_COMNG::setupdia ( DIALOG &dia) { dia.newf_title (MSG_U(T_MODUPDATEMON,"Module updatemon"),1,"",MSG_R(T_MODUPDATEMON)); dia.newf_chk ("",logwin,MSG_U(I_LOGWIN,"Enable the file updates window")); dia.newf_chk ("",cmdwin,MSG_U(I_CMDWIN,"Enable the actions window")); dia.newf_str (MSG_U(F_DIFFOPT,"Diff command options"),diffopt); dia.last_noempty(); dia.newf_num (MSG_U(F_LENLOG,"filesupd.log length"),lenlog); dia.addhelp (help_updatewin,MSG_R(T_MODUPDATEMON)); } PUBLIC int UPDATEMON_COMNG::save( PRIVILEGE *priv) { linuxconf_replace (K_UPDATEMON,K_LOGWIN,logwin); linuxconf_replace (K_UPDATEMON,K_CMDWIN,cmdwin); linuxconf_replace (K_UPDATEMON,K_DIFFOPT,diffopt); linuxconf_replace (K_UPDATEMON,K_LENLOG,lenlog); return 0; } PUBLIC int UPDATEMON_COMNG::validate( DIALOG &, int &nof) { return 0; } static USERACCT_COMNG *UPDATEMON_newcomng( const char *key, DICTIONARY &dict) { USERACCT_COMNG *ret = NULL; if (strcmp(key,"features")==0){ ret = new UPDATEMON_COMNG (dict); } return ret; } static REGISTER_USERACCT_COMNG ppp (UPDATEMON_newcomng);