github.com/vmware/govmomi@v0.51.0/toolbox/vix/protocol.go (about) 1 // © Broadcom. All Rights Reserved. 2 // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. 3 // SPDX-License-Identifier: Apache-2.0 4 5 package vix 6 7 import ( 8 "bytes" 9 "encoding/base64" 10 "encoding/binary" 11 "fmt" 12 "os" 13 "os/exec" 14 "syscall" 15 ) 16 17 const ( 18 CommandMagicWord = 0xd00d0001 19 20 CommandGetToolsState = 62 21 22 CommandStartProgram = 185 23 CommandListProcessesEx = 186 24 CommandReadEnvVariables = 187 25 CommandTerminateProcess = 193 26 27 CommandCreateDirectoryEx = 178 28 CommandMoveGuestFileEx = 179 29 CommandMoveGuestDirectory = 180 30 CommandCreateTemporaryFileEx = 181 31 CommandCreateTemporaryDirectory = 182 32 CommandSetGuestFileAttributes = 183 33 CommandDeleteGuestFileEx = 194 34 CommandDeleteGuestDirectoryEx = 195 35 36 CommandListFiles = 177 37 HgfsSendPacketCommand = 84 38 CommandInitiateFileTransferFromGuest = 188 39 CommandInitiateFileTransferToGuest = 189 40 41 // VIX_USER_CREDENTIAL_NAME_PASSWORD 42 UserCredentialTypeNamePassword = 1 43 44 // VIX_E_* constants from vix.h 45 OK = 0 46 Fail = 1 47 InvalidArg = 3 48 FileNotFound = 4 49 FileAlreadyExists = 12 50 FileAccessError = 13 51 AuthenticationFail = 35 52 53 UnrecognizedCommandInGuest = 3025 54 InvalidMessageHeader = 10000 55 InvalidMessageBody = 10001 56 NotAFile = 20001 57 NotADirectory = 20002 58 NoSuchProcess = 20003 59 DirectoryNotEmpty = 20006 60 61 // VIX_COMMAND_* constants from Commands.h 62 CommandGuestReturnsBinary = 0x80 63 64 // VIX_FILE_ATTRIBUTES_ constants from vix.h 65 FileAttributesDirectory = 0x0001 66 FileAttributesSymlink = 0x0002 67 ) 68 69 // SetGuestFileAttributes flags as defined in vixOpenSource.h 70 const ( 71 FileAttributeSetAccessDate = 0x0001 72 FileAttributeSetModifyDate = 0x0002 73 FileAttributeSetReadonly = 0x0004 74 FileAttributeSetHidden = 0x0008 75 FileAttributeSetUnixOwnerid = 0x0010 76 FileAttributeSetUnixGroupid = 0x0020 77 FileAttributeSetUnixPermissions = 0x0040 78 ) 79 80 type Error int 81 82 func (err Error) Error() string { 83 return fmt.Sprintf("vix error=%d", err) 84 } 85 86 // ErrorCode does its best to map the given error to a VIX error code. 87 // See also: Vix_TranslateErrno 88 func ErrorCode(err error) int { 89 switch t := err.(type) { 90 case Error: 91 return int(t) 92 case *os.PathError: 93 if errno, ok := t.Err.(syscall.Errno); ok { 94 switch errno { 95 case syscall.ENOTEMPTY: 96 return DirectoryNotEmpty 97 } 98 } 99 case *exec.Error: 100 if t.Err == exec.ErrNotFound { 101 return FileNotFound 102 } 103 } 104 105 switch { 106 case os.IsNotExist(err): 107 return FileNotFound 108 case os.IsExist(err): 109 return FileAlreadyExists 110 case os.IsPermission(err): 111 return FileAccessError 112 default: 113 return Fail 114 } 115 } 116 117 type Header struct { 118 Magic uint32 119 MessageVersion uint16 120 121 TotalMessageLength uint32 122 HeaderLength uint32 123 BodyLength uint32 124 CredentialLength uint32 125 126 CommonFlags uint8 127 } 128 129 type CommandRequestHeader struct { 130 Header 131 132 OpCode uint32 133 RequestFlags uint32 134 135 TimeOut uint32 136 137 Cookie uint64 138 ClientHandleID uint32 139 140 UserCredentialType uint32 141 } 142 143 type StartProgramRequest struct { 144 CommandRequestHeader 145 146 Body struct { 147 StartMinimized uint8 148 ProgramPathLength uint32 149 ArgumentsLength uint32 150 WorkingDirLength uint32 151 NumEnvVars uint32 152 EnvVarLength uint32 153 } 154 155 ProgramPath string 156 Arguments string 157 WorkingDir string 158 EnvVars []string 159 } 160 161 // MarshalBinary implements the encoding.BinaryMarshaler interface 162 func (r *StartProgramRequest) MarshalBinary() ([]byte, error) { 163 var env bytes.Buffer 164 165 if n := len(r.EnvVars); n != 0 { 166 for _, e := range r.EnvVars { 167 _, _ = env.Write([]byte(e)) 168 _ = env.WriteByte(0) 169 } 170 r.Body.NumEnvVars = uint32(n) 171 r.Body.EnvVarLength = uint32(env.Len()) 172 } 173 174 var fields []string 175 176 add := func(s string, l *uint32) { 177 if n := len(s); n != 0 { 178 *l = uint32(n) + 1 179 fields = append(fields, s) 180 } 181 } 182 183 add(r.ProgramPath, &r.Body.ProgramPathLength) 184 add(r.Arguments, &r.Body.ArgumentsLength) 185 add(r.WorkingDir, &r.Body.WorkingDirLength) 186 187 buf := new(bytes.Buffer) 188 189 _ = binary.Write(buf, binary.LittleEndian, &r.Body) 190 191 for _, val := range fields { 192 _, _ = buf.Write([]byte(val)) 193 _ = buf.WriteByte(0) 194 } 195 196 if r.Body.EnvVarLength != 0 { 197 _, _ = buf.Write(env.Bytes()) 198 } 199 200 return buf.Bytes(), nil 201 } 202 203 // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface 204 func (r *StartProgramRequest) UnmarshalBinary(data []byte) error { 205 buf := bytes.NewBuffer(data) 206 207 err := binary.Read(buf, binary.LittleEndian, &r.Body) 208 if err != nil { 209 return err 210 } 211 212 fields := []struct { 213 len uint32 214 val *string 215 }{ 216 {r.Body.ProgramPathLength, &r.ProgramPath}, 217 {r.Body.ArgumentsLength, &r.Arguments}, 218 {r.Body.WorkingDirLength, &r.WorkingDir}, 219 } 220 221 for _, field := range fields { 222 if field.len == 0 { 223 continue 224 } 225 226 x := buf.Next(int(field.len)) 227 *field.val = string(bytes.TrimRight(x, "\x00")) 228 } 229 230 for i := 0; i < int(r.Body.NumEnvVars); i++ { 231 env, rerr := buf.ReadString(0) 232 if rerr != nil { 233 return rerr 234 } 235 236 env = env[:len(env)-1] // discard NULL terminator 237 r.EnvVars = append(r.EnvVars, env) 238 } 239 240 return nil 241 } 242 243 type KillProcessRequest struct { 244 CommandRequestHeader 245 246 Body struct { 247 Pid int64 248 Options uint32 249 } 250 } 251 252 // MarshalBinary implements the encoding.BinaryMarshaler interface 253 func (r *KillProcessRequest) MarshalBinary() ([]byte, error) { 254 buf := new(bytes.Buffer) 255 256 _ = binary.Write(buf, binary.LittleEndian, &r.Body) 257 258 return buf.Bytes(), nil 259 } 260 261 // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface 262 func (r *KillProcessRequest) UnmarshalBinary(data []byte) error { 263 buf := bytes.NewBuffer(data) 264 265 return binary.Read(buf, binary.LittleEndian, &r.Body) 266 } 267 268 type ListProcessesRequest struct { 269 CommandRequestHeader 270 271 Body struct { 272 Key uint32 273 Offset uint32 274 NumPids uint32 275 } 276 277 Pids []int64 278 } 279 280 // MarshalBinary implements the encoding.BinaryMarshaler interface 281 func (r *ListProcessesRequest) MarshalBinary() ([]byte, error) { 282 r.Body.NumPids = uint32(len(r.Pids)) 283 284 buf := new(bytes.Buffer) 285 286 _ = binary.Write(buf, binary.LittleEndian, &r.Body) 287 288 for _, pid := range r.Pids { 289 _ = binary.Write(buf, binary.LittleEndian, &pid) 290 } 291 292 return buf.Bytes(), nil 293 } 294 295 // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface 296 func (r *ListProcessesRequest) UnmarshalBinary(data []byte) error { 297 buf := bytes.NewBuffer(data) 298 299 err := binary.Read(buf, binary.LittleEndian, &r.Body) 300 if err != nil { 301 return err 302 } 303 304 r.Pids = make([]int64, r.Body.NumPids) 305 306 for i := uint32(0); i < r.Body.NumPids; i++ { 307 err := binary.Read(buf, binary.LittleEndian, &r.Pids[i]) 308 if err != nil { 309 return err 310 } 311 } 312 313 return nil 314 } 315 316 type ReadEnvironmentVariablesRequest struct { 317 CommandRequestHeader 318 319 Body struct { 320 NumNames uint32 321 NamesLength uint32 322 } 323 324 Names []string 325 } 326 327 // MarshalBinary implements the encoding.BinaryMarshaler interface 328 func (r *ReadEnvironmentVariablesRequest) MarshalBinary() ([]byte, error) { 329 var env bytes.Buffer 330 331 if n := len(r.Names); n != 0 { 332 for _, e := range r.Names { 333 _, _ = env.Write([]byte(e)) 334 _ = env.WriteByte(0) 335 } 336 r.Body.NumNames = uint32(n) 337 r.Body.NamesLength = uint32(env.Len()) 338 } 339 340 buf := new(bytes.Buffer) 341 342 _ = binary.Write(buf, binary.LittleEndian, &r.Body) 343 344 if r.Body.NamesLength != 0 { 345 _, _ = buf.Write(env.Bytes()) 346 } 347 348 return buf.Bytes(), nil 349 } 350 351 // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface 352 func (r *ReadEnvironmentVariablesRequest) UnmarshalBinary(data []byte) error { 353 buf := bytes.NewBuffer(data) 354 355 err := binary.Read(buf, binary.LittleEndian, &r.Body) 356 if err != nil { 357 return err 358 } 359 360 for i := 0; i < int(r.Body.NumNames); i++ { 361 env, rerr := buf.ReadString(0) 362 if rerr != nil { 363 return rerr 364 } 365 366 env = env[:len(env)-1] // discard NULL terminator 367 r.Names = append(r.Names, env) 368 } 369 370 return nil 371 } 372 373 type CreateTempFileRequest struct { 374 CommandRequestHeader 375 376 Body struct { 377 Options int32 378 FilePrefixLength uint32 379 FileSuffixLength uint32 380 DirectoryPathLength uint32 381 PropertyListLength uint32 382 } 383 384 FilePrefix string 385 FileSuffix string 386 DirectoryPath string 387 } 388 389 // MarshalBinary implements the encoding.BinaryMarshaler interface 390 func (r *CreateTempFileRequest) MarshalBinary() ([]byte, error) { 391 var fields []string 392 393 add := func(s string, l *uint32) { 394 *l = uint32(len(s)) // NOTE: NULL byte is not included in the length fields on the wire 395 fields = append(fields, s) 396 } 397 398 add(r.FilePrefix, &r.Body.FilePrefixLength) 399 add(r.FileSuffix, &r.Body.FileSuffixLength) 400 add(r.DirectoryPath, &r.Body.DirectoryPathLength) 401 402 buf := new(bytes.Buffer) 403 404 _ = binary.Write(buf, binary.LittleEndian, &r.Body) 405 406 for _, val := range fields { 407 _, _ = buf.Write([]byte(val)) 408 _ = buf.WriteByte(0) 409 } 410 411 return buf.Bytes(), nil 412 } 413 414 // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface 415 func (r *CreateTempFileRequest) UnmarshalBinary(data []byte) error { 416 buf := bytes.NewBuffer(data) 417 418 err := binary.Read(buf, binary.LittleEndian, &r.Body) 419 if err != nil { 420 return err 421 } 422 423 fields := []struct { 424 len uint32 425 val *string 426 }{ 427 {r.Body.FilePrefixLength, &r.FilePrefix}, 428 {r.Body.FileSuffixLength, &r.FileSuffix}, 429 {r.Body.DirectoryPathLength, &r.DirectoryPath}, 430 } 431 432 for _, field := range fields { 433 field.len++ // NOTE: NULL byte is not included in the length fields on the wire 434 435 x := buf.Next(int(field.len)) 436 *field.val = string(bytes.TrimRight(x, "\x00")) 437 } 438 439 return nil 440 } 441 442 type FileRequest struct { 443 CommandRequestHeader 444 445 Body struct { 446 FileOptions int32 447 GuestPathNameLength uint32 448 } 449 450 GuestPathName string 451 } 452 453 // MarshalBinary implements the encoding.BinaryMarshaler interface 454 func (r *FileRequest) MarshalBinary() ([]byte, error) { 455 buf := new(bytes.Buffer) 456 457 r.Body.GuestPathNameLength = uint32(len(r.GuestPathName)) 458 459 _ = binary.Write(buf, binary.LittleEndian, &r.Body) 460 461 _, _ = buf.WriteString(r.GuestPathName) 462 _ = buf.WriteByte(0) 463 464 return buf.Bytes(), nil 465 } 466 467 // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface 468 func (r *FileRequest) UnmarshalBinary(data []byte) error { 469 buf := bytes.NewBuffer(data) 470 471 err := binary.Read(buf, binary.LittleEndian, &r.Body) 472 if err != nil { 473 return err 474 } 475 476 name := buf.Next(int(r.Body.GuestPathNameLength)) 477 r.GuestPathName = string(bytes.TrimRight(name, "\x00")) 478 479 return nil 480 } 481 482 type DirRequest struct { 483 CommandRequestHeader 484 485 Body struct { 486 FileOptions int32 487 GuestPathNameLength uint32 488 FilePropertiesLength uint32 489 Recursive bool 490 } 491 492 GuestPathName string 493 } 494 495 // MarshalBinary implements the encoding.BinaryMarshaler interface 496 func (r *DirRequest) MarshalBinary() ([]byte, error) { 497 buf := new(bytes.Buffer) 498 499 r.Body.GuestPathNameLength = uint32(len(r.GuestPathName)) 500 501 _ = binary.Write(buf, binary.LittleEndian, &r.Body) 502 503 _, _ = buf.WriteString(r.GuestPathName) 504 _ = buf.WriteByte(0) 505 506 return buf.Bytes(), nil 507 } 508 509 // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface 510 func (r *DirRequest) UnmarshalBinary(data []byte) error { 511 buf := bytes.NewBuffer(data) 512 513 err := binary.Read(buf, binary.LittleEndian, &r.Body) 514 if err != nil { 515 return err 516 } 517 518 name := buf.Next(int(r.Body.GuestPathNameLength)) 519 r.GuestPathName = string(bytes.TrimRight(name, "\x00")) 520 521 return nil 522 } 523 524 type RenameFileRequest struct { 525 CommandRequestHeader 526 527 Body struct { 528 CopyFileOptions int32 529 OldPathNameLength uint32 530 NewPathNameLength uint32 531 FilePropertiesLength uint32 532 Overwrite bool 533 } 534 535 OldPathName string 536 NewPathName string 537 } 538 539 // MarshalBinary implements the encoding.BinaryMarshaler interface 540 func (r *RenameFileRequest) MarshalBinary() ([]byte, error) { 541 var fields []string 542 543 add := func(s string, l *uint32) { 544 *l = uint32(len(s)) // NOTE: NULL byte is not included in the length fields on the wire 545 fields = append(fields, s) 546 } 547 548 add(r.OldPathName, &r.Body.OldPathNameLength) 549 add(r.NewPathName, &r.Body.NewPathNameLength) 550 551 buf := new(bytes.Buffer) 552 553 _ = binary.Write(buf, binary.LittleEndian, &r.Body) 554 555 for _, val := range fields { 556 _, _ = buf.Write([]byte(val)) 557 _ = buf.WriteByte(0) 558 } 559 560 return buf.Bytes(), nil 561 } 562 563 // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface 564 func (r *RenameFileRequest) UnmarshalBinary(data []byte) error { 565 buf := bytes.NewBuffer(data) 566 567 err := binary.Read(buf, binary.LittleEndian, &r.Body) 568 if err != nil { 569 return err 570 } 571 572 fields := []struct { 573 len uint32 574 val *string 575 }{ 576 {r.Body.OldPathNameLength, &r.OldPathName}, 577 {r.Body.NewPathNameLength, &r.NewPathName}, 578 } 579 580 for _, field := range fields { 581 field.len++ // NOTE: NULL byte is not included in the length fields on the wire 582 583 x := buf.Next(int(field.len)) 584 *field.val = string(bytes.TrimRight(x, "\x00")) 585 } 586 587 return nil 588 } 589 590 type ListFilesRequest struct { 591 CommandRequestHeader 592 593 Body struct { 594 FileOptions int32 595 GuestPathNameLength uint32 596 PatternLength uint32 597 Index int32 598 MaxResults int32 599 Offset uint64 600 } 601 602 GuestPathName string 603 Pattern string 604 } 605 606 // MarshalBinary implements the encoding.BinaryMarshaler interface 607 func (r *ListFilesRequest) MarshalBinary() ([]byte, error) { 608 var fields []string 609 610 add := func(s string, l *uint32) { 611 if n := len(s); n != 0 { 612 *l = uint32(n) + 1 613 fields = append(fields, s) 614 } 615 } 616 617 add(r.GuestPathName, &r.Body.GuestPathNameLength) 618 add(r.Pattern, &r.Body.PatternLength) 619 620 buf := new(bytes.Buffer) 621 622 _ = binary.Write(buf, binary.LittleEndian, &r.Body) 623 624 for _, val := range fields { 625 _, _ = buf.Write([]byte(val)) 626 _ = buf.WriteByte(0) 627 } 628 629 return buf.Bytes(), nil 630 } 631 632 // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface 633 func (r *ListFilesRequest) UnmarshalBinary(data []byte) error { 634 buf := bytes.NewBuffer(data) 635 636 err := binary.Read(buf, binary.LittleEndian, &r.Body) 637 if err != nil { 638 return err 639 } 640 641 fields := []struct { 642 len uint32 643 val *string 644 }{ 645 {r.Body.GuestPathNameLength, &r.GuestPathName}, 646 {r.Body.PatternLength, &r.Pattern}, 647 } 648 649 for _, field := range fields { 650 if field.len == 0 { 651 continue 652 } 653 654 x := buf.Next(int(field.len)) 655 *field.val = string(bytes.TrimRight(x, "\x00")) 656 } 657 658 return nil 659 } 660 661 type SetGuestFileAttributesRequest struct { 662 CommandRequestHeader 663 664 Body struct { 665 FileOptions int32 666 AccessTime int64 667 ModificationTime int64 668 OwnerID int32 669 GroupID int32 670 Permissions int32 671 Hidden bool 672 ReadOnly bool 673 GuestPathNameLength uint32 674 } 675 676 GuestPathName string 677 } 678 679 // MarshalBinary implements the encoding.BinaryMarshaler interface 680 func (r *SetGuestFileAttributesRequest) MarshalBinary() ([]byte, error) { 681 buf := new(bytes.Buffer) 682 683 r.Body.GuestPathNameLength = uint32(len(r.GuestPathName)) 684 685 _ = binary.Write(buf, binary.LittleEndian, &r.Body) 686 687 _, _ = buf.WriteString(r.GuestPathName) 688 _ = buf.WriteByte(0) 689 690 return buf.Bytes(), nil 691 } 692 693 // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface 694 func (r *SetGuestFileAttributesRequest) UnmarshalBinary(data []byte) error { 695 buf := bytes.NewBuffer(data) 696 697 err := binary.Read(buf, binary.LittleEndian, &r.Body) 698 if err != nil { 699 return err 700 } 701 702 name := buf.Next(int(r.Body.GuestPathNameLength)) 703 r.GuestPathName = string(bytes.TrimRight(name, "\x00")) 704 705 return nil 706 } 707 708 func (r *SetGuestFileAttributesRequest) IsSet(opt int32) bool { 709 return r.Body.FileOptions&opt == opt 710 } 711 712 type CommandHgfsSendPacket struct { 713 CommandRequestHeader 714 715 Body struct { 716 PacketSize uint32 717 Timeout int32 718 } 719 720 Packet []byte 721 } 722 723 // MarshalBinary implements the encoding.BinaryMarshaler interface 724 func (r *CommandHgfsSendPacket) MarshalBinary() ([]byte, error) { 725 buf := new(bytes.Buffer) 726 727 _ = binary.Write(buf, binary.LittleEndian, &r.Body) 728 729 _, _ = buf.Write(r.Packet) 730 731 return buf.Bytes(), nil 732 } 733 734 // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface 735 func (r *CommandHgfsSendPacket) UnmarshalBinary(data []byte) error { 736 buf := bytes.NewBuffer(data) 737 738 err := binary.Read(buf, binary.LittleEndian, &r.Body) 739 if err != nil { 740 return err 741 } 742 743 r.Packet = buf.Next(int(r.Body.PacketSize)) 744 745 return nil 746 } 747 748 type InitiateFileTransferToGuestRequest struct { 749 CommandRequestHeader 750 751 Body struct { 752 Options int32 753 GuestPathNameLength uint32 754 Overwrite bool 755 } 756 757 GuestPathName string 758 } 759 760 // MarshalBinary implements the encoding.BinaryMarshaler interface 761 func (r *InitiateFileTransferToGuestRequest) MarshalBinary() ([]byte, error) { 762 buf := new(bytes.Buffer) 763 764 r.Body.GuestPathNameLength = uint32(len(r.GuestPathName)) 765 766 _ = binary.Write(buf, binary.LittleEndian, &r.Body) 767 768 _, _ = buf.WriteString(r.GuestPathName) 769 _ = buf.WriteByte(0) 770 771 return buf.Bytes(), nil 772 } 773 774 // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface 775 func (r *InitiateFileTransferToGuestRequest) UnmarshalBinary(data []byte) error { 776 buf := bytes.NewBuffer(data) 777 778 err := binary.Read(buf, binary.LittleEndian, &r.Body) 779 if err != nil { 780 return err 781 } 782 783 name := buf.Next(int(r.Body.GuestPathNameLength)) 784 r.GuestPathName = string(bytes.TrimRight(name, "\x00")) 785 786 return nil 787 } 788 789 type UserCredentialNamePassword struct { 790 Body struct { 791 NameLength uint32 792 PasswordLength uint32 793 } 794 795 Name string 796 Password string 797 } 798 799 func (c *UserCredentialNamePassword) UnmarshalBinary(data []byte) error { 800 buf := bytes.NewBuffer(bytes.TrimRight(data, "\x00")) 801 802 err := binary.Read(buf, binary.LittleEndian, &c.Body) 803 if err != nil { 804 return err 805 } 806 807 str, err := base64.StdEncoding.DecodeString(buf.String()) 808 if err != nil { 809 return err 810 } 811 812 c.Name = string(str[0:c.Body.NameLength]) 813 c.Password = string(str[c.Body.NameLength+1 : len(str)-1]) 814 815 return nil 816 } 817 818 func (c *UserCredentialNamePassword) MarshalBinary() ([]byte, error) { 819 buf := new(bytes.Buffer) 820 821 c.Body.NameLength = uint32(len(c.Name)) 822 c.Body.PasswordLength = uint32(len(c.Password)) 823 824 _ = binary.Write(buf, binary.LittleEndian, &c.Body) 825 826 src := append([]byte(c.Name+"\x00"), []byte(c.Password+"\x00")...) 827 828 enc := base64.StdEncoding 829 pwd := make([]byte, enc.EncodedLen(len(src))) 830 enc.Encode(pwd, src) 831 _, _ = buf.Write(pwd) 832 _ = buf.WriteByte(0) 833 834 return buf.Bytes(), nil 835 }