diff options
Diffstat (limited to 'recipes-containers/vcontainer/files/vrunner-backend-xen.sh')
| -rw-r--r-- | recipes-containers/vcontainer/files/vrunner-backend-xen.sh | 301 |
1 files changed, 258 insertions, 43 deletions
diff --git a/recipes-containers/vcontainer/files/vrunner-backend-xen.sh b/recipes-containers/vcontainer/files/vrunner-backend-xen.sh index 9f423dc6..55e87bd1 100644 --- a/recipes-containers/vcontainer/files/vrunner-backend-xen.sh +++ b/recipes-containers/vcontainer/files/vrunner-backend-xen.sh | |||
| @@ -13,8 +13,8 @@ | |||
| 13 | # Key differences from QEMU backend: | 13 | # Key differences from QEMU backend: |
| 14 | # - Block devices appear as /dev/xvd* instead of /dev/vd* | 14 | # - Block devices appear as /dev/xvd* instead of /dev/vd* |
| 15 | # - Network uses bridge + iptables NAT instead of QEMU slirp | 15 | # - Network uses bridge + iptables NAT instead of QEMU slirp |
| 16 | # - Console uses PV console (hvc0/hvc1) instead of virtio-serial | 16 | # - Console uses PV console (hvc0) with serial='pty' for PTY on Dom0 |
| 17 | # - 9p file sharing uses trans=xen instead of trans=virtio | 17 | # - Daemon IPC uses direct PTY I/O (no socat bridge needed) |
| 18 | # - VM tracking uses domain name instead of PID | 18 | # - VM tracking uses domain name instead of PID |
| 19 | 19 | ||
| 20 | # ============================================================================ | 20 | # ============================================================================ |
| @@ -43,8 +43,12 @@ hv_setup_arch() { | |||
| 43 | ;; | 43 | ;; |
| 44 | esac | 44 | esac |
| 45 | 45 | ||
| 46 | # Xen domain name (unique per instance) | 46 | # Xen domain name: use container name if set, otherwise PID-based |
| 47 | HV_DOMNAME="vxn-$$" | 47 | if [ -n "${CONTAINER_NAME:-}" ]; then |
| 48 | HV_DOMNAME="vxn-${CONTAINER_NAME}" | ||
| 49 | else | ||
| 50 | HV_DOMNAME="vxn-$$" | ||
| 51 | fi | ||
| 48 | HV_VM_PID="" | 52 | HV_VM_PID="" |
| 49 | 53 | ||
| 50 | # Xen domain config path (generated at runtime) | 54 | # Xen domain config path (generated at runtime) |
| @@ -97,6 +101,8 @@ hv_prepare_container() { | |||
| 97 | fi | 101 | fi |
| 98 | 102 | ||
| 99 | # Parse image name and any trailing command from "docker run [opts] <image> [cmd...]" | 103 | # Parse image name and any trailing command from "docker run [opts] <image> [cmd...]" |
| 104 | # Uses word counting + cut to extract the user command portion from the | ||
| 105 | # original string, preserving internal spaces (e.g., -c "echo hello && sleep 5"). | ||
| 100 | local args | 106 | local args |
| 101 | args=$(echo "$DOCKER_CMD" | sed 's/^[a-z]* run //') | 107 | args=$(echo "$DOCKER_CMD" | sed 's/^[a-z]* run //') |
| 102 | 108 | ||
| @@ -104,16 +110,16 @@ hv_prepare_container() { | |||
| 104 | local user_cmd="" | 110 | local user_cmd="" |
| 105 | local skip_next=false | 111 | local skip_next=false |
| 106 | local found_image=false | 112 | local found_image=false |
| 113 | local word_count=0 | ||
| 107 | for arg in $args; do | 114 | for arg in $args; do |
| 115 | if [ "$found_image" = "true" ]; then | ||
| 116 | break | ||
| 117 | fi | ||
| 118 | word_count=$((word_count + 1)) | ||
| 108 | if [ "$skip_next" = "true" ]; then | 119 | if [ "$skip_next" = "true" ]; then |
| 109 | skip_next=false | 120 | skip_next=false |
| 110 | continue | 121 | continue |
| 111 | fi | 122 | fi |
| 112 | if [ "$found_image" = "true" ]; then | ||
| 113 | # Everything after image name is the user command | ||
| 114 | user_cmd="$user_cmd $arg" | ||
| 115 | continue | ||
| 116 | fi | ||
| 117 | case "$arg" in | 123 | case "$arg" in |
| 118 | --rm|--detach|-d|-i|--interactive|-t|--tty|--privileged|-it) | 124 | --rm|--detach|-d|-i|--interactive|-t|--tty|--privileged|-it) |
| 119 | ;; | 125 | ;; |
| @@ -130,7 +136,21 @@ hv_prepare_container() { | |||
| 130 | ;; | 136 | ;; |
| 131 | esac | 137 | esac |
| 132 | done | 138 | done |
| 133 | user_cmd=$(echo "$user_cmd" | sed 's/^ *//') | 139 | |
| 140 | # Extract user command from original string using cut (preserves internal spaces) | ||
| 141 | if [ "$found_image" = "true" ]; then | ||
| 142 | user_cmd=$(echo "$args" | cut -d' ' -f$((word_count + 1))-) | ||
| 143 | # If cut returns the whole string (no fields after image), clear it | ||
| 144 | [ "$user_cmd" = "$args" ] && [ "$word_count" -ge "$(echo "$args" | wc -w)" ] && user_cmd="" | ||
| 145 | fi | ||
| 146 | |||
| 147 | # Strip /bin/sh -c wrapper from user command — the guest already wraps | ||
| 148 | # with /bin/sh -c in exec_in_container_background(), so passing it through | ||
| 149 | # would create nested shells with broken quoting. | ||
| 150 | case "$user_cmd" in | ||
| 151 | "/bin/sh -c "*) user_cmd="${user_cmd#/bin/sh -c }" ;; | ||
| 152 | "sh -c "*) user_cmd="${user_cmd#sh -c }" ;; | ||
| 153 | esac | ||
| 134 | 154 | ||
| 135 | if [ -z "$image" ]; then | 155 | if [ -z "$image" ]; then |
| 136 | log "DEBUG" "hv_prepare_container: no image found in DOCKER_CMD" | 156 | log "DEBUG" "hv_prepare_container: no image found in DOCKER_CMD" |
| @@ -245,22 +265,17 @@ hv_build_network_opts() { | |||
| 245 | hv_build_9p_opts() { | 265 | hv_build_9p_opts() { |
| 246 | local share_dir="$1" | 266 | local share_dir="$1" |
| 247 | local share_tag="$2" | 267 | local share_tag="$2" |
| 248 | # For Xen, 9p is configured in the domain config, not as a command-line option | 268 | # Xen 9p (xen_9pfsd) is not reliable in all environments (e.g. nested |
| 249 | # We accumulate these and include them in the config file | 269 | # QEMU→Xen). Keep the interface for future use but don't depend on it |
| 250 | _XEN_9P+=("{ 'tag': '$share_tag', 'path': '$share_dir', 'security_model': 'none' }") | 270 | # for daemon IPC — we use serial/PTY + socat instead. |
| 251 | # Return empty string since Xen doesn't use command-line 9p options | 271 | _XEN_9P+=("'tag=$share_tag,path=$share_dir,security_model=none,type=xen_9pfsd'") |
| 252 | echo "" | ||
| 253 | } | 272 | } |
| 254 | 273 | ||
| 255 | hv_build_daemon_opts() { | 274 | hv_build_daemon_opts() { |
| 256 | HV_DAEMON_OPTS="" | 275 | HV_DAEMON_OPTS="" |
| 257 | # Xen uses PV console (hvc1) for daemon command channel | 276 | # Xen daemon mode uses hvc0 with serial='pty' for bidirectional IPC. |
| 258 | # The init scripts already have /dev/hvc1 as a fallback for the daemon port | 277 | # The PTY is created on Dom0 and bridged to a Unix socket via socat. |
| 259 | # No extra config needed - hvc1 is automatically available in PV guests | 278 | # This is the same approach runx used (serial_start). |
| 260 | # | ||
| 261 | # For the host-side socket, we'll use xl console with a pipe | ||
| 262 | # The daemon socket is handled differently for Xen: | ||
| 263 | # We create a socat bridge between the Xen console and a unix socket | ||
| 264 | } | 279 | } |
| 265 | 280 | ||
| 266 | hv_build_vm_cmd() { | 281 | hv_build_vm_cmd() { |
| @@ -316,6 +331,8 @@ extra = "console=hvc0 quiet loglevel=0 init=/init vcontainer.blk=xvd vcontainer. | |||
| 316 | disk = [ $disk_array ] | 331 | disk = [ $disk_array ] |
| 317 | vif = [ $vif_array ] | 332 | vif = [ $vif_array ] |
| 318 | 333 | ||
| 334 | serial = 'pty' | ||
| 335 | |||
| 319 | on_poweroff = "destroy" | 336 | on_poweroff = "destroy" |
| 320 | on_reboot = "destroy" | 337 | on_reboot = "destroy" |
| 321 | on_crash = "destroy" | 338 | on_crash = "destroy" |
| @@ -353,16 +370,36 @@ hv_start_vm_background() { | |||
| 353 | # Create the domain | 370 | # Create the domain |
| 354 | xl create "$HV_XEN_CFG" >> "$log_file" 2>&1 | 371 | xl create "$HV_XEN_CFG" >> "$log_file" 2>&1 |
| 355 | 372 | ||
| 356 | # For background monitoring, we need a PID-like concept | 373 | # Xen domains don't have a PID on Dom0 — xl manages them by name. |
| 357 | # Use the domain name as VM identifier | 374 | # For daemon mode, start a lightweight monitor process that stays alive |
| 358 | HV_VM_PID="$$" # Use our PID as a placeholder for compatibility | 375 | # while the domain exists. This gives vcontainer-common.sh a real PID |
| 376 | # to check in /proc/$pid for daemon_is_running(). | ||
| 377 | HV_VM_PID="$$" | ||
| 359 | 378 | ||
| 360 | if [ "$DAEMON_MODE" = "start" ]; then | 379 | if [ "$DAEMON_MODE" = "start" ]; then |
| 361 | # Daemon mode: bridge xl console (hvc1) to the daemon unix socket | 380 | # Daemon mode: get the domain's hvc0 PTY for direct I/O. |
| 362 | # xl console -n 1 connects to the second PV console (hvc1) | 381 | # serial='pty' in the xl config creates a PTY on Dom0. |
| 363 | socat "UNIX-LISTEN:$DAEMON_SOCKET,fork" "EXEC:xl console -n 1 $HV_DOMNAME" & | 382 | # We read/write this PTY directly — no socat bridge needed. |
| 364 | _XEN_SOCAT_PID=$! | 383 | local domid |
| 365 | log "DEBUG" "Console-socket bridge started (PID: $_XEN_SOCAT_PID)" | 384 | domid=$(xl domid "$HV_DOMNAME" 2>/dev/null) |
| 385 | if [ -n "$domid" ]; then | ||
| 386 | _XEN_PTY=$(xenstore-read "/local/domain/$domid/console/tty" 2>/dev/null) | ||
| 387 | if [ -n "$_XEN_PTY" ]; then | ||
| 388 | log "DEBUG" "Domain $HV_DOMNAME (domid $domid) console PTY: $_XEN_PTY" | ||
| 389 | echo "$_XEN_PTY" > "$DAEMON_SOCKET_DIR/daemon.pty" | ||
| 390 | else | ||
| 391 | log "ERROR" "Could not read console PTY from xenstore for domid $domid" | ||
| 392 | fi | ||
| 393 | else | ||
| 394 | log "ERROR" "Could not get domid for $HV_DOMNAME" | ||
| 395 | fi | ||
| 396 | |||
| 397 | # Monitor process: stays alive while domain exists. | ||
| 398 | # vcontainer-common.sh checks /proc/$pid → alive means daemon running. | ||
| 399 | # When domain dies (xl destroy, guest reboot), monitor exits. | ||
| 400 | local _domname="$HV_DOMNAME" | ||
| 401 | (while xl list "$_domname" >/dev/null 2>&1; do sleep 10; done) & | ||
| 402 | HV_VM_PID=$! | ||
| 366 | else | 403 | else |
| 367 | # Ephemeral mode: capture guest console (hvc0) to log file | 404 | # Ephemeral mode: capture guest console (hvc0) to log file |
| 368 | # so the monitoring loop in vrunner.sh can see output markers | 405 | # so the monitoring loop in vrunner.sh can see output markers |
| @@ -413,11 +450,6 @@ hv_destroy_vm() { | |||
| 413 | if [ -n "${_XEN_CONSOLE_PID:-}" ]; then | 450 | if [ -n "${_XEN_CONSOLE_PID:-}" ]; then |
| 414 | kill $_XEN_CONSOLE_PID 2>/dev/null || true | 451 | kill $_XEN_CONSOLE_PID 2>/dev/null || true |
| 415 | fi | 452 | fi |
| 416 | |||
| 417 | # Clean up console bridge (daemon mode) | ||
| 418 | if [ -n "${_XEN_SOCAT_PID:-}" ]; then | ||
| 419 | kill $_XEN_SOCAT_PID 2>/dev/null || true | ||
| 420 | fi | ||
| 421 | } | 453 | } |
| 422 | 454 | ||
| 423 | hv_get_vm_id() { | 455 | hv_get_vm_id() { |
| @@ -524,6 +556,181 @@ hv_daemon_is_running() { | |||
| 524 | [ -n "$HV_DOMNAME" ] && xl list "$HV_DOMNAME" >/dev/null 2>&1 | 556 | [ -n "$HV_DOMNAME" ] && xl list "$HV_DOMNAME" >/dev/null 2>&1 |
| 525 | } | 557 | } |
| 526 | 558 | ||
| 559 | # PTY-based daemon readiness check. | ||
| 560 | # The guest emits ===PONG=== on hvc0 at daemon startup and in response to PING. | ||
| 561 | # We read the PTY (saved by hv_start_vm_background) looking for this marker. | ||
| 562 | hv_daemon_ping() { | ||
| 563 | local pty_file="$DAEMON_SOCKET_DIR/daemon.pty" | ||
| 564 | [ -f "$pty_file" ] || return 1 | ||
| 565 | local pty | ||
| 566 | pty=$(cat "$pty_file") | ||
| 567 | [ -c "$pty" ] || return 1 | ||
| 568 | |||
| 569 | # Open PTY for read/write on fd 3 | ||
| 570 | exec 3<>"$pty" | ||
| 571 | |||
| 572 | # Send PING (guest also emits PONG at startup) | ||
| 573 | echo "===PING===" >&3 | ||
| 574 | |||
| 575 | # Read lines looking for PONG (skip boot messages, log lines) | ||
| 576 | local line | ||
| 577 | while IFS= read -t 5 -r line <&3; do | ||
| 578 | line=$(echo "$line" | tr -d '\r') | ||
| 579 | case "$line" in | ||
| 580 | *"===PONG==="*) exec 3<&- 3>&-; return 0 ;; | ||
| 581 | esac | ||
| 582 | done | ||
| 583 | |||
| 584 | exec 3<&- 3>&- 2>/dev/null | ||
| 585 | return 1 | ||
| 586 | } | ||
| 587 | |||
| 588 | # PTY-based daemon command send. | ||
| 589 | # Writes base64-encoded command to PTY, reads response with markers. | ||
| 590 | # Same protocol as socat-based daemon_send in vrunner.sh. | ||
| 591 | hv_daemon_send() { | ||
| 592 | local cmd="$1" | ||
| 593 | local pty_file="$DAEMON_SOCKET_DIR/daemon.pty" | ||
| 594 | [ -f "$pty_file" ] || { log "ERROR" "No daemon PTY file"; return 1; } | ||
| 595 | local pty | ||
| 596 | pty=$(cat "$pty_file") | ||
| 597 | [ -c "$pty" ] || { log "ERROR" "PTY $pty not a character device"; return 1; } | ||
| 598 | |||
| 599 | # Update activity timestamp | ||
| 600 | touch "$DAEMON_SOCKET_DIR/activity" 2>/dev/null || true | ||
| 601 | |||
| 602 | # Encode command | ||
| 603 | local cmd_b64 | ||
| 604 | cmd_b64=$(echo -n "$cmd" | base64 -w0) | ||
| 605 | |||
| 606 | # Open PTY for read/write on fd 3 | ||
| 607 | exec 3<>"$pty" | ||
| 608 | |||
| 609 | # Drain any pending output (boot messages, prior log lines) | ||
| 610 | while IFS= read -t 0.5 -r _discard <&3; do :; done | ||
| 611 | |||
| 612 | # Send command | ||
| 613 | echo "$cmd_b64" >&3 | ||
| 614 | |||
| 615 | # Read response with markers | ||
| 616 | local EXIT_CODE=0 | ||
| 617 | local in_output=false | ||
| 618 | local line | ||
| 619 | while IFS= read -t 60 -r line <&3; do | ||
| 620 | line=$(echo "$line" | tr -d '\r') | ||
| 621 | case "$line" in | ||
| 622 | *"===OUTPUT_START==="*) | ||
| 623 | in_output=true | ||
| 624 | ;; | ||
| 625 | *"===OUTPUT_END==="*) | ||
| 626 | in_output=false | ||
| 627 | ;; | ||
| 628 | *"===EXIT_CODE="*"==="*) | ||
| 629 | EXIT_CODE=$(echo "$line" | sed 's/.*===EXIT_CODE=\([0-9]*\)===/\1/') | ||
| 630 | ;; | ||
| 631 | *"===END==="*) | ||
| 632 | break | ||
| 633 | ;; | ||
| 634 | *) | ||
| 635 | if [ "$in_output" = "true" ]; then | ||
| 636 | echo "$line" | ||
| 637 | fi | ||
| 638 | ;; | ||
| 639 | esac | ||
| 640 | done | ||
| 641 | |||
| 642 | exec 3<&- 3>&- 2>/dev/null | ||
| 643 | return ${EXIT_CODE:-0} | ||
| 644 | } | ||
| 645 | |||
| 646 | # Run a container in a memres (persistent) DomU. | ||
| 647 | # Hot-plugs an input disk, sends ===RUN_CONTAINER=== command via PTY, | ||
| 648 | # reads output, detaches disk. | ||
| 649 | # Usage: hv_daemon_run_container <resolved_cmd> <input_disk_path> | ||
| 650 | hv_daemon_run_container() { | ||
| 651 | local cmd="$1" | ||
| 652 | local input_disk="$2" | ||
| 653 | |||
| 654 | hv_daemon_load_state | ||
| 655 | if [ -z "$HV_DOMNAME" ]; then | ||
| 656 | log "ERROR" "No memres domain name" | ||
| 657 | return 1 | ||
| 658 | fi | ||
| 659 | |||
| 660 | # Hot-plug the input disk as xvdb (read-only) | ||
| 661 | if [ -n "$input_disk" ] && [ -f "$input_disk" ]; then | ||
| 662 | log "INFO" "Hot-plugging container disk to $HV_DOMNAME..." | ||
| 663 | xl block-attach "$HV_DOMNAME" "format=raw,vdev=xvdb,access=ro,target=$input_disk" 2>/dev/null || { | ||
| 664 | log "ERROR" "Failed to attach block device" | ||
| 665 | return 1 | ||
| 666 | } | ||
| 667 | sleep 1 # Let the kernel register the device | ||
| 668 | fi | ||
| 669 | |||
| 670 | # Build the command line: ===RUN_CONTAINER===<cmd_b64> | ||
| 671 | local raw_line="===RUN_CONTAINER===" | ||
| 672 | if [ -n "$cmd" ]; then | ||
| 673 | raw_line="${raw_line}$(echo -n "$cmd" | base64 -w0)" | ||
| 674 | fi | ||
| 675 | |||
| 676 | # Send via PTY and read response (same protocol as hv_daemon_send) | ||
| 677 | local pty_file="$DAEMON_SOCKET_DIR/daemon.pty" | ||
| 678 | [ -f "$pty_file" ] || { log "ERROR" "No daemon PTY file"; return 1; } | ||
| 679 | local pty | ||
| 680 | pty=$(cat "$pty_file") | ||
| 681 | [ -c "$pty" ] || { log "ERROR" "PTY $pty not a character device"; return 1; } | ||
| 682 | |||
| 683 | touch "$DAEMON_SOCKET_DIR/activity" 2>/dev/null || true | ||
| 684 | |||
| 685 | exec 3<>"$pty" | ||
| 686 | |||
| 687 | # Drain pending output | ||
| 688 | while IFS= read -t 0.5 -r _discard <&3; do :; done | ||
| 689 | |||
| 690 | # Send command | ||
| 691 | echo "$raw_line" >&3 | ||
| 692 | |||
| 693 | # Read response with markers | ||
| 694 | local EXIT_CODE=0 | ||
| 695 | local in_output=false | ||
| 696 | local line | ||
| 697 | while IFS= read -t 120 -r line <&3; do | ||
| 698 | line=$(echo "$line" | tr -d '\r') | ||
| 699 | case "$line" in | ||
| 700 | *"===OUTPUT_START==="*) | ||
| 701 | in_output=true | ||
| 702 | ;; | ||
| 703 | *"===OUTPUT_END==="*) | ||
| 704 | in_output=false | ||
| 705 | ;; | ||
| 706 | *"===EXIT_CODE="*"==="*) | ||
| 707 | EXIT_CODE=$(echo "$line" | sed 's/.*===EXIT_CODE=\([0-9]*\)===/\1/') | ||
| 708 | ;; | ||
| 709 | *"===ERROR==="*) | ||
| 710 | in_output=true | ||
| 711 | ;; | ||
| 712 | *"===END==="*) | ||
| 713 | break | ||
| 714 | ;; | ||
| 715 | *) | ||
| 716 | if [ "$in_output" = "true" ]; then | ||
| 717 | echo "$line" | ||
| 718 | fi | ||
| 719 | ;; | ||
| 720 | esac | ||
| 721 | done | ||
| 722 | |||
| 723 | exec 3<&- 3>&- 2>/dev/null | ||
| 724 | |||
| 725 | # Detach the input disk | ||
| 726 | if [ -n "$input_disk" ] && [ -f "$input_disk" ]; then | ||
| 727 | log "DEBUG" "Detaching container disk from $HV_DOMNAME..." | ||
| 728 | xl block-detach "$HV_DOMNAME" xvdb 2>/dev/null || true | ||
| 729 | fi | ||
| 730 | |||
| 731 | return ${EXIT_CODE:-0} | ||
| 732 | } | ||
| 733 | |||
| 527 | hv_daemon_stop() { | 734 | hv_daemon_stop() { |
| 528 | hv_daemon_load_state | 735 | hv_daemon_load_state |
| 529 | if [ -z "$HV_DOMNAME" ]; then | 736 | if [ -z "$HV_DOMNAME" ]; then |
| @@ -532,10 +739,15 @@ hv_daemon_stop() { | |||
| 532 | 739 | ||
| 533 | log "INFO" "Shutting down Xen domain $HV_DOMNAME..." | 740 | log "INFO" "Shutting down Xen domain $HV_DOMNAME..." |
| 534 | 741 | ||
| 535 | # Send shutdown command via socket first (graceful guest shutdown) | 742 | # Send shutdown command via PTY (graceful guest shutdown) |
| 536 | if [ -S "$DAEMON_SOCKET" ]; then | 743 | local pty_file="$DAEMON_SOCKET_DIR/daemon.pty" |
| 537 | echo "===SHUTDOWN===" | socat - "UNIX-CONNECT:$DAEMON_SOCKET" 2>/dev/null || true | 744 | if [ -f "$pty_file" ]; then |
| 538 | sleep 2 | 745 | local pty |
| 746 | pty=$(cat "$pty_file") | ||
| 747 | if [ -c "$pty" ]; then | ||
| 748 | echo "===SHUTDOWN===" > "$pty" 2>/dev/null || true | ||
| 749 | sleep 2 | ||
| 750 | fi | ||
| 539 | fi | 751 | fi |
| 540 | 752 | ||
| 541 | # Try graceful xl shutdown | 753 | # Try graceful xl shutdown |
| @@ -554,11 +766,14 @@ hv_daemon_stop() { | |||
| 554 | xl destroy "$HV_DOMNAME" 2>/dev/null || true | 766 | xl destroy "$HV_DOMNAME" 2>/dev/null || true |
| 555 | fi | 767 | fi |
| 556 | 768 | ||
| 557 | # Clean up console bridge | 769 | # Kill monitor process (PID stored in daemon.pid) |
| 558 | if [ -n "${_XEN_SOCAT_PID:-}" ]; then | 770 | local pid_file="$DAEMON_SOCKET_DIR/daemon.pid" |
| 559 | kill $_XEN_SOCAT_PID 2>/dev/null || true | 771 | if [ -f "$pid_file" ]; then |
| 772 | local mpid | ||
| 773 | mpid=$(cat "$pid_file" 2>/dev/null) | ||
| 774 | [ -n "$mpid" ] && kill "$mpid" 2>/dev/null || true | ||
| 560 | fi | 775 | fi |
| 561 | 776 | ||
| 562 | rm -f "$(_xen_domname_file)" | 777 | rm -f "$(_xen_domname_file)" "$pty_file" "$pid_file" |
| 563 | log "INFO" "Xen domain stopped" | 778 | log "INFO" "Xen domain stopped" |
| 564 | } | 779 | } |
