diff --git a/cmd/termshark/termshark.go b/cmd/termshark/termshark.go index c4b0e60..a0f2695 100644 --- a/cmd/termshark/termshark.go +++ b/cmd/termshark/termshark.go @@ -921,11 +921,16 @@ func cmain() int { var progCancelTimer *time.Timer + checkedPcapCache := false + checkPcapCacheDuration := 5 * time.Second + checkPcapCacheTimer := time.NewTimer(checkPcapCacheDuration) + Loop: for { var finChan <-chan time.Time var tickChan <-chan time.Time var inactivityChan <-chan time.Time + var checkPcapCacheChan <-chan time.Time var tcellEvents <-chan tcell.Event var opsChan <-chan gowid.RunFunction var afterRenderEvents <-chan gowid.IAfterRenderEvent @@ -992,6 +997,10 @@ Loop: inactivityChan = inactivityTimer.C } + if !checkedPcapCache { + checkPcapCacheChan = checkPcapCacheTimer.C + } + // Only process tcell and gowid events if the UI is running. if ui.Running { tcellEvents = app.TCellEvents @@ -1017,6 +1026,16 @@ Loop: select { + case <-checkPcapCacheChan: + // Only check the cache dir if we own it; don't want to delete pcap files + // that might be shared with wireshark + if termshark.ConfBool("main.use-tshark-temp-for-pcap-cache", false) { + log.Infof("Termshark does not own the pcap temp dir %s; skipping size check", termshark.PcapDir()) + } else { + termshark.PrunePcapCache() + } + checkedPcapCache = true + case <-inactivityChan: ui.Fin.Activate() app.Redraw() @@ -1215,6 +1234,7 @@ Loop: case ev := <-tcellEvents: app.HandleTCellEvent(ev, gowid.IgnoreUnhandledInput) inactivityTimer.Reset(inactiveDuration) + checkPcapCacheTimer.Reset(checkPcapCacheDuration) case ev, ok := <-afterRenderEvents: // This means app.Quit() has been called, which closes the AfterRenderEvents diff --git a/utils.go b/utils.go index 3b774d3..aea10a4 100644 --- a/utils.go +++ b/utils.go @@ -22,6 +22,7 @@ import ( "path/filepath" "regexp" "runtime" + "sort" "strconv" "strings" "sync" @@ -827,6 +828,69 @@ var _ table.ICompare = IPCompare{} //====================================================================== +func PrunePcapCache() error { + // This is a new option. Best to err on the side of caution and, if not, present + // assume the cache can grow indefinitely - in case users are now relying on this + // to keep old pcaps around. I don't want to delete any files without the user's + // explicit permission. + var diskCacheSize int64 = int64(ConfInt("main.disk-cache-size-mb", -1)) + + if diskCacheSize == -1 { + log.Infof("No pcap disk cache size set. Skipping cache pruning.") + return nil + } + + // Let user use MB as the most sensible unit of disk size. Convert to + // bytes for comparing to file sizes. + diskCacheSize = diskCacheSize * 1024 * 1024 + + log.Infof("Pruning termshark's pcap disk cache at %s...", PcapDir()) + + var totalSize int64 + var fileInfos []os.FileInfo + err := filepath.Walk(PcapDir(), + func(path string, info os.FileInfo, err error) error { + if err == nil { + totalSize += info.Size() + fileInfos = append(fileInfos, info) + } + return nil + }, + ) + if err != nil { + return err + } + + sort.Slice(fileInfos, func(i, j int) bool { + return fileInfos[i].ModTime().Before(fileInfos[j].ModTime()) + }) + + filesRemoved := 0 + curCacheSize := totalSize + for len(fileInfos) > 0 && curCacheSize > diskCacheSize { + err = os.Remove(filepath.Join(PcapDir(), fileInfos[0].Name())) + if err != nil { + log.Warnf("Could not remove pcap cache file %s while pruning - %v", fileInfos[0].Name(), err) + } else { + curCacheSize = curCacheSize - fileInfos[0].Size() + filesRemoved++ + } + fileInfos = fileInfos[1:] + } + + if filesRemoved > 0 { + log.Infof("Pruning complete. Removed %d old pcaps. Cache size is now %d MB", + filesRemoved, curCacheSize/(1024*1024)) + } else { + log.Infof("Pruning complete. No old pcaps removed. Cache size is %d MB", + curCacheSize/(1024*1024)) + } + + return nil +} + +//====================================================================== + var cpuProfileRunning *abool.AtomicBool func init() {