mirror of
https://github.com/containers/podman.git
synced 2025-10-20 12:43:58 +08:00

Fix a potential panic in the events endpoint when parsing the filters parameter. Values of the filters map might be empty, so we need to account for that instead of uncondtitionally accessing the first item. Also apply a similar for race conditions as done in commit f4a2d25c0fca: Fix a race that could cause read errors to be masked. Masking such errors is likely to report red herrings since users don't see that reading failed for some reasons but that a given event could not be found. Another race was the handler closing event channel, which could lead to two kinds of panics: double close, send to close channel. The backend takes care of that. However, make sure that the backend stops working in case the context has been cancelled. Fixes: #6899 Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
100 lines
2.2 KiB
Go
100 lines
2.2 KiB
Go
package events
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/containers/storage"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// EventLogFile is the structure for event writing to a logfile. It contains the eventer
|
|
// options and the event itself. Methods for reading and writing are also defined from it.
|
|
type EventLogFile struct {
|
|
options EventerOptions
|
|
}
|
|
|
|
// Writes to the log file
|
|
func (e EventLogFile) Write(ee Event) error {
|
|
// We need to lock events file
|
|
lock, err := storage.GetLockfile(e.options.LogFilePath + ".lock")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
lock.Lock()
|
|
defer lock.Unlock()
|
|
f, err := os.OpenFile(e.options.LogFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0700)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
eventJSONString, err := ee.ToJSONString()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err := f.WriteString(fmt.Sprintf("%s\n", eventJSONString)); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
|
|
}
|
|
|
|
// Reads from the log file
|
|
func (e EventLogFile) Read(ctx context.Context, options ReadOptions) error {
|
|
defer close(options.EventChannel)
|
|
eventOptions, err := generateEventOptions(options.Filters, options.Since, options.Until)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "unable to generate event options")
|
|
}
|
|
t, err := e.getTail(options)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
funcDone := make(chan bool)
|
|
copy := true
|
|
go func() {
|
|
select {
|
|
case <-funcDone:
|
|
// Do nothing
|
|
case <-ctx.Done():
|
|
copy = false
|
|
t.Kill(errors.New("hangup by client"))
|
|
}
|
|
}()
|
|
for line := range t.Lines {
|
|
select {
|
|
case <-ctx.Done():
|
|
// the consumer has cancelled
|
|
return nil
|
|
default:
|
|
// fallthrough
|
|
}
|
|
|
|
event, err := newEventFromJSONString(line.Text)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch event.Type {
|
|
case Image, Volume, Pod, System, Container:
|
|
// no-op
|
|
default:
|
|
return errors.Errorf("event type %s is not valid in %s", event.Type.String(), e.options.LogFilePath)
|
|
}
|
|
include := true
|
|
for _, filter := range eventOptions {
|
|
include = include && filter(event)
|
|
}
|
|
if include && copy {
|
|
options.EventChannel <- event
|
|
}
|
|
}
|
|
funcDone <- true
|
|
return nil
|
|
}
|
|
|
|
// String returns a string representation of the logger
|
|
func (e EventLogFile) String() string {
|
|
return LogFile.String()
|
|
}
|