1/*
2Copyright (c) 2016 VMware, Inc. All Rights Reserved.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package datastore
18
19import (
20	"context"
21	"flag"
22	"io"
23	"os"
24	"time"
25
26	"github.com/vmware/govmomi/govc/cli"
27	"github.com/vmware/govmomi/govc/flags"
28)
29
30type tail struct {
31	*flags.DatastoreFlag
32	*flags.HostSystemFlag
33
34	count  int64
35	lines  int
36	follow bool
37}
38
39func init() {
40	cli.Register("datastore.tail", &tail{})
41}
42
43func (cmd *tail) Register(ctx context.Context, f *flag.FlagSet) {
44	cmd.DatastoreFlag, ctx = flags.NewDatastoreFlag(ctx)
45	cmd.DatastoreFlag.Register(ctx, f)
46
47	cmd.HostSystemFlag, ctx = flags.NewHostSystemFlag(ctx)
48	cmd.HostSystemFlag.Register(ctx, f)
49
50	f.Int64Var(&cmd.count, "c", -1, "Output the last NUM bytes")
51	f.IntVar(&cmd.lines, "n", 10, "Output the last NUM lines")
52	f.BoolVar(&cmd.follow, "f", false, "Output appended data as the file grows")
53}
54
55func (cmd *tail) Description() string {
56	return `Output the last part of datastore files.
57
58Examples:
59  govc datastore.tail -n 100 vm-name/vmware.log
60  govc datastore.tail -n 0 -f vm-name/vmware.log`
61}
62
63func (cmd *tail) Process(ctx context.Context) error {
64	if err := cmd.DatastoreFlag.Process(ctx); err != nil {
65		return err
66	}
67	if err := cmd.HostSystemFlag.Process(ctx); err != nil {
68		return err
69	}
70	return nil
71}
72
73func (cmd *tail) Usage() string {
74	return "PATH"
75}
76
77func (cmd *tail) Run(ctx context.Context, f *flag.FlagSet) error {
78	if f.NArg() != 1 {
79		return flag.ErrHelp
80	}
81
82	p := cmd.Args(f.Args())[0]
83
84	ds, err := cmd.Datastore()
85	if err != nil {
86		return err
87	}
88
89	h, err := cmd.HostSystemIfSpecified()
90	if err != nil {
91		return err
92	}
93
94	if h != nil {
95		ctx = ds.HostContext(ctx, h)
96	}
97
98	file, err := ds.Open(ctx, p.Path)
99	if err != nil {
100		return err
101	}
102
103	var reader io.ReadCloser = file
104
105	var offset int64
106
107	if cmd.count >= 0 {
108		info, serr := file.Stat()
109		if serr != nil {
110			return serr
111		}
112
113		if info.Size() > cmd.count {
114			offset = info.Size() - cmd.count
115
116			_, err = file.Seek(offset, io.SeekStart)
117			if err != nil {
118				return err
119			}
120		}
121	} else if cmd.lines >= 0 {
122		err = file.Tail(cmd.lines)
123		if err != nil {
124			return err
125		}
126	}
127
128	if cmd.follow {
129		reader = file.Follow(time.Second)
130	}
131
132	_, err = io.Copy(os.Stdout, reader)
133
134	_ = reader.Close()
135
136	return err
137}
138