diff --git a/sender/file.go b/sender/file.go index 72976159..8626b38e 100644 --- a/sender/file.go +++ b/sender/file.go @@ -3,6 +3,8 @@ * * Copyright (c) 2019-2020 Telenor Norge AS * Author(s): + * - Roshini Narasimha Raghavan + * - Kristian Lyngstøl * - Håkon Solbjørg * * This library is free software; you can redistribute it and/or @@ -26,7 +28,9 @@ package sender import ( "fmt" "os" + "os/signal" "sync" + "syscall" "github.com/telenornms/skogul" "github.com/telenornms/skogul/encoder" @@ -49,6 +53,7 @@ type File struct { once sync.Once f *os.File c chan []byte + sighup chan os.Signal } func (f *File) init() { @@ -104,15 +109,38 @@ func (f *File) startChan() { fileLog.Trace("Starting file writer channel") // Making sure we close the file if this function exits defer f.f.Close() - for b := range f.c { - written, err := f.f.Write(append(b, newLineChar)) - if err != nil { - f.ok = false - fileLog.WithField("path", f.File).WithError(err).Errorf("Failed to write to file. Wrote %d of %d bytes", written, len(b)) + f.sighup = make(chan os.Signal, 1) + signal.Notify(f.sighup, syscall.SIGHUP) + for { + select { + case b := <-f.c: + written, err := f.f.Write(append(b, newLineChar)) + if err != nil { + f.ok = false + fileLog.WithField("path", f.File).WithError(err).Errorf("Failed to write to file. Wrote %d of %d bytes", written, len(b)) + } + f.f.Sync() + case <-f.sighup: + f.f.Close() + var file *os.File + if finfo, err := os.Stat(f.File); !os.IsNotExist(err) && f.Append { + fileLog.WithField("path", f.File).Trace("File exists, let's open it for writing") + file, err = os.OpenFile(f.File, os.O_APPEND|os.O_WRONLY, finfo.Mode()) + f.f = file + f.ok = true + } else { + // Otherwise, create the file (which will truncate it if it already exists) + fileLog.WithField("path", f.File).Trace("Creating file since it doesn't exist or we don't want to append to it") + _, err = os.Create(f.File) + if err != nil { + fmt.Errorf("%s: Error reopening: %s\n", skogul.Now(), err) + f.ok = false + return + } + f.ok = true + } } - f.f.Sync() } - fileLog.WithField("path", f.File).Warning("File writer chan closed, not handling any more writes!") } // Send receives a skogul container and writes it to file. diff --git a/sender/file_test.go b/sender/file_test.go index 7bd5d4ec..aa11d66b 100644 --- a/sender/file_test.go +++ b/sender/file_test.go @@ -3,6 +3,8 @@ * * Copyright (c) 2019-2020 Telenor Norge AS * Author(s): + * - Roshini Narasimha Raghavan + * - Kristian Lyngstøl * - Håkon Solbjørg * * This library is free software; you can redistribute it and/or @@ -29,6 +31,7 @@ import ( "os" "path" "strings" + "syscall" "testing" "time" @@ -140,3 +143,67 @@ func TestAppendToExistingFile(t *testing.T) { t.Errorf("Test file does not contain test string 'some data', was it overwritten? Contents: %s", str) } } + +func TestSignals(t *testing.T) { + filename := "skogul-file-1.txt" + path := path.Join(os.TempDir(), filename) + + f, err := os.Create(path) + if err != nil { + t.Error(err) + return + } + + f.Write([]byte("some data\n")) + f.Sync() + + time.Sleep(time.Second) + + sender := &sender.File{ + File: path, + Append: false, + } + + c := createContainer() + + sender.Send(&c) + + os.Rename(path, path+".old") + + // file is closed and it will just log that file is closed and wrote 0 bytes. + sender.Send(&c) + + // the file should not exists since we renamed. If it continues to exists, then fail the test. + if _, err := os.Stat(path); err == nil { + t.Error(err) + t.FailNow() + } + + // Since the write is done by a goroutine + // we have to make sure it is properly + // flushed before we try to read it back + time.Sleep(time.Second) + proc, err := os.FindProcess(os.Getpid()) + if err != nil { + t.Error(err) + return + } + + // provides an interrup signal to the current process handling the file. + if err := proc.Signal(syscall.SIGHUP); err != nil { + t.Error(err) + return + } + + // give enough time for the sighup signal to be handled and send the container again. This should reopen the file and write the values sent by the container. + time.Sleep(time.Second * 4) + sender.Send(&c) + + // If the file remains closed, then the following commands will fail the test. + _, err = os.Stat(path) + if err != nil { + t.Error(err) + return + } + +}