(ctx context.Context)
| 27 | ) |
| 28 | |
| 29 | func (a *HostAgent) startInotify(ctx context.Context) error { |
| 30 | mountWatchCh := make(chan notify.EventInfo, 128) |
| 31 | if err := a.setupWatchers(mountWatchCh); err != nil { |
| 32 | return err |
| 33 | } |
| 34 | // notify.Watch allocates per-call kernel watchers and an internal reader |
| 35 | // goroutine; without notify.Stop they leak for the lifetime of the process. |
| 36 | defer notify.Stop(mountWatchCh) |
| 37 | |
| 38 | // PostInotify returns a usable stream wrapper before the server-side |
| 39 | // handler is installed, so Sends issued during the gap fail with EOF. |
| 40 | select { |
| 41 | case <-a.guestAgentAliveCh: |
| 42 | case <-ctx.Done(): |
| 43 | return nil |
| 44 | } |
| 45 | |
| 46 | client, err := a.getOrCreateClient(ctx) |
| 47 | if err != nil { |
| 48 | return fmt.Errorf("inotify: failed to obtain guest agent client: %w", err) |
| 49 | } |
| 50 | inotifyClient, err := client.Inotify(ctx) |
| 51 | if err != nil { |
| 52 | return err |
| 53 | } |
| 54 | // Finalize the gRPC client-stream so the guest agent's PostInotify handler |
| 55 | // can return instead of staying parked on a half-open stream. |
| 56 | defer func() { _ = inotifyClient.CloseSend() }() |
| 57 | |
| 58 | for { |
| 59 | select { |
| 60 | case <-ctx.Done(): |
| 61 | return nil |
| 62 | case watchEvent := <-mountWatchCh: |
| 63 | watchPath := watchEvent.Path() |
| 64 | stat, err := os.Stat(watchPath) |
| 65 | if err != nil { |
| 66 | continue |
| 67 | } |
| 68 | |
| 69 | if filterEvents(watchEvent, stat) { |
| 70 | continue |
| 71 | } |
| 72 | |
| 73 | watchPath = translateToGuestPath(watchPath, mountSymlinks, mountLocations) |
| 74 | |
| 75 | utcTimestamp := timestamppb.New(stat.ModTime().UTC()) |
| 76 | event := &guestagentapi.Inotify{MountPath: watchPath, Time: utcTimestamp} |
| 77 | if err := inotifyClient.Send(event); err != nil { |
| 78 | // Stream is gone (typically a guest-agent reconnect). Return so |
| 79 | // the caller can re-spawn against the new client instead of |
| 80 | // looping silently with a dead stream. |
| 81 | return fmt.Errorf("inotify stream closed: %w", err) |
| 82 | } |
| 83 | } |
| 84 | } |
| 85 | } |
| 86 |
no test coverage detected