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 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
|
# Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2 of the
# License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
# 02110-1301 USA
import os
import sys
import time
import stat
from workbench.tcp_utils import SocketClient
from workbench.os_utils import FileUtils, OSUtils
import subprocess
# The script is executed with elevated privileges which means it is
# being executed on a clean environment on the admin user and so it is
# not possible to send back the command output
#
# It uses a socket client to send the output to a command listener
# Which requires authentication (done with the handshake logic)
# and also a way to tell the listener that the command has completed
# Retrieves the parameters
params = ' '.join(sys.argv[1:])
port, handshake, done_key, command = params.split(' ', 3)
class CommandProcessor:
def __init__(self, command, client):
self._command, self._args = command.split(' ', 1)
self._client = client
self._result_code = 0
self._result_message = ""
self._commands = {}
self._commands['LISTDIR'] = self._process_listdir
self._commands['GETFILE'] = self._process_getfile
self._commands['GETFILE_LINES'] = self._process_getfile_lines
self._commands['GET_FREE_SPACE'] = self._process_get_free_space
self._commands['CHECK_DIR_WRITABLE'] = self._process_check_dir_writable
self._commands['CHECK_PATH_EXISTS'] = self._process_check_path_exists
self._commands['CREATE_DIRECTORY'] = self._process_create_directory
self._commands['CREATE_DIRECTORY_RECURSIVE'] = self._process_create_directory_recursive
self._commands['REMOVE_DIRECTORY'] = self._process_remove_directory
self._commands['REMOVE_DIRECTORY_RECURSIVE'] = self._process_remove_directory_recursive
self._commands['DELETE_FILE'] = self._process_delete_file
self._commands['COPY_FILE'] = self._process_copy_file
self._commands['GET_FILE_OWNER'] = self._process_get_file_owner
self._commands['EXEC'] = self._process_exec
if not self._commands.has_key(self._command):
raise RuntimeError("Command %s is not supported" % self._command)
def execute(self):
"""
Selects the proper method for the command execution,
when an additional custom command (i.e. LISTDIR) is needed
a method should be created on this class and called on this
method based on the command name.
When no specific method is defined for the given command it
will be handled by the _execute_command method which assumes
the command is valid for the operating system
"""
self._commands[self._command]()
def _process_listdir(self):
"""
Lists the content of a directory and returns it to the command listener,
either including or not the file sizes.
Syntax:
LISTDIR <size> <path>
size: Indicates if the returned list should containg the size
or not. 0 indicates no, 1 indicates yes.
path: The path to the file or folder that will be listed.
"""
include_size, path = self._args.split(' ', 1)
include_size = (include_size == '1')
FileUtils.list_dir(path, include_size, self._send_to_listener)
def _process_getfile(self):
"""
Load data from a file and sends it back to the command listener in 64K chunks.
Syntax:
GETFILE <offset> <size> <path>
offset: In bytes, indicates the position of the file where the
read operation will start. 0 indicates the beggining of
the file.
size: In bytes, indicates the amount of bytes to be read from
the file. 0 indicates the whole file should be read.
path: The path to the file that will be read.
"""
offset, size, path = self._args.split(' ', 2)
read_size = chunk_size = 64000
offset = int(offset)
size = int(size)
remaining = size
try:
f = open(path, 'r')
# If specifieds moves the read to the offset position
if offset:
f.seek(offset)
# Starts the read process
continue_reading = True
while continue_reading:
# Calculates the amount of data to be read, it will be the chunk size
# most of the time, except when a specific amount of data is requested,
# in such case the limit needs to be considered
if size:
read_size = chunk_size if chunk_size < remaining else remaining
data = f.read(read_size)
# The read operation returns an empty string when no more data
# is in the file
if data:
self._client.send(data)
# When a size is specified, the read will be done once that
# size is reached
if size:
remaining = remaining - read_size
if remaining == 0:
continue_reading = False
else:
continue_reading = False
f.close()
except (IOError), e:
self._result_code = 1
self._result_message = repr(e)
def _process_getfile_lines(self):
#GETFILE_LINES <skip> <path>
skip, path = self._args.split(' ', 1)
skip = int(skip)
FileUtils.get_file_lines(path, skip, self._send_to_listener)
def _process_get_free_space(self):
free_space = FileUtils.get_free_space(self._args)
self._client.send(str(free_space))
def _process_check_dir_writable(self):
self._client.send(str(FileUtils.check_dir_writable(self._args)))
def _process_check_path_exists(self):
self._client.send(str(FileUtils.check_path_exists(self._args)))
def _process_create_directory(self):
FileUtils.create_directory(self._args)
def _process_create_directory_recursive(self):
FileUtils.create_directory_recursive(self._args)
def _process_remove_directory(self):
FileUtils.remove_directory(self._args)
def _process_remove_directory_recursive(self):
FileUtils.remove_directory_recursive(self._args)
def _process_delete_file(self):
FileUtils.delete_file(self._args)
def _process_copy_file(self):
files = self._args.split('>')
if len(files) < 2:
raise RuntimeError('Invalid call to the COPY_FILE command')
src = files[0]
tgt = files[1]
tgt_backup = ""
if len(files) > 2:
tgt_backup = files[2]
src.strip()
tgt.strip()
tgt_backup.strip()
FileUtils.copy_file(src, tgt, tgt_backup)
def _process_get_file_owner(self):
self._client.send(str(FileUtils.get_file_owner(self._args)))
# Process any valid OS command that has not been handled by the other functions
def _send_to_listener(self, data):
self._client.send(data)
def _process_exec(self):
"""
Executes any OS valid command and sends the output to the command listener
Syntax:
EXEC <command>
command: A valid OS command.
"""
OSUtils.exec_command(self._args, self._send_to_listener)
# Creates the client to use it to send the output to the command listener
client = SocketClient('127.0.0.1', int(port), handshake, done_key)
try:
if client.start():
processor = CommandProcessor(command, client)
processor.execute()
client.close(processor._result_code, processor._result_message)
except Exception, e:
if client._connected:
client.close(1, repr(e))
else:
print e
|