github.com/vmware/govmomi@v0.43.0/toolbox/command.go (about) 1 /* 2 Copyright (c) 2017 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 toolbox 18 19 import ( 20 "bytes" 21 "encoding/base64" 22 "encoding/binary" 23 "encoding/hex" 24 "fmt" 25 "log" 26 "os" 27 "path/filepath" 28 "runtime" 29 "strings" 30 "time" 31 32 "github.com/vmware/govmomi/toolbox/hgfs" 33 "github.com/vmware/govmomi/toolbox/process" 34 "github.com/vmware/govmomi/toolbox/vix" 35 ) 36 37 type CommandHandler func(vix.CommandRequestHeader, []byte) ([]byte, error) 38 39 type CommandServer struct { 40 Out *ChannelOut 41 42 ProcessManager *process.Manager 43 44 Authenticate func(vix.CommandRequestHeader, []byte) error 45 46 ProcessStartCommand func(*process.Manager, *vix.StartProgramRequest) (int64, error) 47 48 handlers map[uint32]CommandHandler 49 50 FileServer *hgfs.Server 51 } 52 53 func registerCommandServer(service *Service) *CommandServer { 54 server := &CommandServer{ 55 Out: service.out, 56 ProcessManager: process.NewManager(), 57 } 58 59 server.handlers = map[uint32]CommandHandler{ 60 vix.CommandGetToolsState: server.GetToolsState, 61 vix.CommandStartProgram: server.StartCommand, 62 vix.CommandTerminateProcess: server.KillProcess, 63 vix.CommandListProcessesEx: server.ListProcesses, 64 vix.CommandReadEnvVariables: server.ReadEnvironmentVariables, 65 vix.CommandCreateTemporaryFileEx: server.CreateTemporaryFile, 66 vix.CommandCreateTemporaryDirectory: server.CreateTemporaryDirectory, 67 vix.CommandDeleteGuestFileEx: server.DeleteFile, 68 vix.CommandCreateDirectoryEx: server.CreateDirectory, 69 vix.CommandDeleteGuestDirectoryEx: server.DeleteDirectory, 70 vix.CommandMoveGuestFileEx: server.MoveFile, 71 vix.CommandMoveGuestDirectory: server.MoveDirectory, 72 vix.CommandListFiles: server.ListFiles, 73 vix.CommandSetGuestFileAttributes: server.SetGuestFileAttributes, 74 vix.CommandInitiateFileTransferFromGuest: server.InitiateFileTransferFromGuest, 75 vix.CommandInitiateFileTransferToGuest: server.InitiateFileTransferToGuest, 76 vix.HgfsSendPacketCommand: server.ProcessHgfsPacket, 77 } 78 79 server.ProcessStartCommand = DefaultStartCommand 80 81 service.RegisterHandler("Vix_1_Relayed_Command", server.Dispatch) 82 83 return server 84 } 85 86 func commandResult(header vix.CommandRequestHeader, rc int, err error, response []byte) []byte { 87 // All Foundry tools commands return results that start with a foundry error 88 // and a guest-OS-specific error (e.g. errno) 89 errno := 0 90 91 if err != nil { 92 // TODO: inspect err for system error, setting errno 93 94 response = []byte(err.Error()) 95 96 log.Printf("[vix] op=%d error: %s", header.OpCode, err) 97 } 98 99 buf := bytes.NewBufferString(fmt.Sprintf("%d %d ", rc, errno)) 100 101 if header.CommonFlags&vix.CommandGuestReturnsBinary != 0 { 102 // '#' delimits end of ascii and the start of the binary data (see ToolsDaemonTcloReceiveVixCommand) 103 _ = buf.WriteByte('#') 104 } 105 106 _, _ = buf.Write(response) 107 108 if header.CommonFlags&vix.CommandGuestReturnsBinary == 0 { 109 // this is not binary data, so it should be a NULL terminated string (see ToolsDaemonTcloReceiveVixCommand) 110 _ = buf.WriteByte(0) 111 } 112 113 return buf.Bytes() 114 } 115 116 func (c *CommandServer) Dispatch(data []byte) ([]byte, error) { 117 // See ToolsDaemonTcloGetQuotedString 118 if data[0] == '"' { 119 data = data[1:] 120 } 121 122 var name string 123 124 ix := bytes.IndexByte(data, '"') 125 if ix > 0 { 126 name = string(data[:ix]) 127 data = data[ix+1:] 128 } 129 // skip the NULL 130 if data[0] == 0 { 131 data = data[1:] 132 } 133 134 if Trace { 135 fmt.Fprintf(os.Stderr, "vix dispatch %q...\n%s\n", name, hex.Dump(data)) 136 } 137 138 var header vix.CommandRequestHeader 139 buf := bytes.NewBuffer(data) 140 err := binary.Read(buf, binary.LittleEndian, &header) 141 if err != nil { 142 return nil, err 143 } 144 145 if header.Magic != vix.CommandMagicWord { 146 return commandResult(header, vix.InvalidMessageHeader, nil, nil), nil 147 } 148 149 handler, ok := c.handlers[header.OpCode] 150 if !ok { 151 return commandResult(header, vix.UnrecognizedCommandInGuest, nil, nil), nil 152 } 153 154 if header.OpCode != vix.CommandGetToolsState { 155 // Every command expect GetToolsState requires authentication 156 creds := buf.Bytes()[header.BodyLength:] 157 158 err = c.authenticate(header, creds[:header.CredentialLength]) 159 if err != nil { 160 return commandResult(header, vix.AuthenticationFail, err, nil), nil 161 } 162 } 163 164 rc := vix.OK 165 166 response, err := handler(header, buf.Bytes()) 167 if err != nil { 168 rc = vix.ErrorCode(err) 169 } 170 171 return commandResult(header, rc, err, response), nil 172 } 173 174 func (c *CommandServer) RegisterHandler(op uint32, handler CommandHandler) { 175 c.handlers[op] = handler 176 } 177 178 func (c *CommandServer) GetToolsState(_ vix.CommandRequestHeader, _ []byte) ([]byte, error) { 179 hostname, _ := os.Hostname() 180 osname := fmt.Sprintf("%s-%s", runtime.GOOS, runtime.GOARCH) 181 182 // Note that vmtoolsd sends back 40 or so of these properties, sticking with the minimal set for now. 183 props := vix.PropertyList{ 184 vix.NewStringProperty(vix.PropertyGuestOsVersion, osname), 185 vix.NewStringProperty(vix.PropertyGuestOsVersionShort, osname), 186 vix.NewStringProperty(vix.PropertyGuestToolsProductNam, "VMware Tools (Go)"), 187 vix.NewStringProperty(vix.PropertyGuestToolsVersion, "10.0.5 build-3227872 (Compatible)"), 188 vix.NewStringProperty(vix.PropertyGuestName, hostname), 189 vix.NewInt32Property(vix.PropertyGuestToolsAPIOptions, 0x0001), // TODO: const VIX_TOOLSFEATURE_SUPPORT_GET_HANDLE_STATE 190 vix.NewInt32Property(vix.PropertyGuestOsFamily, 1), // TODO: const GUEST_OS_FAMILY_* 191 vix.NewBoolProperty(vix.PropertyGuestStartProgramEnabled, true), 192 vix.NewBoolProperty(vix.PropertyGuestTerminateProcessEnabled, true), 193 vix.NewBoolProperty(vix.PropertyGuestListProcessesEnabled, true), 194 vix.NewBoolProperty(vix.PropertyGuestReadEnvironmentVariableEnabled, true), 195 vix.NewBoolProperty(vix.PropertyGuestMakeDirectoryEnabled, true), 196 vix.NewBoolProperty(vix.PropertyGuestDeleteFileEnabled, true), 197 vix.NewBoolProperty(vix.PropertyGuestDeleteDirectoryEnabled, true), 198 vix.NewBoolProperty(vix.PropertyGuestMoveDirectoryEnabled, true), 199 vix.NewBoolProperty(vix.PropertyGuestMoveFileEnabled, true), 200 vix.NewBoolProperty(vix.PropertyGuestCreateTempFileEnabled, true), 201 vix.NewBoolProperty(vix.PropertyGuestCreateTempDirectoryEnabled, true), 202 vix.NewBoolProperty(vix.PropertyGuestListFilesEnabled, true), 203 vix.NewBoolProperty(vix.PropertyGuestChangeFileAttributesEnabled, true), 204 vix.NewBoolProperty(vix.PropertyGuestInitiateFileTransferFromGuestEnabled, true), 205 vix.NewBoolProperty(vix.PropertyGuestInitiateFileTransferToGuestEnabled, true), 206 } 207 208 src, _ := props.MarshalBinary() 209 enc := base64.StdEncoding 210 buf := make([]byte, enc.EncodedLen(len(src))) 211 enc.Encode(buf, src) 212 213 return buf, nil 214 } 215 216 func (c *CommandServer) StartCommand(header vix.CommandRequestHeader, data []byte) ([]byte, error) { 217 r := &vix.StartProgramRequest{ 218 CommandRequestHeader: header, 219 } 220 221 err := r.UnmarshalBinary(data) 222 if err != nil { 223 return nil, err 224 } 225 226 pid, err := c.ProcessStartCommand(c.ProcessManager, r) 227 if err != nil { 228 return nil, err 229 } 230 231 return append([]byte(fmt.Sprintf("%d", pid)), 0), nil 232 } 233 234 func DefaultStartCommand(m *process.Manager, r *vix.StartProgramRequest) (int64, error) { 235 p := process.New() 236 237 switch r.ProgramPath { 238 case "http.RoundTrip": 239 p = process.NewRoundTrip() 240 default: 241 // Standard vmware-tools requires an absolute path, 242 // we'll enable IO redirection by default without an absolute path. 243 if !strings.Contains(r.ProgramPath, "/") { 244 p = p.WithIO() 245 } 246 } 247 248 return m.Start(r, p) 249 } 250 251 func (c *CommandServer) KillProcess(header vix.CommandRequestHeader, data []byte) ([]byte, error) { 252 r := &vix.KillProcessRequest{ 253 CommandRequestHeader: header, 254 } 255 256 err := r.UnmarshalBinary(data) 257 if err != nil { 258 return nil, err 259 } 260 261 if c.ProcessManager.Kill(r.Body.Pid) { 262 return nil, err 263 } 264 265 // TODO: could kill process started outside of toolbox 266 267 return nil, vix.Error(vix.NoSuchProcess) 268 } 269 270 func (c *CommandServer) ListProcesses(header vix.CommandRequestHeader, data []byte) ([]byte, error) { 271 r := &vix.ListProcessesRequest{ 272 CommandRequestHeader: header, 273 } 274 275 err := r.UnmarshalBinary(data) 276 if err != nil { 277 return nil, err 278 } 279 280 state := c.ProcessManager.ListProcesses(r.Pids) 281 282 return state, nil 283 } 284 285 func (c *CommandServer) ReadEnvironmentVariables(header vix.CommandRequestHeader, data []byte) ([]byte, error) { 286 r := &vix.ReadEnvironmentVariablesRequest{ 287 CommandRequestHeader: header, 288 } 289 290 err := r.UnmarshalBinary(data) 291 if err != nil { 292 return nil, err 293 } 294 295 buf := new(bytes.Buffer) 296 297 if len(r.Names) == 0 { 298 for _, e := range os.Environ() { 299 _, _ = buf.WriteString(fmt.Sprintf("<ev>%s</ev>", process.EscapeXML.Replace(e))) 300 } 301 } else { 302 for _, key := range r.Names { 303 val := os.Getenv(key) 304 if val == "" { 305 continue 306 } 307 _, _ = buf.WriteString(fmt.Sprintf("<ev>%s=%s</ev>", process.EscapeXML.Replace(key), process.EscapeXML.Replace(val))) 308 } 309 } 310 311 return buf.Bytes(), nil 312 } 313 314 func (c *CommandServer) CreateTemporaryFile(header vix.CommandRequestHeader, data []byte) ([]byte, error) { 315 r := &vix.CreateTempFileRequest{ 316 CommandRequestHeader: header, 317 } 318 319 err := r.UnmarshalBinary(data) 320 if err != nil { 321 return nil, err 322 } 323 324 f, err := os.CreateTemp(r.DirectoryPath, r.FilePrefix+"vmware") 325 if err != nil { 326 return nil, err 327 } 328 329 _ = f.Close() 330 331 return []byte(f.Name()), nil 332 } 333 334 func (c *CommandServer) CreateTemporaryDirectory(header vix.CommandRequestHeader, data []byte) ([]byte, error) { 335 r := &vix.CreateTempFileRequest{ 336 CommandRequestHeader: header, 337 } 338 339 err := r.UnmarshalBinary(data) 340 if err != nil { 341 return nil, err 342 } 343 344 name, err := os.MkdirTemp(r.DirectoryPath, r.FilePrefix+"vmware") 345 if err != nil { 346 return nil, err 347 } 348 349 return []byte(name), nil 350 } 351 352 func (c *CommandServer) DeleteFile(header vix.CommandRequestHeader, data []byte) ([]byte, error) { 353 r := &vix.FileRequest{ 354 CommandRequestHeader: header, 355 } 356 357 err := r.UnmarshalBinary(data) 358 if err != nil { 359 return nil, err 360 } 361 362 info, err := os.Stat(r.GuestPathName) 363 if err != nil { 364 return nil, err 365 } 366 367 if info.IsDir() { 368 return nil, vix.Error(vix.NotAFile) 369 } 370 371 err = os.Remove(r.GuestPathName) 372 373 return nil, err 374 } 375 376 func (c *CommandServer) DeleteDirectory(header vix.CommandRequestHeader, data []byte) ([]byte, error) { 377 r := &vix.DirRequest{ 378 CommandRequestHeader: header, 379 } 380 381 err := r.UnmarshalBinary(data) 382 if err != nil { 383 return nil, err 384 } 385 386 info, err := os.Stat(r.GuestPathName) 387 if err != nil { 388 return nil, err 389 } 390 391 if !info.IsDir() { 392 return nil, vix.Error(vix.NotADirectory) 393 } 394 395 if r.Body.Recursive { 396 err = os.RemoveAll(r.GuestPathName) 397 } else { 398 err = os.Remove(r.GuestPathName) 399 } 400 401 return nil, err 402 } 403 404 func (c *CommandServer) CreateDirectory(header vix.CommandRequestHeader, data []byte) ([]byte, error) { 405 r := &vix.DirRequest{ 406 CommandRequestHeader: header, 407 } 408 409 err := r.UnmarshalBinary(data) 410 if err != nil { 411 return nil, err 412 } 413 414 mkdir := os.Mkdir 415 416 if r.Body.Recursive { 417 mkdir = os.MkdirAll 418 } 419 420 err = mkdir(r.GuestPathName, 0700) 421 422 return nil, err 423 } 424 425 func (c *CommandServer) MoveDirectory(header vix.CommandRequestHeader, data []byte) ([]byte, error) { 426 r := &vix.RenameFileRequest{ 427 CommandRequestHeader: header, 428 } 429 430 err := r.UnmarshalBinary(data) 431 if err != nil { 432 return nil, err 433 } 434 435 info, err := os.Stat(r.OldPathName) 436 if err != nil { 437 return nil, err 438 } 439 440 if !info.IsDir() { 441 return nil, vix.Error(vix.NotADirectory) 442 } 443 444 if !r.Body.Overwrite { 445 _, err = os.Stat(r.NewPathName) 446 if err == nil { 447 return nil, vix.Error(vix.FileAlreadyExists) 448 } 449 } 450 451 return nil, os.Rename(r.OldPathName, r.NewPathName) 452 } 453 454 func (c *CommandServer) MoveFile(header vix.CommandRequestHeader, data []byte) ([]byte, error) { 455 r := &vix.RenameFileRequest{ 456 CommandRequestHeader: header, 457 } 458 459 err := r.UnmarshalBinary(data) 460 if err != nil { 461 return nil, err 462 } 463 464 info, err := os.Stat(r.OldPathName) 465 if err != nil { 466 return nil, err 467 } 468 469 if info.IsDir() { 470 return nil, vix.Error(vix.NotAFile) 471 } 472 473 if !r.Body.Overwrite { 474 _, err = os.Stat(r.NewPathName) 475 if err == nil { 476 return nil, vix.Error(vix.FileAlreadyExists) 477 } 478 } 479 480 return nil, os.Rename(r.OldPathName, r.NewPathName) 481 } 482 483 func (c *CommandServer) ListFiles(header vix.CommandRequestHeader, data []byte) ([]byte, error) { 484 r := &vix.ListFilesRequest{ 485 CommandRequestHeader: header, 486 } 487 488 err := r.UnmarshalBinary(data) 489 if err != nil { 490 return nil, err 491 } 492 493 info, err := os.Lstat(r.GuestPathName) 494 if err != nil { 495 return nil, err 496 } 497 498 var dir string 499 var files []os.FileInfo 500 501 if info.IsDir() { 502 dir = r.GuestPathName 503 entries, err := os.ReadDir(r.GuestPathName) 504 if err != nil { 505 return nil, err 506 } 507 for _, entry := range entries { 508 file, _ := entry.Info() 509 files = append(files, file) 510 } 511 } else { 512 dir = filepath.Dir(r.GuestPathName) 513 files = append(files, info) 514 } 515 516 offset := r.Body.Offset + uint64(r.Body.Index) 517 total := uint64(len(files)) - offset 518 if int(offset) < len(files) { 519 files = files[offset:] 520 } else { 521 total = 0 // offset is not valid (open-vm-tools behaves the same in this case) 522 } 523 524 var remaining uint64 525 526 if r.Body.MaxResults > 0 && total > uint64(r.Body.MaxResults) { 527 remaining = total - uint64(r.Body.MaxResults) 528 files = files[:r.Body.MaxResults] 529 } 530 531 buf := new(bytes.Buffer) 532 buf.WriteString(fmt.Sprintf("<rem>%d</rem>", remaining)) 533 534 for _, info = range files { 535 buf.WriteString(fileExtendedInfoFormat(dir, info)) 536 } 537 538 return buf.Bytes(), nil 539 } 540 541 func chtimes(r *vix.SetGuestFileAttributesRequest) error { 542 var mtime, atime *time.Time 543 544 if r.IsSet(vix.FileAttributeSetModifyDate) { 545 t := time.Unix(r.Body.ModificationTime, 0) 546 mtime = &t 547 } 548 549 if r.IsSet(vix.FileAttributeSetAccessDate) { 550 t := time.Unix(r.Body.AccessTime, 0) 551 atime = &t 552 } 553 554 if mtime == nil && atime == nil { 555 return nil 556 } 557 558 info, err := os.Stat(r.GuestPathName) 559 if err != nil { 560 return err 561 } 562 563 if mtime == nil { 564 t := info.ModTime() 565 mtime = &t 566 } 567 568 if atime == nil { 569 t := info.ModTime() 570 atime = &t 571 } 572 573 return os.Chtimes(r.GuestPathName, *atime, *mtime) 574 } 575 576 func chown(r *vix.SetGuestFileAttributesRequest) error { 577 uid := -1 578 gid := -1 579 580 if r.IsSet(vix.FileAttributeSetUnixOwnerid) { 581 uid = int(r.Body.OwnerID) 582 } 583 584 if r.IsSet(vix.FileAttributeSetUnixGroupid) { 585 gid = int(r.Body.GroupID) 586 } 587 588 if uid == -1 && gid == -1 { 589 return nil 590 } 591 592 return os.Chown(r.GuestPathName, uid, gid) 593 } 594 595 func chmod(r *vix.SetGuestFileAttributesRequest) error { 596 if r.IsSet(vix.FileAttributeSetUnixPermissions) { 597 return os.Chmod(r.GuestPathName, os.FileMode(r.Body.Permissions).Perm()) 598 } 599 600 return nil 601 } 602 603 func (c *CommandServer) SetGuestFileAttributes(header vix.CommandRequestHeader, data []byte) ([]byte, error) { 604 r := &vix.SetGuestFileAttributesRequest{ 605 CommandRequestHeader: header, 606 } 607 608 err := r.UnmarshalBinary(data) 609 if err != nil { 610 return nil, err 611 } 612 613 for _, set := range []func(*vix.SetGuestFileAttributesRequest) error{chtimes, chown, chmod} { 614 err = set(r) 615 if err != nil { 616 return nil, err 617 } 618 } 619 620 return nil, nil 621 } 622 623 func (c *CommandServer) InitiateFileTransferFromGuest(header vix.CommandRequestHeader, data []byte) ([]byte, error) { 624 r := &vix.ListFilesRequest{ 625 CommandRequestHeader: header, 626 } 627 628 err := r.UnmarshalBinary(data) 629 if err != nil { 630 return nil, err 631 } 632 633 info, err := c.FileServer.Stat(r.GuestPathName) 634 if err != nil { 635 return nil, err 636 } 637 638 if info.Mode()&os.ModeSymlink == os.ModeSymlink { 639 return nil, vix.Error(vix.InvalidArg) 640 } 641 642 if info.IsDir() { 643 return nil, vix.Error(vix.NotAFile) 644 } 645 646 return []byte(fileExtendedInfoFormat("", info)), nil 647 } 648 649 func (c *CommandServer) InitiateFileTransferToGuest(header vix.CommandRequestHeader, data []byte) ([]byte, error) { 650 r := &vix.InitiateFileTransferToGuestRequest{ 651 CommandRequestHeader: header, 652 } 653 654 err := r.UnmarshalBinary(data) 655 if err != nil { 656 return nil, err 657 } 658 659 info, err := c.FileServer.Stat(r.GuestPathName) 660 if err == nil { 661 if info.Mode()&os.ModeSymlink == os.ModeSymlink { 662 return nil, vix.Error(vix.InvalidArg) 663 } 664 665 if info.IsDir() { 666 return nil, vix.Error(vix.NotAFile) 667 } 668 669 if !r.Body.Overwrite { 670 return nil, vix.Error(vix.FileAlreadyExists) 671 } 672 } else if !os.IsNotExist(err) { 673 return nil, err 674 } 675 676 return nil, nil 677 } 678 679 func (c *CommandServer) ProcessHgfsPacket(header vix.CommandRequestHeader, data []byte) ([]byte, error) { 680 r := &vix.CommandHgfsSendPacket{ 681 CommandRequestHeader: header, 682 } 683 684 err := r.UnmarshalBinary(data) 685 if err != nil { 686 return nil, err 687 } 688 689 return c.FileServer.Dispatch(r.Packet) 690 } 691 692 func (c *CommandServer) authenticate(r vix.CommandRequestHeader, data []byte) error { 693 if c.Authenticate != nil { 694 return c.Authenticate(r, data) 695 } 696 697 switch r.UserCredentialType { 698 case vix.UserCredentialTypeNamePassword: 699 var c vix.UserCredentialNamePassword 700 701 if err := c.UnmarshalBinary(data); err != nil { 702 return err 703 } 704 705 if Trace { 706 fmt.Fprintf(traceLog, "ignoring credentials: %q:%q\n", c.Name, c.Password) 707 } 708 709 return nil 710 default: 711 return fmt.Errorf("unsupported UserCredentialType=%d", r.UserCredentialType) 712 } 713 }