github.com/vmware/govmomi@v0.37.1/govc/vm/guest/ps.go (about)

     1  /*
     2  Copyright (c) 2014-2023 VMware, Inc. All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package guest
    18  
    19  import (
    20  	"context"
    21  	"flag"
    22  	"fmt"
    23  	"io"
    24  	"strconv"
    25  	"strings"
    26  	"text/tabwriter"
    27  	"time"
    28  
    29  	"github.com/vmware/govmomi/govc/cli"
    30  	"github.com/vmware/govmomi/govc/flags"
    31  	"github.com/vmware/govmomi/vim25/types"
    32  )
    33  
    34  type ps struct {
    35  	*flags.OutputFlag
    36  	*GuestFlag
    37  
    38  	every bool
    39  	exit  bool
    40  	wait  bool
    41  
    42  	pids pidSelector
    43  	uids uidSelector
    44  }
    45  
    46  type pidSelector []int64
    47  
    48  func (s *pidSelector) String() string {
    49  	return fmt.Sprint(*s)
    50  }
    51  
    52  func (s *pidSelector) Set(value string) error {
    53  	v, err := strconv.ParseInt(value, 0, 64)
    54  	if err != nil {
    55  		return err
    56  	}
    57  	*s = append(*s, v)
    58  	return nil
    59  }
    60  
    61  type uidSelector map[string]bool
    62  
    63  func (s uidSelector) String() string {
    64  	return ""
    65  }
    66  
    67  func (s uidSelector) Set(value string) error {
    68  	s[value] = true
    69  	return nil
    70  }
    71  
    72  func init() {
    73  	cli.Register("guest.ps", &ps{})
    74  }
    75  
    76  func (cmd *ps) Register(ctx context.Context, f *flag.FlagSet) {
    77  	cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx)
    78  	cmd.OutputFlag.Register(ctx, f)
    79  
    80  	cmd.GuestFlag, ctx = newGuestProcessFlag(ctx)
    81  	cmd.GuestFlag.Register(ctx, f)
    82  
    83  	cmd.uids = make(map[string]bool)
    84  	f.BoolVar(&cmd.every, "e", false, "Select all processes")
    85  	f.BoolVar(&cmd.exit, "x", false, "Output exit time and code")
    86  	f.BoolVar(&cmd.wait, "X", false, "Wait for process to exit")
    87  	f.Var(&cmd.pids, "p", "Select by process ID")
    88  	f.Var(&cmd.uids, "U", "Select by process UID")
    89  }
    90  
    91  func (cmd *ps) Process(ctx context.Context) error {
    92  	if err := cmd.OutputFlag.Process(ctx); err != nil {
    93  		return err
    94  	}
    95  	if err := cmd.GuestFlag.Process(ctx); err != nil {
    96  		return err
    97  	}
    98  	return nil
    99  }
   100  
   101  func (cmd *ps) Description() string {
   102  	return `List processes in VM.
   103  
   104  By default, unless the '-e', '-p' or '-U' flag is specified, only processes owned
   105  by the '-l' flag user are displayed.
   106  
   107  The '-x' and '-X' flags only apply to processes started by vmware-tools,
   108  such as those started with the govc guest.start command.
   109  
   110  Examples:
   111    govc guest.ps -vm $name
   112    govc guest.ps -vm $name -e
   113    govc guest.ps -vm $name -p 12345
   114    govc guest.ps -vm $name -U root`
   115  }
   116  
   117  func running(procs []types.GuestProcessInfo) bool {
   118  	for _, p := range procs {
   119  		if p.EndTime == nil {
   120  			return true
   121  		}
   122  	}
   123  	return false
   124  }
   125  
   126  func (cmd *ps) list(ctx context.Context) ([]types.GuestProcessInfo, error) {
   127  	m, err := cmd.ProcessManager()
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  
   132  	auth := cmd.Auth()
   133  
   134  	for {
   135  		procs, err := m.ListProcesses(ctx, auth, cmd.pids)
   136  		if err != nil {
   137  			return nil, err
   138  		}
   139  
   140  		if cmd.wait && running(procs) {
   141  			<-time.After(time.Second)
   142  			continue
   143  		}
   144  
   145  		return procs, nil
   146  	}
   147  }
   148  
   149  func (cmd *ps) Run(ctx context.Context, f *flag.FlagSet) error {
   150  	procs, err := cmd.list(ctx)
   151  	if err != nil {
   152  		return err
   153  	}
   154  
   155  	r := &psResult{cmd, procs}
   156  
   157  	return cmd.WriteResult(r)
   158  }
   159  
   160  type psResult struct {
   161  	cmd         *ps
   162  	ProcessInfo []types.GuestProcessInfo `json:"processInfo"`
   163  }
   164  
   165  func (r *psResult) Write(w io.Writer) error {
   166  	tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0)
   167  
   168  	fmt.Fprintf(tw, "%s\t%s\t%s", "UID", "PID", "STIME")
   169  	if r.cmd.exit {
   170  		fmt.Fprintf(tw, "\t%s\t%s", "XTIME", "XCODE")
   171  	}
   172  	fmt.Fprint(tw, "\tCMD\n")
   173  
   174  	if len(r.cmd.pids) != 0 {
   175  		r.cmd.every = true
   176  	}
   177  
   178  	if !r.cmd.every && len(r.cmd.uids) == 0 {
   179  		r.cmd.uids[r.cmd.auth.Username] = true
   180  	}
   181  
   182  	for _, p := range r.ProcessInfo {
   183  		if r.cmd.every || r.cmd.uids[p.Owner] {
   184  			fmt.Fprintf(tw, "%s\t%d\t%s", p.Owner, p.Pid, p.StartTime.Format("15:04"))
   185  			if r.cmd.exit {
   186  				etime := "-"
   187  				ecode := "-"
   188  				if p.EndTime != nil {
   189  					etime = p.EndTime.Format("15:04")
   190  					ecode = strconv.Itoa(int(p.ExitCode))
   191  				}
   192  				fmt.Fprintf(tw, "\t%s\t%s", etime, ecode)
   193  			}
   194  			fmt.Fprintf(tw, "\t%s\n", strings.TrimSpace(p.CmdLine))
   195  		}
   196  	}
   197  
   198  	return tw.Flush()
   199  }