Skip to content
This repository
tree: 1cc739617e
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 183 lines (160 sloc) 7.613 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
# Copyright (C) 2010-2012 Cuckoo Sandbox Developers.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.

import os
import re
import time
import logging
import subprocess
import os.path

from lib.cuckoo.common.abstracts import MachineManager
from lib.cuckoo.common.exceptions import CuckooMachineError

log = logging.getLogger(__name__)


class VirtualBox(MachineManager):
    """Virtualization layer for VirtualBox."""

    # VM states.
    SAVED = "saved"
    RUNNING = "running"
    POWEROFF = "poweroff"
    ABORTED = "aborted"
    ERROR = "machete"

    def _initialize_check(self):
        """Runs all checks when a machine manager is initialized.
@raise CuckooMachineError: if VBoxManage is not found.
"""
        # VirtualBox specific checks.
        if not self.options.virtualbox.path:
            raise CuckooMachineError("VirtualBox VBoxManage path missing, please add it to configuration")
        if not os.path.exists(self.options.virtualbox.path):
            raise CuckooMachineError("VirtualBox VBoxManage not found in specified path %s" % self.options.virtualbox.path)
        if not self.options.virtualbox.timeout:
            raise CuckooMachineError("VirtualBox timeout setting not found, please add it to configuration")
        # Base checks.
        super(VirtualBox, self)._initialize_check()

    def start(self, label):
        """Start a virtual machine.
@param label: virtual machine name.
@raise CuckooMachineError: if unable to start.
"""
        log.debug("Starting vm %s" % label)

        if self._status(label) == self.RUNNING:
            raise CuckooMachineError("Trying to start an already started vm %s" % label)

        try:
            if subprocess.call([self.options.virtualbox.path, "snapshot", label, "restorecurrent"],
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE):
                raise CuckooMachineError("VBoxManage exited with error restoring the machine's snapshot")
        except OSError as e:
            raise CuckooMachineError("VBoxManage failed restoring the machine: %s" % e)
        self._wait_status(label, self.SAVED)

        try:
            subprocess.call([self.options.virtualbox.path,
                              "startvm",
                              label,
                              "--type",
                              self.options.virtualbox.mode],
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
        except OSError as e:
            raise CuckooMachineError("VBoxManage failed starting the machine in %s mode: %s"
                                     % (mode.upper(), e))
        self._wait_status(label, self.RUNNING)

    def stop(self, label):
        """Stops a virtual machine.
@param label: virtual machine name.
@raise CuckooMachineError: if unable to stop.
"""
        log.debug("Stopping vm %s" % label)

        if self._status(label) in [self.POWEROFF, self.ABORTED]:
            raise CuckooMachineError("Trying to stop an already stopped vm %s" % label)

        try:
            proc = subprocess.Popen([self.options.virtualbox.path, "controlvm", label, "poweroff"],
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE)
            # Sometimes VBoxManage stucks when stopping vm so we needed
            # to add a timeout and kill it after that.
            stop_me = 0
            while proc.poll() is None:
                if stop_me < self.options.virtualbox.timeout:
                    time.sleep(1)
                    stop_me += 1
                else:
                    log.debug("Stopping vm %s timeouted. Killing" % label)
                    proc.terminate()

            if proc.returncode != 0 and stop_me < self.options.virtualbox.timeout:
                log.debug("VBoxManage exited with error powering off the machine")
        except OSError as e:
            raise CuckooMachineError("VBoxManage failed powering off the machine: %s" % e)
        self._wait_status(label, [self.POWEROFF, self.ABORTED])

    def _list(self):
        """Lists virtual machines installed.
@return: virtual machine names list.
"""
        try:
            proc = subprocess.Popen([self.options.virtualbox.path, "list", "vms"],
                                    stdout=subprocess.PIPE,
                                    stderr=subprocess.PIPE)
            output = proc.communicate()
        except OSError as e:
            raise CuckooMachineError("VBoxManage error listing installed machines: %s" % e)

        machines = []
        for line in output[0].split("\n"):
            try:
                label = line.split('"')[1]
                if label == "<inaccessible>":
                    log.warning("Found an inaccessible vitual machine: please check his state")
                else:
                    machines.append(label)
            except IndexError:
                continue

        return machines

    def _status(self, label):
        """Gets current status of a vm.
@param label: virtual machine name.
@return: status string.
"""
        log.debug("Getting status for %s"% label)
        try:
            proc = subprocess.Popen([self.options.virtualbox.path,
                                     "showvminfo",
                                     label,
                                     "--machinereadable"],
                                     stdout=subprocess.PIPE,
                                     stderr=subprocess.PIPE)
            output, err = proc.communicate()

            if proc.returncode != 0:
                # It's quite common for virtualbox crap utility to exit with:
                # VBoxManage: error: Details: code E_ACCESSDENIED (0x80070005)
                # So we just log to debug this.
                log.debug("VBoxManage returns error checking status for machine %s: %s"
                                       % (label, err))
                return self.ERROR
        except OSError as e:
            log.warning("VBoxManage failed to check status for machine %s: %s"
                                     % (label, e))
            return self.ERROR

        for line in output.split("\n"):
            state = re.match(r"VMState=\"(\w+)\"", line, re.M|re.I)
            if state:
                status = state.group(1)
                log.debug("Machine %s status %s" % (label, status))
                return status.lower()
        raise CuckooMachineError("Unable to get status for %s" % label)

    def _wait_status(self, label, state):
        """Waits for a vm status.
@param label: virtual machine name.
@param state: virtual machine status, accepts more than one states in a list.
@raise CuckooMachineError: if default waiting timeout expire.
"""
        # This block was originally suggested by Loic Jaquemet.
        waitme = 0
        current = self._status(label)
        if isinstance(state, str):
            state = [state]
        while current not in state:
            log.debug("Waiting %i cuckooseconds for vm %s to switch to status %s" % (waitme, label, state))
            if waitme > int(self.options.virtualbox.timeout):
                raise CuckooMachineError("Waiting too much for vm %s status change. Please check manually" % label)
            time.sleep(1)
            waitme += 1
            current = self._status(label)
Something went wrong with that request. Please try again.