From 6a2a80cc0edce7795fcf02bece942e2deed436f6 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 29 Oct 2012 15:32:21 +1100 Subject: [PATCH 1/4] imessaging: Add irpc_all_servers() to list all available servers This is implemented with a tdb_traverse_read(), and will allow a tool to disover the name and server_id of all Samba processes, as each process registers itself to recieve messages. Andrew Bartlett (cherry picked from commit 3b4ef03097293f758d8f11cbe434063ed1dc6b91) --- source4/lib/messaging/irpc.h | 2 ++ source4/lib/messaging/messaging.c | 71 +++++++++++++++++++++++++++++++++++++ source4/librpc/idl/irpc.idl | 13 ++++++- 3 files changed, 85 insertions(+), 1 deletion(-) diff --git a/source4/lib/messaging/irpc.h b/source4/lib/messaging/irpc.h index 15f8259..456d190 100644 --- a/source4/lib/messaging/irpc.h +++ b/source4/lib/messaging/irpc.h @@ -75,6 +75,8 @@ void irpc_binding_handle_add_security_token(struct dcerpc_binding_handle *h, NTSTATUS irpc_add_name(struct imessaging_context *msg_ctx, const char *name); struct server_id *irpc_servers_byname(struct imessaging_context *msg_ctx, TALLOC_CTX *mem_ctx, const char *name); +struct irpc_name_records *irpc_all_servers(struct imessaging_context *msg_ctx, + TALLOC_CTX *mem_ctx); void irpc_remove_name(struct imessaging_context *msg_ctx, const char *name); NTSTATUS irpc_send_reply(struct irpc_message *m, NTSTATUS status); diff --git a/source4/lib/messaging/messaging.c b/source4/lib/messaging/messaging.c index 4d69b94..6618897 100644 --- a/source4/lib/messaging/messaging.c +++ b/source4/lib/messaging/messaging.c @@ -985,6 +985,77 @@ struct server_id *irpc_servers_byname(struct imessaging_context *msg_ctx, return ret; } +static int all_servers_func(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *state) +{ + struct irpc_name_records *name_records = talloc_get_type(state, struct irpc_name_records); + struct irpc_name_record *name_record; + int i; + + name_records->names + = talloc_realloc(name_records, name_records->names, + struct irpc_name_record *, name_records->num_records+1); + if (!name_records->names) { + return -1; + } + + name_records->names[name_records->num_records] = name_record + = talloc(name_records->names, + struct irpc_name_record); + if (!name_record) { + return -1; + } + + name_records->num_records++; + + name_record->name + = talloc_strndup(name_record, + (const char *)key.dptr, key.dsize); + if (!name_record->name) { + return -1; + } + + name_record->count = data.dsize / sizeof(struct server_id); + name_record->ids = talloc_array(name_record, + struct server_id, + name_record->count); + if (name_record->ids == NULL) { + return -1; + } + for (i=0;icount;i++) { + name_record->ids[i] = ((struct server_id *)data.dptr)[i]; + } + return 0; +} + +/* + return a list of server ids for a server name +*/ +struct irpc_name_records *irpc_all_servers(struct imessaging_context *msg_ctx, + TALLOC_CTX *mem_ctx) +{ + struct tdb_wrap *t; + int ret; + struct irpc_name_records *name_records = talloc_zero(mem_ctx, struct irpc_name_records); + if (name_records == NULL) { + return NULL; + } + + t = irpc_namedb_open(msg_ctx); + if (t == NULL) { + return NULL; + } + + ret = tdb_traverse_read(t->tdb, all_servers_func, name_records); + if (ret == -1) { + talloc_free(t); + return NULL; + } + + talloc_free(t); + + return name_records; +} + /* remove a name from a messaging context */ diff --git a/source4/librpc/idl/irpc.idl b/source4/librpc/idl/irpc.idl index ed331c7..6a55eef 100644 --- a/source4/librpc/idl/irpc.idl +++ b/source4/librpc/idl/irpc.idl @@ -1,6 +1,6 @@ #include "idl_types.h" -import "misc.idl", "security.idl", "nbt.idl", "netlogon.idl"; +import "misc.idl", "security.idl", "nbt.idl", "netlogon.idl", "server_id.idl"; /* definitions for irpc primitives @@ -29,6 +29,17 @@ import "misc.idl", "security.idl", "nbt.idl", "netlogon.idl"; [flag(NDR_ALIGN8)] DATA_BLOB _pad; } irpc_header; + typedef [public] struct { + utf8string name; + uint32 count; + [size_is(count)] server_id ids[*]; + } irpc_name_record; + + typedef [public] struct { + [size_is(num_records)] irpc_name_record *names[*]; + uint32 num_records; + } irpc_name_records; + /****************************************************** uptime call - supported by all messaging servers *******************************************************/ -- 1.7.9.5 From fd8021b67f2468b08c26678d4eae620dce887292 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 29 Oct 2012 15:33:59 +1100 Subject: [PATCH 2/4] pymessaging: Use the server_id IDL structure rather than a tuple This will make it easier to pass this structure in and out. The tuple is still accepted as input. Andrew Bartlett (cherry picked from commit 76b7348299870279acec5b7c9f02f4e4b2461703) --- librpc/wscript_build | 5 +++++ source4/lib/messaging/pymessaging.c | 25 ++++++++++++++++----- source4/librpc/wscript_build | 6 +++++ source4/scripting/python/samba/tests/messaging.py | 4 ++-- 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/librpc/wscript_build b/librpc/wscript_build index 0eeb01b..8a4c169 100644 --- a/librpc/wscript_build +++ b/librpc/wscript_build @@ -559,6 +559,11 @@ bld.SAMBA_SUBSYSTEM('RPC_NDR_SCERPC', public_deps='dcerpc-binding NDR_SCERPC' ) +bld.SAMBA_SUBSYSTEM('RPC_NDR_SERVER_ID', + source='gen_ndr/ndr_server_id_c.c', + public_deps='dcerpc-binding NDR_SERVER_ID' + ) + bld.SAMBA_SUBSYSTEM('RPC_NDR_NTSVCS', source='gen_ndr/ndr_ntsvcs_c.c', public_deps='dcerpc-binding ndr-standard' diff --git a/source4/lib/messaging/pymessaging.c b/source4/lib/messaging/pymessaging.c index e8ef4a8..fb74f3e 100644 --- a/source4/lib/messaging/pymessaging.c +++ b/source4/lib/messaging/pymessaging.c @@ -26,12 +26,14 @@ #include "librpc/rpc/pyrpc_util.h" #include "librpc/ndr/libndr.h" #include "lib/messaging/messaging.h" +#include "lib/messaging/irpc.h" #include "lib/events/events.h" #include "cluster/cluster.h" #include "param/param.h" #include "param/pyparam.h" #include "librpc/rpc/dcerpc.h" #include "librpc/gen_ndr/server_id.h" +#include void initmessaging(void); @@ -40,10 +42,14 @@ extern PyTypeObject imessaging_Type; static bool server_id_from_py(PyObject *object, struct server_id *server_id) { if (!PyTuple_Check(object)) { - PyErr_SetString(PyExc_ValueError, "Expected tuple"); - return false; - } + if (!py_check_dcerpc_type(object, "server_id", "server_id")) { + PyErr_SetString(PyExc_ValueError, "Expected tuple or server_id"); + return false; + } + *server_id = *pytalloc_get_type(object, struct server_id); + return true; + } if (PyTuple_Size(object) == 3) { return PyArg_ParseTuple(object, "iii", &server_id->pid, &server_id->task_id, &server_id->vnn); } else { @@ -228,10 +234,19 @@ static PyMethodDef py_imessaging_methods[] = { static PyObject *py_imessaging_server_id(PyObject *obj, void *closure) { imessaging_Object *iface = (imessaging_Object *)obj; + PyObject *py_server_id; struct server_id server_id = imessaging_get_server_id(iface->msg_ctx); + struct server_id *p_server_id = talloc(NULL, struct server_id); + if (!p_server_id) { + PyErr_NoMemory(); + return NULL; + } + *p_server_id = server_id; + + py_server_id = py_return_ndr_struct("samba.dcerpc.server_id", "server_id", p_server_id, p_server_id); + talloc_unlink(NULL, p_server_id); - return Py_BuildValue("(iii)", server_id.pid, server_id.task_id, - server_id.vnn); + return py_server_id; } static PyGetSetDef py_imessaging_getset[] = { diff --git a/source4/librpc/wscript_build b/source4/librpc/wscript_build index d3b81b1..c53acef 100755 --- a/source4/librpc/wscript_build +++ b/source4/librpc/wscript_build @@ -296,6 +296,12 @@ bld.SAMBA_PYTHON('python_irpc', realname='samba/dcerpc/irpc.so' ) +bld.SAMBA_PYTHON('python_server_id', + source='../../librpc/gen_ndr/py_server_id.c', + deps='RPC_NDR_SERVER_ID pytalloc-util pyrpc_util', + realname='samba/dcerpc/server_id.so' + ) + bld.SAMBA_PYTHON('python_winbind', source='gen_ndr/py_winbind.c', deps='RPC_NDR_WINBIND pytalloc-util pyrpc_util python_netlogon', diff --git a/source4/scripting/python/samba/tests/messaging.py b/source4/scripting/python/samba/tests/messaging.py index fd9aa8e..3348ff8 100644 --- a/source4/scripting/python/samba/tests/messaging.py +++ b/source4/scripting/python/samba/tests/messaging.py @@ -21,6 +21,7 @@ from samba.messaging import Messaging from samba.tests import TestCase +from samba.dcerpc.server_id import server_id class MessagingTests(TestCase): @@ -36,8 +37,7 @@ class MessagingTests(TestCase): def test_assign_server_id(self): x = self.get_context() - self.assertTrue(isinstance(x.server_id, tuple)) - self.assertEquals(3, len(x.server_id)) + self.assertTrue(isinstance(x.server_id, server_id)) def test_ping_speed(self): server_ctx = self.get_context((0, 1)) -- 1.7.9.5 From eb0f63c0182713c2b4fbb171246f3536960465a0 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 29 Oct 2012 15:34:41 +1100 Subject: [PATCH 3/4] pymessaging: Add irpc_servers_byname() and irpc_all_servers() This will allow python scripts to inspect the process list. Andrew Bartlett (cherry picked from commit a732f2a621665923322422c5a3d788c9d1aa8df9) --- source4/lib/messaging/pymessaging.c | 99 +++++++++++++++++++++ source4/scripting/python/samba/tests/messaging.py | 9 ++ 2 files changed, 108 insertions(+) diff --git a/source4/lib/messaging/pymessaging.c b/source4/lib/messaging/pymessaging.c index fb74f3e..fca46e6 100644 --- a/source4/lib/messaging/pymessaging.c +++ b/source4/lib/messaging/pymessaging.c @@ -221,6 +221,101 @@ static PyObject *py_imessaging_deregister(PyObject *self, PyObject *args, PyObje Py_RETURN_NONE; } +static PyObject *py_irpc_servers_byname(PyObject *self, PyObject *args, PyObject *kwargs) +{ + imessaging_Object *iface = (imessaging_Object *)self; + char *server_name; + struct server_id *ids; + PyObject *pylist; + int i; + TALLOC_CTX *mem_ctx = talloc_new(NULL); + if (!mem_ctx) { + PyErr_NoMemory(); + return NULL; + } + + if (!PyArg_ParseTuple(args, "s", &server_name)) { + TALLOC_FREE(mem_ctx); + return NULL; + } + + ids = irpc_servers_byname(iface->msg_ctx, mem_ctx, server_name); + + if (ids == NULL) { + TALLOC_FREE(mem_ctx); + PyErr_SetString(PyExc_KeyError, "No such name"); + return NULL; + } + + for (i = 0; ids[i].pid != 0; i++) { + /* Do nothing */ + } + + pylist = PyList_New(i); + if (pylist == NULL) { + TALLOC_FREE(mem_ctx); + PyErr_NoMemory(); + return NULL; + } + for (i = 0; ids[i].pid; i++) { + PyObject *py_server_id; + struct server_id *p_server_id = talloc(NULL, struct server_id); + if (!p_server_id) { + PyErr_NoMemory(); + return NULL; + } + *p_server_id = ids[i]; + + py_server_id = py_return_ndr_struct("samba.dcerpc.server_id", "server_id", p_server_id, p_server_id); + if (!py_server_id) { + return NULL; + } + PyList_SetItem(pylist, i, py_server_id); + talloc_unlink(NULL, p_server_id); + } + TALLOC_FREE(mem_ctx); + return pylist; +} + +static PyObject *py_irpc_all_servers(PyObject *self, PyObject *args, PyObject *kwargs) +{ + imessaging_Object *iface = (imessaging_Object *)self; + PyObject *pylist; + int i; + struct irpc_name_records *records; + TALLOC_CTX *mem_ctx = talloc_new(NULL); + if (!mem_ctx) { + PyErr_NoMemory(); + return NULL; + } + + records = irpc_all_servers(iface->msg_ctx, mem_ctx); + if (records == NULL) { + return NULL; + } + + pylist = PyList_New(records->num_records); + if (pylist == NULL) { + TALLOC_FREE(mem_ctx); + PyErr_NoMemory(); + return NULL; + } + for (i = 0; i < records->num_records; i++) { + PyObject *py_name_record + = py_return_ndr_struct("samba.dcerpc.irpc", + "name_record", + records->names[i], + records->names[i]); + if (!py_name_record) { + return NULL; + } + PyList_SetItem(pylist, i, + py_name_record); + } + TALLOC_FREE(mem_ctx); + return pylist; +} + static PyMethodDef py_imessaging_methods[] = { { "send", (PyCFunction)py_imessaging_send, METH_VARARGS|METH_KEYWORDS, "S.send(target, msg_type, data) -> None\nSend a message" }, @@ -228,6 +323,10 @@ static PyMethodDef py_imessaging_methods[] = { "S.register(callback, msg_type=None) -> msg_type\nRegister a message handler" }, { "deregister", (PyCFunction)py_imessaging_deregister, METH_VARARGS|METH_KEYWORDS, "S.deregister(callback, msg_type) -> None\nDeregister a message handler" }, + { "irpc_servers_byname", (PyCFunction)py_irpc_servers_byname, METH_VARARGS, + "S.irpc_servers_byname(name) -> list\nGet list of server_id values that are registered for a particular name" }, + { "irpc_all_servers", (PyCFunction)py_irpc_all_servers, METH_NOARGS, + "S.irpc_servers_byname() -> list\nGet list of all registered names and the associated server_id values" }, { NULL, NULL, 0, NULL } }; diff --git a/source4/scripting/python/samba/tests/messaging.py b/source4/scripting/python/samba/tests/messaging.py index 3348ff8..f0cd368 100644 --- a/source4/scripting/python/samba/tests/messaging.py +++ b/source4/scripting/python/samba/tests/messaging.py @@ -35,6 +35,15 @@ class MessagingTests(TestCase): msg_type = x.register(callback) x.deregister(callback, msg_type) + def test_all_servers(self): + x = self.get_context() + self.assertTrue(isinstance(x.irpc_all_servers(), list)) + + def test_by_name(self): + x = self.get_context() + for name in x.irpc_all_servers(): + self.assertTrue(isinstance(x.irpc_servers_byname(name.name), list)) + def test_assign_server_id(self): x = self.get_context() self.assertTrue(isinstance(x.server_id, server_id)) -- 1.7.9.5 From cb745634df97dedaafefee4a92219873ca27d9df Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 29 Oct 2012 15:36:36 +1100 Subject: [PATCH 4/4] samba-tool: Add samba-tool processes subcommand This will allow administrators to inspect the process list in a similar way to what running on a platform with setproctitle might permit. --pid= returns the registered server names for a PID (eg kdc, cldap_server) --name= returns the pids registered with a particular name. Andrew Bartlett (cherry picked from commit 42c379f0dfdeb36598bb2636aa2b6e3ca4410930) --- source4/scripting/python/samba/netcmd/main.py | 2 + source4/scripting/python/samba/netcmd/processes.py | 78 ++++++++++++++++++++ .../python/samba/tests/samba_tool/processes.py | 35 +++++++++ source4/selftest/tests.py | 1 + 4 files changed, 116 insertions(+) create mode 100644 source4/scripting/python/samba/netcmd/processes.py create mode 100644 source4/scripting/python/samba/tests/samba_tool/processes.py diff --git a/source4/scripting/python/samba/netcmd/main.py b/source4/scripting/python/samba/netcmd/main.py index af3abfd..5f788235 100644 --- a/source4/scripting/python/samba/netcmd/main.py +++ b/source4/scripting/python/samba/netcmd/main.py @@ -38,6 +38,7 @@ from samba.netcmd.testparm import cmd_testparm from samba.netcmd.time import cmd_time from samba.netcmd.user import cmd_user from samba.netcmd.vampire import cmd_vampire +from samba.netcmd.processes import cmd_processes class cmd_sambatool(SuperCommand): @@ -66,3 +67,4 @@ class cmd_sambatool(SuperCommand): subcommands["time"] = cmd_time() subcommands["user"] = cmd_user() subcommands["vampire"] = cmd_vampire() + subcommands["processes"] = cmd_processes() diff --git a/source4/scripting/python/samba/netcmd/processes.py b/source4/scripting/python/samba/netcmd/processes.py new file mode 100644 index 0000000..751ab94 --- /dev/null +++ b/source4/scripting/python/samba/netcmd/processes.py @@ -0,0 +1,78 @@ +# Unix SMB/CIFS implementation. +# List processes (to aid debugging on systems without setproctitle) +# Copyright (C) 2010-2011 Jelmer Vernooij +# +# 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; either version 3 of the License, or +# (at your option) any later version. +# +# 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, see . +# +# Testbed for loadparm.c/params.c +# +# This module simply loads a specified configuration file and +# if successful, dumps it's contents to stdout. Note that the +# operation is performed with DEBUGLEVEL at 3. +# +# Useful for a quick 'syntax check' of a configuration file. +# + +import os +import sys + +import samba +import samba.getopt as options +from samba.netcmd import Command, CommandError, Option +from samba.messaging import Messaging + +class cmd_processes(Command): + """List processes (to aid debugging on systems without setproctitle).""" + + synopsis = "%prog [options]" + + takes_optiongroups = { + "sambaopts": options.SambaOptions, + "versionopts": options.VersionOptions + } + + takes_options = [ + Option("--name", type=str, + help="Return only processes associated with one particular name"), + Option("--pid", type=int, + help="Return only names assoicated with one particular PID"), + ] + + takes_args = [] + + def run(self, sambaopts, versionopts, section_name=None, + name=None, pid=None): + + lp = sambaopts.get_loadparm() + logger = self.get_logger("processes") + + msg_ctx = Messaging() + + if name is not None: + ids = msg_ctx.irpc_servers_byname(name) + for server_id in ids: + print "%d\n" % server_id.pid + elif pid is not None: + names = msg_ctx.irpc_all_servers() + for name in names: + for server_id in name.ids: + if server_id.pid == int(pid): + print "%s\n" % name.name + else: + names = msg_ctx.irpc_all_servers() + for name in names: + print "%s: " % name.name + for server_id in name.ids: + print "%d " % server_id.pid + print "\n" diff --git a/source4/scripting/python/samba/tests/samba_tool/processes.py b/source4/scripting/python/samba/tests/samba_tool/processes.py new file mode 100644 index 0000000..91a5266 --- /dev/null +++ b/source4/scripting/python/samba/tests/samba_tool/processes.py @@ -0,0 +1,35 @@ +# Unix SMB/CIFS implementation. +# Copyright (C) Andrew Bartlett 2012 +# +# based on time.py: +# Copyright (C) Sean Dague 2011 +# +# 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; either version 3 of the License, or +# (at your option) any later version. +# +# 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, see . +# + +import os +from samba.tests.samba_tool.base import SambaToolCmdTest + +class ProcessCmdTestCase(SambaToolCmdTest): + """Tests for samba-tool process subcommands""" + + def test_name(self): + """Run processes command""" + (result, out, err) = self.runcmd("processes", "--name", "samba") + self.assertCmdSuccess(result, "Ensuring processes ran successfully") + + def test_all(self): + """Run processes command""" + (result, out, err) = self.runcmd("processes") + self.assertCmdSuccess(result, "Ensuring processes ran successfully") diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index ea09071..ca5bdd3 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -405,6 +405,7 @@ planpythontestsuite("dc:local", "samba.tests.dcerpc.bare") planpythontestsuite("dc:local", "samba.tests.dcerpc.unix") planpythontestsuite("dc:local", "samba.tests.dcerpc.srvsvc") planpythontestsuite("dc:local", "samba.tests.samba_tool.timecmd") +planpythontestsuite("dc:local", "samba.tests.samba_tool.processes") planpythontestsuite("dc:local", "samba.tests.samba_tool.user") planpythontestsuite("dc:local", "samba.tests.samba_tool.group") planpythontestsuite("plugin_s4_dc:local", "samba.tests.samba_tool.ntacl") -- 1.7.9.5