Skip to content

Commit 9ba10f3

Browse files
author
Teddy Reed
committed
Add SpawnInstance API
1 parent 6e0ff88 commit 9ba10f3

File tree

3 files changed

+126
-3
lines changed

3 files changed

+126
-3
lines changed

examples/spawn.py

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#!/usr/bin/env python
2+
"""
3+
simple script which runs a query from the command-line by spawning osqueryd
4+
"""
5+
6+
import sys
7+
import osquery
8+
9+
if __name__ == "__main__":
10+
if len(sys.argv) != 2:
11+
print("Usage: %s \"query\"" % sys.argv[0])
12+
sys.exit(1)
13+
INSTANCE = osquery.SpawnInstance()
14+
INSTANCE.open()
15+
RESULTS = INSTANCE.client.query(sys.argv[1])
16+
if RESULTS.status.code != 0:
17+
print("Error running the query: %s" % RESULTS.status.message)
18+
sys.exit(1)
19+
20+
for row in RESULTS.response:
21+
print("=" * 80)
22+
for key, val in row.iteritems():
23+
print("%s => %s" % (key, val))
24+
if len(RESULTS.response) > 0:
25+
print("=" * 80)

osquery/__init__.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"""
55

66
__title__ = "osquery"
7-
__version__ = "1.4.5"
7+
__version__ = "1.5.3"
88
__author__ = "osquery developers"
99
__license__ = "BSD"
1010
__copyright__ = "Copyright 2015 Facebook"
@@ -23,6 +23,7 @@
2323
"register_plugin",
2424
"start_extension",
2525
"Singleton",
26+
"SpawnInstance",
2627
"STRING",
2728
"TableColumn",
2829
"TablePlugin",
@@ -32,8 +33,8 @@
3233
from osquery.extension_client import DEFAULT_SOCKET_PATH, ExtensionClient
3334
from osquery.extension_manager import ExtensionManager
3435
from osquery.logger_plugin import LoggerPlugin
35-
from osquery.management import deregister_extension, parse_cli_params, \
36-
register_plugin, start_extension
36+
from osquery.management import SpawnInstance, \
37+
deregister_extension, parse_cli_params, register_plugin, start_extension
3738
from osquery.plugin import BasePlugin
3839
from osquery.singleton import Singleton
3940
from osquery.table_plugin import INTEGER, STRING, TableColumn, TablePlugin

osquery/management.py

+97
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@
99
from __future__ import unicode_literals
1010

1111
import argparse
12+
import shutil
1213
import socket
14+
import subprocess
15+
import sys
16+
import tempfile
17+
import time
1318

1419
from thrift.protocol import TBinaryProtocol
1520
from thrift.server import TServer
@@ -21,6 +26,98 @@
2126
from osquery.extension_client import ExtensionClient, DEFAULT_SOCKET_PATH
2227
from osquery.extension_manager import ExtensionManager
2328

29+
DARWIN_BINARY_PATH = "/usr/local/bin/osqueryd"
30+
LINUX_BINARY_PATH = "/usr/bin/osqueryd"
31+
32+
class SpawnInstance(object):
33+
"""Spawn a standalone osquery instance"""
34+
35+
"""The osquery process instance."""
36+
instance = None
37+
"""The extension client connection attached to the instance."""
38+
connection = None
39+
_socket = None
40+
41+
def __init__(self, path=None):
42+
"""
43+
Keyword arguments:
44+
path -- the path to and osqueryd binary to spawn
45+
"""
46+
if path is None:
47+
# Darwin is special and must have binaries installed in /usr/local.
48+
if sys.platform == "darwin":
49+
self.path = DARWIN_BINARY_PATH
50+
else:
51+
self.path = LINUX_BINARY_PATH
52+
else:
53+
self.path = path
54+
self._socket = tempfile.mkstemp(prefix="pyosqsock")
55+
self._pidfile = tempfile.mkstemp(prefix="pyosqpid")
56+
with open(self._pidfile[1], "w") as fh:
57+
fh.write("100000")
58+
self._dbpath = tempfile.mkdtemp(prefix="pyoqsdb")
59+
60+
def __del__(self):
61+
if self.connection is not None:
62+
self.connection.close()
63+
if self.instance is not None:
64+
self.instance.kill()
65+
shutil.rmtree(self._dbpath)
66+
self.instance.wait()
67+
68+
def open(self, timeout=2, interval=0.01):
69+
"""
70+
Start the instance process and open an extension client
71+
72+
Keyword arguments:
73+
timeout -- maximum number of seconds to wait for client
74+
interval -- seconds between client open attempts
75+
"""
76+
proc = [
77+
self.path,
78+
"--extensions_socket",
79+
self._socket[1],
80+
"--database_path",
81+
# This is a temporary directory, there is not FD tuple.
82+
self._dbpath,
83+
"--pidfile",
84+
self._pidfile[1],
85+
"--disable_watchdog",
86+
"--disable_logging",
87+
"--config_path",
88+
"/dev/null",
89+
]
90+
self.instance = subprocess.Popen(proc,
91+
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
92+
stderr=subprocess.PIPE)
93+
self.connection = ExtensionClient(path=self._socket[1])
94+
if not self.is_running():
95+
raise Exception("Cannot start process from path: %s" % (self.path))
96+
97+
# Attempt to open the extension client.
98+
delay = 0
99+
while delay < timeout:
100+
try:
101+
self.connection.open()
102+
return
103+
except:
104+
time.sleep(interval)
105+
delay += interval
106+
self.instance.kill()
107+
self.instance = None
108+
raise Exception("Cannot open socket: %s" % (self._socket[1]))
109+
110+
def is_running(self):
111+
"""Check if the instance has spawned."""
112+
if self.instance is None:
113+
return False
114+
return self.instance.poll() is None
115+
116+
@property
117+
def client(self):
118+
"""The extension client."""
119+
return self.connection.extension_manager_client()
120+
24121
def parse_cli_params():
25122
"""Parse CLI parameters passed to the extension executable"""
26123
parser = argparse.ArgumentParser(description=(

0 commit comments

Comments
 (0)