#include #include #include #include #include #include "syslogconf.h" #include "syslogconf.m" #include #include #include #include #include #include #include static HELP_FILE help_viewlog ("syslogconf","viewlog"); static HELP_FILE help_filter ("syslogconf","filter"); static MESSAGE_DEF msg_treemenu_guirunning ("treemenu_guirunning"); class VIEWLOG_API{ int owners; // How many threads are using this object public: virtual int getline (int no, SSTRING &line)=0; VIEWLOG_API(); virtual ~VIEWLOG_API(); bool may_delete() const; void raiseowner(); void lowerowner(); }; VIEWLOG_API::VIEWLOG_API() { owners = 0; } VIEWLOG_API::~VIEWLOG_API() { } void VIEWLOG_API::raiseowner() { owners++; } void VIEWLOG_API::lowerowner() { owners--; } bool VIEWLOG_API::may_delete() const { return owners == 0; } #define BUF_SIZE 4096 class VIEWLOG: public VIEWLOG_API{ FILE *fin; long size; int firstline; // Line number of the first line in lines long firstoffset; // Offset of the first line in lines long endoffset; // Offset of the last line in lines (end of last line) SSTRINGS lines; int fillbackward(); int fillforward(); public: VIEWLOG(const char *fname); virtual ~VIEWLOG(); int getline (int no, SSTRING &line); bool is_ok(); }; VIEWLOG::VIEWLOG(const char *fname) { fin = xconf_fopen (fname,"r"); size = 0; if (fseek(fin,0,SEEK_END)!=-1){ size = ftell (fin); } firstline = 0; firstoffset = size; endoffset = size; } VIEWLOG::~VIEWLOG() { if (fin != NULL) fclose (fin); } bool VIEWLOG::is_ok() { return fin != NULL; } /* Refill the lines buffer before the ones in the cache */ int VIEWLOG::fillbackward () { lines.remove_all(); long reach = firstoffset; long backup = reach - BUF_SIZE; if (backup < 0) backup = 0; if (fseek(fin,backup,SEEK_SET)!=-1){ bool discard = true; while (1){ long pos = ftell(fin); if (pos >= reach) break; char line[2000]; if (fgets(line,sizeof(line)-1,fin)==NULL) break; if (discard){ // We discard the first line because we are not sure // we were really at the start discard = false; firstoffset = ftell(fin); }else{ lines.add (new SSTRING(line)); } } endoffset = ftell(fin); firstline -= lines.getnb(); } return lines.getnb()==0 ? -1 : 0; } /* Refill the buffer after the ones in the cache */ int VIEWLOG::fillforward () { firstline += lines.getnb(); lines.remove_all(); long reach = endoffset+BUF_SIZE; if (fseek(fin,endoffset,SEEK_SET)!=-1){ firstoffset = endoffset; while (1){ long pos = ftell(fin); if (pos >= reach) break; char line[2000]; if (fgets(line,sizeof(line)-1,fin)==NULL) break; lines.add (new SSTRING(line)); } endoffset = ftell(fin); } return lines.getnb()==0 ? -1 : 0; } /* Extract a line relative to the end. If no == 0, get the last line if no == 1, get the line before. Return -1 if the line is not available */ int VIEWLOG::getline (int no, SSTRING &line) { int ret = -1; if (no < firstline){ while (no < firstline){ if (fillbackward()==-1) break; } }else if (no >= firstline + lines.getnb()){ while (no >= firstline + lines.getnb()){ if (fillforward() == -1) break; } } if (no >= firstline && no < firstline + lines.getnb()){ SSTRING *s = lines.getitem(no - firstline); line.setfrom(s->get()); line.strip_end(); ret = 0; } return ret; } class SYSLOG_PARSE{ public: char date[20]; char host[100]; char prog[100]; char pid[20]; char msg[200]; SYSLOG_PARSE(const char *s); }; SYSLOG_PARSE::SYSLOG_PARSE (const char *s) { memcpy (date,s,15); date[15] = '\0'; s += 15; s = str_skip(s); s = str_copyword (host,s,100); s = str_copyword (prog,s,100); s = str_skip(s); pid[0] = '\0'; char *pt = strchr(prog,'['); if (pt != NULL){ *pt++ = '\0'; char *dst = pid; while (isdigit(*pt) && (unsigned)(dst-pid)raiseowner(); index[0] = 0; firstline = 0; start_date = end_date = 0; pid = 0; show_child = 0; } VIEWLOG_FILTER::~VIEWLOG_FILTER() { vlog->lowerowner(); if (vlog->may_delete()) delete vlog; } bool VIEWLOG_FILTER::linematch (SSTRING &line) { SYSLOG_PARSE p(line.get()); return (progs.getnb()==0 || progs.lookup (p.prog)!=-1) && (pid == 0 || pid == atoi(p.pid)) && (hosts.getnb()==0 || hosts.lookup(p.host)!=-1) && (search.is_empty() || strstr(p.msg,search.get())) ; } int VIEWLOG_FILTER::fillbackward() { int nbfound=0; for (int i=index[0]-1; nbfound<30; i--){ SSTRING line; if (vlog->getline(i,line)!=-1){ if (linematch (line)){ lines.insert (0,new SSTRING(line)); memmove (index+1,index,sizeof(index)-sizeof(index[0])); index[0] = i; nbfound++; firstline--; } }else{ break; } } while (lines.getnb() > 60) lines.remove_del (lines.getnb()-1); return nbfound > 0 ? 0 : -1; } int VIEWLOG_FILTER::fillforward() { int last = lines.getnb()==0 ? 0 : index[lines.getnb()-1]+1; int nbfound = 0; for (int i=last; nbfound<30; i++){ SSTRING line; if (vlog->getline(i,line)!=-1){ if (linematch(line)){ index[lines.getnb()] = i; lines.add (new SSTRING(line)); nbfound++; } }else{ break; } } while (lines.getnb() > 60){ memmove (index,index+1,sizeof(index)-sizeof(index[0])); lines.remove_del (0); firstline++; } return nbfound > 0 ? 0 : -1; } /* Extract a line relative to the end. If no == 0, get the last line if no == 1, get the line before. Return -1 if the line is not available */ int VIEWLOG_FILTER::getline (int no, SSTRING &line) { int ret = -1; if (no < firstline){ while (no < firstline){ if (fillbackward()==-1) break; } }else if (no >= firstline + lines.getnb()){ while (no >= firstline + lines.getnb()){ if (fillforward() == -1) break; } } if (no >= firstline && no < firstline + lines.getnb()){ SSTRING *s = lines.getitem(no - firstline); line.setfrom(s->get()); line.strip_end(); ret = 0; } return ret; } int VIEWLOG_FILTER::editfilter() { int ret = -1; DIALOG dia; dia.settype (DIATYPE_POPUP); SSTRING hoststr,progstr,pidstr,start_datestr,end_datestr; for (int i=0; iget()); } for (int i=0; iget()); } if (pid != 0) pidstr.setfrom (pid); dia.newf_str (MSG_U(F_HOSTS,"Hosts to track"),hoststr); dia.newf_str (MSG_U(F_PROGS,"Programs to track"),progstr); int usepid = 1; int str_vals[] = {0,1}; const char *titles[] = {MSG_U(I_ANYPID,"Ignore process ID"),NULL}; dia.newf_chkm_str (MSG_U(F_PID,"Process ID to track"),usepid,pidstr ,str_vals,titles); dia.newf_chk ("",show_child,MSG_U(I_SHOWCHILD,"Show child processes")); dia.newf_str (MSG_U(F_STARTDATE,"Start date"),start_datestr); dia.newf_str (MSG_U(F_ENDDATE,"End date"),end_datestr); dia.newf_str (MSG_U(F_SEARCH,"Search string"),search); dia.setbutinfo (MENU_USR1,MSG_U(B_CLEAR,"Clear"),MSG_R(B_CLEAR)); int nof = 0; while (1){ MENU_STATUS code = dia.edit (MSG_U(T_VIEWLOGFILTER,"System log filter") ,MSG_U(I_VIEWLOGFILTER ,"You can view a subset of the system log\n" "by filling this screen.\n" "All field are optionals.") ,help_filter ,nof,MENUBUT_CANCEL|MENUBUT_ACCEPT|MENUBUT_USR1); if (code == MENU_CANCEL || code == MENU_ESCAPE){ dia.restore(); break; }else if (code == MENU_USR1){ hoststr.setfrom(""); progstr.setfrom(""); usepid = 0; start_datestr.setfrom(""); end_datestr.setfrom(""); search.setfrom (""); show_child = 0; dia.reload(); }else if (code == MENU_ACCEPT){ progs.remove_all(); hosts.remove_all(); pid = 0; start_date = end_date = 0; if (usepid > 0 && !pidstr.is_empty()) pid = pidstr.getval(); str_splitline (progstr.get(),' ',progs); str_splitline (hoststr.get(),' ',hosts); ret = 0; break; } } return ret; } void VIEWLOG_FILTER::addprog(const char *p) { progs.add (new SSTRING(p)); } void VIEWLOG_FILTER::setpid(int _pid) { pid = _pid; } // Information passed to the new thread struct VIEWLOG_PARMS{ int noline; VIEWLOG_API *api; PRIVILEGE *priv; SSTRING title; VIEWLOG_PARMS (int _noline, VIEWLOG_API *_api, PRIVILEGE *_priv, const char *_title); }; VIEWLOG_PARMS::VIEWLOG_PARMS ( int _noline, VIEWLOG_API *_api, PRIVILEGE *_priv, const char *_title) { noline = _noline; api = _api; priv = _priv; title.setfrom (_title); } static void syslog_fct(void *p) { VIEWLOG_PARMS *parms = (VIEWLOG_PARMS*)p; SSTRING line; if (parms->api->getline (parms->noline,line)!=-1){ VIEWLOG_FILTER *vlogf = new VIEWLOG_FILTER (parms->api); SYSLOG_PARSE p(line.get()); vlogf->addprog (p.prog); vlogf->setpid (atoi(p.pid)); if (vlogf->editfilter()!=-1){ parms->title.append ("-"); syslog_viewone (parms->title.get(),vlogf,NULL); } } delete parms; } /* Locate the path of the binary associated with this process ID */ static int syslog_pid2path (int pid, char binpath[PATH_MAX]) { int ret = -1; char path[PATH_MAX]; sprintf (path,"/proc/%d/exe",pid); int nb = readlink(path,binpath,PATH_MAX); if (nb == -1){ if (kill(pid,0)==-1){ xconf_notice (MSG_U(M_PROGDEAD ,"Process %d is not running anymore\n" "Can't locate package information"),pid); }else{ xconf_error (MSG_U(E_NOPROGPATH ,"Odd, can't locate the program path for process id %d") ,pid); } }else{ binpath[nb] = '\0'; ret = 0; } return ret; } /* Show information about the package associated with the process ID */ static void syslog_pkginfo (int pid) { char binpath[PATH_MAX]; if (syslog_pid2path(pid,binpath)!=-1){ PACKAGE_API *pkg_api = package_api_init ("syslogconf"); if (pkg_api != NULL){ PACKAGE_VERSION ver; SSTRING pkg; if (pkg_api->path2pkg (pkg_api,binpath,pkg,ver) == -1){ xconf_error (MSG_U(E_NOPACKAGE ,"There is no package associated with program %s.\n" "It may have been installed without the package manager") ,binpath); }else{ pkg_api->showinfo (pkg_api,pkg.get()); } package_api_end (pkg_api); } } } /* Present a control panel to start/stop/disable a service */ static void syslog_service (int pid) { char binpath[PATH_MAX]; if (syslog_pid2path(pid,binpath)!=-1){ SERVICECTL_API *tb[MAX_API_PROVIDERS]; int n = servicectl_apis_init ("syslogconf",tb); int i; for (i=0; icontrol(binpath)!=-1) break; } if (i==n){ xconf_error (MSG_U(E_NOSERVICE ,"Can't identify the service associated with the program\n" "\t%s") ,binpath); } } } static void syslog_service_thread (void *p) { int pid = (int)p; syslog_service (pid); } /* Position to the next page of the log */ static int syslog_gotoend ( VIEWLOG_API *vlog, int noline, // Current line int skip) // Number of line per page { int nextline = noline+skip; while (1){ SSTRING s; vlog->getline (nextline,s); if (s.is_empty()){ noline = nextline-skip; break; } nextline++; } return noline; } void syslog_viewone (const char *title, VIEWLOG_API *vlog, PRIVILEGE *priv) { int noline; // Line number relative to the end of the file VIEWLOG_API *vlog; int skip; PRIVATE_MESSAGE msg; PRIVILEGE *priv; const char *title; char locked; bool guiframework; // We are running the in treemenu framework // not as a standalone window if (!perm_access (priv,MSG_U(P_VIEWLOGS,"view log files"))) return; glocal.guiframework = dialog_mode == DIALOG_GUI && diajava_context && module_sendmessage(msg_treemenu_guirunning)==0; glocal.skip = glocal.guiframework ? 5 : (dialog_mode == DIALOG_HTML ? 20 : 15); glocal.noline = -glocal.skip; glocal.priv = priv; glocal.title = title; glocal.locked = 0; dialog_settimer (glocal.msg,1,true); glocal.vlog = vlog; vlog->raiseowner(); (title,"",help_viewlog); newf_clist(); gui_passthrough (P_Form,"form $hexpand=0"); new_button_icon (1,"xquit",""); newline(); new_button_icon (3,"uparrow",""); newline(); new_button_icon (2,"run",""); newline(); new_button_icon (4,"downarrow",""); newline(); new_button_help (); newline(); gui_passthrough (P_End,""); nobutton(); newf_head (MSG_U(H_VIEWLOG,"Date\tHost\tProcess\tPid\tDetails")); if (dialog_mode != DIALOG_GUI){ setbutinfo (MENU_USR1,MSG_U(B_PGUP,"Page up"),MSG_R(B_PGUP)); setbutinfo (MENU_USR2,MSG_U(B_PGDN,"Page down"),MSG_R(B_PGDN)); }else{ if (glocal.guiframework) setcontext ("tree-0.logs"); } waitfor (glocal.msg); // sortable(); // sortpolicy ("aaana"); sethdispo ("lllrl"); const char *cut = html_getcutinfo(); if (cut != NULL) glocal.noline = atoi(cut); for (int i=0; igetline (glocal.noline+i,s); if (s.is_empty()){ new_menuitem ("",""); }else{ char buf1[100],buf2[200]; syslog_format (s.get(),buf1,buf2); new_menuitem (buf1,buf2); } } SSTRING line; glocal.vlog->getline (glocal.noline+no,line); SYSLOG_PARSE p(line.get()); DIALOG_MENUPOPUP dia; static const char *filter = MSG_U(M_FILTER,"Filter view"); static const char *sendkill = MSG_U(M_KILL,"Kill process using signal"); static const char *pkginfo = MSG_U(M_PKGINFO,"Package information"); static const char *service = MSG_U(M_SERVICE,"Control service"); dia.new_menuitem ("",filter); int opt_signal = 0; if (p.pid[0] != '\0'){ if (package_api_available("syslogconf")){ dia.new_menuitem("",pkginfo); if (servicectl_api_available("syslogconf")){ dia.new_menuitem("",service); } } dia.newf_title (MSG_U(I_KILL,"kill"),1,"",sendkill); opt_signal = dia.getnb(); for (int i=1; i<32; i++){ static const char *tbsig[]={ MSG_U(I_SIGHANGUP,"hangup HUP"), MSG_U(I_SIGINT,"interrupt INT"), MSG_U(I_SIGQUIT,"quit QUIT"), MSG_U(I_SIGILL,"illegal instruction ILL"), MSG_U(I_SIGTRAP,"trap TRAP"), MSG_U(I_SIGABRT,"abort ABRT"), MSG_U(I_SIGBUS,"bus error BUS"), MSG_U(I_SIGFPE,"floating point exception FPE"), MSG_U(I_SIGKILL,"kill KILL"), MSG_U(I_SIGUSR1,"user defined USR1"), MSG_U(I_SIGSEGV,"segmentation fault SEGV"), MSG_U(I_SIGUSR2,"user defined USR2"), MSG_U(I_SIGPIPE,"pipe error PIPE"), MSG_U(I_SIGALRM,"alarm ALRM"), MSG_U(I_SIGTERM,"terminate TERM"), MSG_U(I_SIGSTKFLT,"stack fault STKFLT"), MSG_U(I_SIGCHLD,"child process death CHLD"), MSG_U(I_SIGCONT,"continue CONT"), MSG_U(I_SIGSTOP,"stop STOP"), MSG_U(I_SIGTSTP,"TSTP"), MSG_U(I_SIGTTIN,"TTIN"), MSG_U(I_SIGTTOU,"TTOU"), MSG_U(I_SIGURG,"urgent URG"), MSG_U(I_SIGXCPU,"XCPU"), MSG_U(I_SIGXFSZ,"XFSZ"), MSG_U(I_SIGVTALRM,"VTALRM"), MSG_U(I_SIGPROG,"PROF"), MSG_U(I_SIGWINCH,"window size changed WINCH"), MSG_U(I_SIGIO,"input/output failure IO"), MSG_U(I_SIGPWR,"power failue PWR"), MSG_U(I_SIGUNUSED,"UNUSED"), }; dia.new_menuitem("",tbsig[i-1]); } } int sel = 0; if (dia.editmenu ("",sel)==MENU_OK){ const char *str = dia.getmenustr(sel); int pid = atoi(p.pid); if (str == filter){ VIEWLOG_PARMS *p = new VIEWLOG_PARMS(glocal.noline + no ,glocal.vlog,glocal.priv,glocal.title); uithread (syslog_fct,p); }else if (str == pkginfo){ syslog_pkginfo (pid); }else if (str == service){ uithread (syslog_service_thread,(void*)pid); }else if (sel >= opt_signal){ if (kill(pid,0)==-1){ xconf_notice (MSG_U(M_PROGISDEAD ,"Process %d is not running anymore\n"),pid); }else{ int signum = sel-opt_signal+1; net_introlog (NETINTRO_MISC); net_prtlog (NETLOG_CMD,MSG_U(I_KILLING ,"Killing program %s (pid %d) with signal %d\n") ,p.prog,pid,signum); kill (pid,signum); } } } if (code == MENU_USR1 || code == MENU_USR2){ glocal.noline += (code == MENU_USR1) ? -glocal.skip : glocal.skip; }else if (id == 1){ endedit(); }else if (id == 2){ glocal.locked = !glocal.locked; set_button_icon (2,glocal.locked ? "stop" : "run"); if (!glocal.locked){ glocal.noline = syslog_gotoend(glocal.vlog,glocal.noline ,glocal.skip); } }else if (id == 3){ glocal.noline -= glocal.skip; glocal.locked = true; set_button_icon (2,"stop"); }else if (id == 4){ glocal.noline += glocal.skip; } char cutstr[20]; snprintf (cutstr,19,"%d",glocal.noline); html_setcutinfo (cutstr); if (!glocal.locked){ glocal.noline = syslog_gotoend(glocal.vlog,glocal.noline ,glocal.skip); } dialog_deltimer (glocal.msg); vlog->lowerowner(); if (vlog->may_delete()) delete vlog; } void syslog_viewone (const char *file, PRIVILEGE *priv) { VIEWLOG *vlog = new VIEWLOG (file); if (vlog->is_ok()){ syslog_viewone (file,vlog,priv); }else{ delete vlog; } } void syslog_menu() { SYSLOGCONF conf; conf.sort(); const char *menuopt[conf.getnb()*2+1]; int nb=0; for (int i=0; iis_file()){ menuopt[nb*2] = ""; menuopt[nb*2+1] = f->path.get(); nb++; } } menuopt[nb*2] = NULL; (MSG_U(T_SYSLOGFILES,"System log files") ,MSG_U(I_SYSLOGFILES ,"You can review the various system log files") ,help_nil,menuopt); SSTRING tmp(key); syslog_viewone (tmp.get(),NULL); }