mirror of
https://github.com/samba-team/samba.git
synced 2025-02-22 05:57:43 +03:00
s3:rpc_server: Delete unused code and doc references
Signed-off-by: Volker Lendecke <vl@samba.org> Reviewed-by: Samuel Cabrero <scabrero@samba.org> Reviewed-by: Jeremy Allison <jra@samba.org> Reviewed-by: Stefan Metzmacher <metze@samba.org>
This commit is contained in:
parent
9e3ee8c40c
commit
730f7dfd61
@ -107,7 +107,6 @@
|
||||
|
||||
<programlisting>
|
||||
<smbconfsection name="[global]"/>
|
||||
<smbconfoption name="rpc_daemon:fssd">fork</smbconfoption>
|
||||
<smbconfoption name="registry shares">yes</smbconfoption>
|
||||
<smbconfoption name="include">registry</smbconfoption>
|
||||
|
||||
|
@ -128,7 +128,6 @@
|
||||
|
||||
<programlisting>
|
||||
<smbconfsection name="[global]"/>
|
||||
<smbconfoption name="rpc_daemon:fssd">fork</smbconfoption>
|
||||
<smbconfoption name="registry shares">yes</smbconfoption>
|
||||
<smbconfoption name="include">registry</smbconfoption>
|
||||
</programlisting>
|
||||
|
@ -70,7 +70,6 @@
|
||||
|
||||
<programlisting>
|
||||
<smbconfsection name="[global]"/>
|
||||
<smbconfoption name="rpc_daemon:fssd">fork</smbconfoption>
|
||||
<smbconfoption name="registry shares">yes</smbconfoption>
|
||||
<smbconfoption name="include">registry</smbconfoption>
|
||||
</programlisting>
|
||||
|
@ -1,76 +0,0 @@
|
||||
<samba:parameter name="rpc_daemon:DAEMON"
|
||||
context="G"
|
||||
type="string"
|
||||
xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
|
||||
<description>
|
||||
<para>
|
||||
Defines whether to use the embedded code or start a separate daemon
|
||||
for the defined rpc services.
|
||||
The rpc_daemon prefix must be followed by the server name, and a value.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Two possible values are currently supported:
|
||||
<programlisting>
|
||||
disabled
|
||||
fork
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The classic method is to run rpc services as internal daemons
|
||||
embedded in smbd, therefore the external daemons are
|
||||
<emphasis>disabled</emphasis> by default.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Choosing the <emphasis>fork</emphasis> option will cause samba to fork
|
||||
a separate process for each daemon configured this way. Each daemon may
|
||||
in turn fork a number of children used to handle requests from multiple
|
||||
smbds and direct tcp/ip connections (if the Endpoint Mapper is
|
||||
enabled). Communication with smbd happens over named pipes and require
|
||||
that said pipes are forward to the external daemon (see <smbconfoption
|
||||
name="rpc_server"/>).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Forked RPC Daemons support dynamically forking children to handle
|
||||
connections. The heuristics about how many children to keep around and
|
||||
how fast to allow them to fork and also how many clients each child is
|
||||
allowed to handle concurrently is defined by parametrical options named
|
||||
after the daemon.
|
||||
Five options are currently supported:
|
||||
<programlisting>
|
||||
prefork_min_children
|
||||
prefork_max_children
|
||||
prefork_spawn_rate
|
||||
prefork_max_allowed_clients
|
||||
prefork_child_min_life
|
||||
</programlisting>
|
||||
|
||||
To set one of these options use the following syntax:
|
||||
<programlisting>
|
||||
daemonname:prefork_min_children = 5
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Samba includes separate daemons for spoolss, lsarpc/lsass,
|
||||
netlogon, samr, FSRVP and mdssvc(Spotlight). Currently five
|
||||
daemons are available and they are called:
|
||||
<programlisting>
|
||||
epmd
|
||||
lsasd
|
||||
spoolssd
|
||||
fssd
|
||||
mdssd
|
||||
</programlisting>
|
||||
Example:
|
||||
<programlisting>
|
||||
rpc_daemon:spoolssd = fork
|
||||
</programlisting>
|
||||
</para>
|
||||
</description>
|
||||
|
||||
<value type="default">disabled</value>
|
||||
</samba:parameter>
|
@ -1,94 +0,0 @@
|
||||
<samba:parameter name="rpc_server:SERVER"
|
||||
context="G"
|
||||
type="string"
|
||||
xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
|
||||
<description>
|
||||
<para>
|
||||
With this option you can define if a rpc service should be
|
||||
running internal/embedded in smbd or should be redirected to an
|
||||
external daemon like Samba4, the endpoint mapper daemon, the
|
||||
spoolss daemon or the new LSA service daemon. The rpc_server
|
||||
prefix must be followed by the pipe name, and a value.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This option can be set for each available rpc service in Samba.
|
||||
The following list shows all available pipe names services you
|
||||
can modify with this option.
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para>epmapper - Endpoint Mapper</para></listitem>
|
||||
<listitem><para>winreg - Remote Registry Service</para></listitem>
|
||||
<listitem><para>srvsvc - Remote Server Services</para></listitem>
|
||||
<listitem><para>lsarpc - Local Security Authority</para></listitem>
|
||||
<listitem><para>samr - Security Account Management</para></listitem>
|
||||
<listitem><para>netlogon - Netlogon Remote Protocol</para></listitem>
|
||||
<listitem><para>netdfs - Settings for Distributed File System</para></listitem>
|
||||
<listitem><para>dssetup - Active Directory Setup</para></listitem>
|
||||
<listitem><para>wkssvc - Workstation Services</para></listitem>
|
||||
<listitem><para>spoolss - Network Printing Spooler</para></listitem>
|
||||
<listitem><para>svcctl - Service Control</para></listitem>
|
||||
<listitem><para>ntsvcs - Plug and Play Services</para></listitem>
|
||||
<listitem><para>eventlog - Event Logger</para></listitem>
|
||||
<listitem><para>initshutdown - Init Shutdown Service</para></listitem>
|
||||
<listitem><para>mdssvc - Spotlight</para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
Three possible values currently supported are:
|
||||
<command moreinfo="none">embedded</command>
|
||||
<command moreinfo="none">external</command>
|
||||
<command moreinfo="none">disabled</command>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The classic method is to run every pipe as an internal function
|
||||
<emphasis>embedded</emphasis> in smbd. The defaults may vary
|
||||
depending on the service.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Choosing the <emphasis>external</emphasis> option allows one to run
|
||||
a separate daemon or even a completely independent (3rd party)
|
||||
server capable of interfacing with samba via the MS-RPC
|
||||
interface over named pipes.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Currently in Samba3 we support four daemons, spoolssd, epmd,
|
||||
lsasd and mdssd. These daemons can be enabled using the
|
||||
<emphasis>rpc_daemon</emphasis> option. For spoolssd you have
|
||||
to enable the daemon and proxy the named pipe with:
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Examples:
|
||||
<programlisting>
|
||||
rpc_daemon:lsasd = fork
|
||||
rpc_server:lsarpc = external
|
||||
rpc_server:samr = external
|
||||
rpc_server:netlogon = external
|
||||
|
||||
rpc_server:spoolss = external
|
||||
rpc_server:epmapper = disabled
|
||||
|
||||
rpc_daemon:mdssd = fork
|
||||
rpc_server:mdssvc = external
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
There is one special option which allows you to enable rpc
|
||||
services to listen for ncacn_ip_tcp connections too. Currently
|
||||
this is only used for testing and doesn't scale!
|
||||
|
||||
<programlisting>
|
||||
rpc_server:tcpip = yes
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
</description>
|
||||
|
||||
<value type="default">embedded</value>
|
||||
</samba:parameter>
|
@ -18,11 +18,6 @@
|
||||
Samba must be configured and built with Spotlight support.
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
The <emphasis>mdssvc</emphasis> RPC service must be
|
||||
enabled, see below.
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para> Tracker integration must be setup and the
|
||||
share must be indexed by Tracker.</para></listitem>
|
||||
</itemizedlist>
|
||||
@ -31,26 +26,6 @@
|
||||
url="https://wiki.samba.org/index.php/Spotlight">https://wiki.samba.org/index.php/Spotlight</ulink>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The Spotlight RPC service can either be enabled as embedded
|
||||
RPC service:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
<smbconfsection name="[Global]"/>
|
||||
<smbconfoption name="rpc_server:mdsvc">embedded</smbconfoption>
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
Or it can be run in a separate RPC service daemon:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
<smbconfsection name="[Global]"/>
|
||||
<smbconfoption name="rpc_server:mdssd">fork</smbconfoption>
|
||||
<smbconfoption name="rpc_server:mdsvc">external</smbconfoption>
|
||||
</programlisting>
|
||||
|
||||
</description>
|
||||
<value type="default">no</value>
|
||||
</samba:parameter>
|
||||
|
@ -1697,7 +1697,6 @@ sub setup_fileserver
|
||||
my $ip4 = Samba::get_ipv4_addr("FILESERVER");
|
||||
my $fileserver_options = "
|
||||
kernel change notify = yes
|
||||
rpc_server:mdssvc = embedded
|
||||
spotlight backend = elasticsearch
|
||||
elasticsearch:address = $ip4
|
||||
elasticsearch:port = 8080
|
||||
|
@ -1335,15 +1335,6 @@ winbindd:use external pipes = true
|
||||
server signing = enabled
|
||||
raw NTLMv2 auth = yes
|
||||
|
||||
rpc_server:default = external
|
||||
rpc_server:svcctl = embedded
|
||||
rpc_server:srvsvc = embedded
|
||||
rpc_server:eventlog = embedded
|
||||
rpc_server:ntsvcs = embedded
|
||||
rpc_server:winreg = embedded
|
||||
rpc_server:spoolss = embedded
|
||||
rpc_daemon:spoolssd = embedded
|
||||
rpc_server:tcpip = no
|
||||
# override the new SMB2 only default
|
||||
client min protocol = CORE
|
||||
server min protocol = LANMAN1
|
||||
|
@ -1,698 +0,0 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Common server globals
|
||||
|
||||
Copyright (C) Simo Sorce <idra@samba.org> 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "serverid.h"
|
||||
#include "messages.h"
|
||||
#include "system/time.h"
|
||||
#include "system/shmem.h"
|
||||
#include "system/filesys.h"
|
||||
#include "server_prefork.h"
|
||||
#include "../lib/util/samba_util.h"
|
||||
#include "../lib/util/tevent_unix.h"
|
||||
|
||||
struct prefork_pool {
|
||||
int listen_fd_size;
|
||||
struct pf_listen_fd *listen_fds;
|
||||
|
||||
prefork_main_fn_t *main_fn;
|
||||
void *private_data;
|
||||
|
||||
int pool_size;
|
||||
struct pf_worker_data *pool;
|
||||
|
||||
int allowed_clients;
|
||||
|
||||
prefork_sigchld_fn_t *sigchld_fn;
|
||||
void *sigchld_data;
|
||||
};
|
||||
|
||||
static bool prefork_setup_sigchld_handler(struct tevent_context *ev_ctx,
|
||||
struct prefork_pool *pfp);
|
||||
|
||||
static int prefork_pool_destructor(struct prefork_pool *pfp)
|
||||
{
|
||||
anonymous_shared_free(pfp->pool);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool prefork_create_pool(TALLOC_CTX *mem_ctx,
|
||||
struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
int listen_fd_size, struct pf_listen_fd *listen_fds,
|
||||
int min_children, int max_children,
|
||||
prefork_main_fn_t *main_fn, void *private_data,
|
||||
struct prefork_pool **pf_pool)
|
||||
{
|
||||
struct prefork_pool *pfp = NULL;
|
||||
pid_t pid;
|
||||
time_t now = time(NULL);
|
||||
size_t data_size;
|
||||
int ret;
|
||||
int i;
|
||||
bool ok;
|
||||
|
||||
pfp = talloc_zero(mem_ctx, struct prefork_pool);
|
||||
if (!pfp) {
|
||||
DEBUG(1, ("Out of memory!\n"));
|
||||
goto fail;
|
||||
}
|
||||
pfp->listen_fd_size = listen_fd_size;
|
||||
pfp->listen_fds = talloc_array(pfp, struct pf_listen_fd,
|
||||
listen_fd_size);
|
||||
if (!pfp->listen_fds) {
|
||||
DEBUG(1, ("Out of memory!\n"));
|
||||
goto fail;
|
||||
}
|
||||
for (i = 0; i < listen_fd_size; i++) {
|
||||
pfp->listen_fds[i] = listen_fds[i];
|
||||
/* force sockets in non-blocking mode */
|
||||
ret = set_blocking(listen_fds[i].fd, false);
|
||||
if (ret < 0) {
|
||||
DBG_WARNING("Failed to set sockets to non-blocking!\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
pfp->main_fn = main_fn;
|
||||
pfp->private_data = private_data;
|
||||
|
||||
pfp->pool_size = max_children;
|
||||
data_size = sizeof(struct pf_worker_data) * max_children;
|
||||
|
||||
pfp->pool = (struct pf_worker_data *)anonymous_shared_allocate(
|
||||
data_size);
|
||||
if (pfp->pool == NULL) {
|
||||
DEBUG(1, ("Failed to mmap memory for prefork pool!\n"));
|
||||
goto fail;
|
||||
}
|
||||
talloc_set_destructor(pfp, prefork_pool_destructor);
|
||||
|
||||
for (i = 0; i < min_children; i++) {
|
||||
|
||||
pfp->pool[i].allowed_clients = 1;
|
||||
pfp->pool[i].started = now;
|
||||
|
||||
pid = fork();
|
||||
switch (pid) {
|
||||
case -1:
|
||||
DEBUG(1, ("Failed to prefork child n. %d !\n", i));
|
||||
break;
|
||||
|
||||
case 0: /* THE CHILD */
|
||||
|
||||
pfp->pool[i].status = PF_WORKER_ALIVE;
|
||||
ret = pfp->main_fn(ev_ctx, msg_ctx,
|
||||
&pfp->pool[i], i + 1,
|
||||
pfp->listen_fd_size,
|
||||
pfp->listen_fds,
|
||||
pfp->private_data);
|
||||
exit(ret);
|
||||
|
||||
default: /* THE PARENT */
|
||||
pfp->pool[i].pid = pid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ok = prefork_setup_sigchld_handler(ev_ctx, pfp);
|
||||
if (!ok) {
|
||||
DEBUG(1, ("Failed to setup SIGCHLD Handler!\n"));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*pf_pool = pfp;
|
||||
return true;
|
||||
fail:
|
||||
TALLOC_FREE(pfp);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Provide the new max children number in new_max
|
||||
* (must be larger than current max).
|
||||
* Returns: 0 if all fine
|
||||
* ENOSPC if mremap fails to expand
|
||||
* EINVAL if new_max is invalid
|
||||
*/
|
||||
int prefork_expand_pool(struct prefork_pool *pfp, int new_max)
|
||||
{
|
||||
struct prefork_pool *pool = NULL;
|
||||
size_t old_size;
|
||||
size_t new_size;
|
||||
int ret;
|
||||
|
||||
if (new_max <= pfp->pool_size) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
old_size = sizeof(struct pf_worker_data) * pfp->pool_size;
|
||||
new_size = sizeof(struct pf_worker_data) * new_max;
|
||||
|
||||
pool = (struct prefork_pool *)anonymous_shared_resize(
|
||||
&pfp->pool, new_size, false);
|
||||
if (pool == NULL) {
|
||||
ret = errno;
|
||||
DEBUG(3, ("Failed to mremap memory (%d: %s)!\n",
|
||||
ret, strerror(ret)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
memset(&pool[pfp->pool_size], 0, new_size - old_size);
|
||||
|
||||
pfp->pool_size = new_max;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prefork_add_children(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct prefork_pool *pfp,
|
||||
int num_children)
|
||||
{
|
||||
pid_t pid;
|
||||
time_t now = time(NULL);
|
||||
int ret;
|
||||
int i, j;
|
||||
|
||||
for (i = 0, j = 0; i < pfp->pool_size && j < num_children; i++) {
|
||||
|
||||
if (pfp->pool[i].status != PF_WORKER_NONE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pfp->pool[i].allowed_clients = 1;
|
||||
pfp->pool[i].started = now;
|
||||
|
||||
pid = fork();
|
||||
switch (pid) {
|
||||
case -1:
|
||||
DEBUG(1, ("Failed to prefork child n. %d !\n", j));
|
||||
break;
|
||||
|
||||
case 0: /* THE CHILD */
|
||||
|
||||
pfp->pool[i].status = PF_WORKER_ALIVE;
|
||||
ret = pfp->main_fn(ev_ctx, msg_ctx,
|
||||
&pfp->pool[i], i + 1,
|
||||
pfp->listen_fd_size,
|
||||
pfp->listen_fds,
|
||||
pfp->private_data);
|
||||
|
||||
pfp->pool[i].status = PF_WORKER_EXITING;
|
||||
exit(ret);
|
||||
|
||||
default: /* THE PARENT */
|
||||
pfp->pool[i].pid = pid;
|
||||
j++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG(5, ("Added %d children!\n", j));
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
struct prefork_oldest {
|
||||
int num;
|
||||
time_t started;
|
||||
};
|
||||
|
||||
/* sort in inverse order */
|
||||
static int prefork_sort_oldest(const void *ap, const void *bp)
|
||||
{
|
||||
const struct prefork_oldest *a = (const struct prefork_oldest *)ap;
|
||||
const struct prefork_oldest *b = (const struct prefork_oldest *)bp;
|
||||
|
||||
if (a->started == b->started) {
|
||||
return 0;
|
||||
}
|
||||
if (a->started < b->started) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int prefork_retire_children(struct messaging_context *msg_ctx,
|
||||
struct prefork_pool *pfp,
|
||||
int num_children, time_t age_limit)
|
||||
{
|
||||
const DATA_BLOB ping = data_blob_null;
|
||||
time_t now = time(NULL);
|
||||
struct prefork_oldest *oldest = NULL;
|
||||
int i, j;
|
||||
|
||||
oldest = talloc_array(pfp, struct prefork_oldest, pfp->pool_size);
|
||||
if (!oldest) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < pfp->pool_size; i++) {
|
||||
oldest[i].num = i;
|
||||
if (pfp->pool[i].status == PF_WORKER_ALIVE ||
|
||||
pfp->pool[i].status == PF_WORKER_ACCEPTING) {
|
||||
oldest[i].started = pfp->pool[i].started;
|
||||
} else {
|
||||
oldest[i].started = now;
|
||||
}
|
||||
}
|
||||
|
||||
qsort(oldest, pfp->pool_size,
|
||||
sizeof(struct prefork_oldest),
|
||||
prefork_sort_oldest);
|
||||
|
||||
for (i = 0, j = 0; i < pfp->pool_size && j < num_children; i++) {
|
||||
if (((pfp->pool[i].status == PF_WORKER_ALIVE) &&
|
||||
(pfp->pool[i].num_clients < 1)) &&
|
||||
(pfp->pool[i].started <= age_limit)) {
|
||||
/* tell the child it's time to give up */
|
||||
DEBUG(5, ("Retiring pid %u!\n", (unsigned int)pfp->pool[i].pid));
|
||||
pfp->pool[i].cmds = PF_SRV_MSG_EXIT;
|
||||
messaging_send(msg_ctx,
|
||||
pid_to_procid(pfp->pool[i].pid),
|
||||
MSG_PREFORK_PARENT_EVENT, &ping);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
int prefork_count_children(struct prefork_pool *pfp, int *active)
|
||||
{
|
||||
int i, a, t;
|
||||
|
||||
a = 0;
|
||||
t = 0;
|
||||
for (i = 0; i < pfp->pool_size; i++) {
|
||||
if (pfp->pool[i].status == PF_WORKER_NONE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
t++;
|
||||
|
||||
if ((pfp->pool[i].status == PF_WORKER_EXITING) ||
|
||||
(pfp->pool[i].num_clients <= 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
a++;
|
||||
}
|
||||
|
||||
if (active) {
|
||||
*active = a;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
static void prefork_cleanup_loop(struct prefork_pool *pfp)
|
||||
{
|
||||
int status;
|
||||
pid_t pid;
|
||||
int i;
|
||||
|
||||
/* TODO: should we use a process group id wait instead of looping ? */
|
||||
for (i = 0; i < pfp->pool_size; i++) {
|
||||
if (pfp->pool[i].status == PF_WORKER_NONE ||
|
||||
pfp->pool[i].pid == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pid = waitpid(pfp->pool[i].pid, &status, WNOHANG);
|
||||
if (pid > 0) {
|
||||
|
||||
if (pfp->pool[i].status != PF_WORKER_EXITING) {
|
||||
DEBUG(3, ("Child (%d) terminated abnormally:"
|
||||
" %d\n", (int)pid, status));
|
||||
} else {
|
||||
DEBUG(10, ("Child (%d) terminated with status:"
|
||||
" %d\n", (int)pid, status));
|
||||
}
|
||||
|
||||
/* reset all fields,
|
||||
* this makes status = PF_WORK_NONE */
|
||||
memset(&pfp->pool[i], 0,
|
||||
sizeof(struct pf_worker_data));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int prefork_count_allowed_connections(struct prefork_pool *pfp)
|
||||
{
|
||||
int c;
|
||||
int i;
|
||||
|
||||
c = 0;
|
||||
for (i = 0; i < pfp->pool_size; i++) {
|
||||
if (pfp->pool[i].status == PF_WORKER_NONE ||
|
||||
pfp->pool[i].status == PF_WORKER_EXITING) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pfp->pool[i].num_clients < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
c += pfp->pool[i].allowed_clients - pfp->pool[i].num_clients;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void prefork_increase_allowed_clients(struct prefork_pool *pfp, int max)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pfp->pool_size; i++) {
|
||||
if (pfp->pool[i].status == PF_WORKER_NONE ||
|
||||
pfp->pool[i].status == PF_WORKER_EXITING) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pfp->pool[i].num_clients < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pfp->pool[i].allowed_clients < max) {
|
||||
pfp->pool[i].allowed_clients++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void prefork_decrease_allowed_clients(struct prefork_pool *pfp)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pfp->pool_size; i++) {
|
||||
if (pfp->pool[i].status == PF_WORKER_NONE ||
|
||||
pfp->pool[i].status == PF_WORKER_EXITING) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pfp->pool[i].num_clients < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pfp->pool[i].allowed_clients > 1) {
|
||||
pfp->pool[i].allowed_clients--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void prefork_reset_allowed_clients(struct prefork_pool *pfp)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pfp->pool_size; i++) {
|
||||
pfp->pool[i].allowed_clients = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void prefork_send_signal_to_all(struct prefork_pool *pfp, int signal_num)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pfp->pool_size; i++) {
|
||||
if (pfp->pool[i].status == PF_WORKER_NONE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
kill(pfp->pool[i].pid, signal_num);
|
||||
}
|
||||
}
|
||||
|
||||
void prefork_warn_active_children(struct messaging_context *msg_ctx,
|
||||
struct prefork_pool *pfp)
|
||||
{
|
||||
const DATA_BLOB ping = data_blob_null;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pfp->pool_size; i++) {
|
||||
if (pfp->pool[i].status == PF_WORKER_NONE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
messaging_send(msg_ctx,
|
||||
pid_to_procid(pfp->pool[i].pid),
|
||||
MSG_PREFORK_PARENT_EVENT, &ping);
|
||||
}
|
||||
}
|
||||
|
||||
static void prefork_sigchld_handler(struct tevent_context *ev_ctx,
|
||||
struct tevent_signal *se,
|
||||
int signum, int count,
|
||||
void *siginfo, void *pvt)
|
||||
{
|
||||
struct prefork_pool *pfp = talloc_get_type_abort(
|
||||
pvt, struct prefork_pool);
|
||||
|
||||
/* run the cleanup function to make sure all dead children are
|
||||
* properly and timely retired. */
|
||||
prefork_cleanup_loop(pfp);
|
||||
|
||||
if (pfp->sigchld_fn) {
|
||||
pfp->sigchld_fn(ev_ctx, pfp, pfp->sigchld_data);
|
||||
}
|
||||
}
|
||||
|
||||
static bool prefork_setup_sigchld_handler(struct tevent_context *ev_ctx,
|
||||
struct prefork_pool *pfp)
|
||||
{
|
||||
struct tevent_signal *se = NULL;
|
||||
|
||||
se = tevent_add_signal(ev_ctx, pfp, SIGCHLD, 0,
|
||||
prefork_sigchld_handler, pfp);
|
||||
if (!se) {
|
||||
DEBUG(0, ("Failed to setup SIGCHLD handler!\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void prefork_set_sigchld_callback(struct prefork_pool *pfp,
|
||||
prefork_sigchld_fn_t *sigchld_fn,
|
||||
void *private_data)
|
||||
{
|
||||
pfp->sigchld_fn = sigchld_fn;
|
||||
pfp->sigchld_data = private_data;
|
||||
}
|
||||
|
||||
/* ==== Functions used by children ==== */
|
||||
|
||||
struct pf_listen_state {
|
||||
struct tevent_context *ev;
|
||||
struct pf_worker_data *pf;
|
||||
|
||||
int listen_fd_size;
|
||||
struct pf_listen_fd *listen_fds;
|
||||
|
||||
struct pf_listen_fd accept;
|
||||
|
||||
struct tsocket_address *srv_addr;
|
||||
struct tsocket_address *cli_addr;
|
||||
|
||||
int error;
|
||||
};
|
||||
|
||||
struct pf_listen_ctx {
|
||||
TALLOC_CTX *fde_ctx;
|
||||
struct tevent_req *req;
|
||||
int listen_fd;
|
||||
void *listen_fd_data;
|
||||
};
|
||||
|
||||
static void prefork_listen_accept_handler(struct tevent_context *ev,
|
||||
struct tevent_fd *fde,
|
||||
uint16_t flags, void *pvt);
|
||||
|
||||
struct tevent_req *prefork_listen_send(TALLOC_CTX *mem_ctx,
|
||||
struct tevent_context *ev,
|
||||
struct pf_worker_data *pf,
|
||||
int listen_fd_size,
|
||||
struct pf_listen_fd *listen_fds)
|
||||
{
|
||||
struct tevent_req *req = NULL;
|
||||
struct pf_listen_state *state = NULL;
|
||||
struct pf_listen_ctx *ctx = NULL;
|
||||
struct tevent_fd *fde = NULL;
|
||||
TALLOC_CTX *fde_ctx = NULL;
|
||||
int i;
|
||||
|
||||
req = tevent_req_create(mem_ctx, &state, struct pf_listen_state);
|
||||
if (!req) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
state->ev = ev;
|
||||
state->pf = pf;
|
||||
state->listen_fd_size = listen_fd_size;
|
||||
state->listen_fds = listen_fds;
|
||||
state->accept.fd = -1;
|
||||
state->accept.fd_data = NULL;
|
||||
state->error = 0;
|
||||
|
||||
fde_ctx = talloc_new(state);
|
||||
if (tevent_req_nomem(fde_ctx, req)) {
|
||||
return tevent_req_post(req, ev);
|
||||
}
|
||||
|
||||
/* race on accept */
|
||||
for (i = 0; i < state->listen_fd_size; i++) {
|
||||
ctx = talloc(fde_ctx, struct pf_listen_ctx);
|
||||
if (tevent_req_nomem(ctx, req)) {
|
||||
return tevent_req_post(req, ev);
|
||||
}
|
||||
ctx->fde_ctx = fde_ctx;
|
||||
ctx->req = req;
|
||||
ctx->listen_fd = state->listen_fds[i].fd;
|
||||
ctx->listen_fd_data = state->listen_fds[i].fd_data;
|
||||
|
||||
fde = tevent_add_fd(state->ev, fde_ctx,
|
||||
ctx->listen_fd, TEVENT_FD_READ,
|
||||
prefork_listen_accept_handler, ctx);
|
||||
if (tevent_req_nomem(fde, req)) {
|
||||
return tevent_req_post(req, ev);
|
||||
}
|
||||
}
|
||||
|
||||
pf->status = PF_WORKER_ACCEPTING;
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
static void prefork_listen_accept_handler(struct tevent_context *ev,
|
||||
struct tevent_fd *fde,
|
||||
uint16_t flags, void *pvt)
|
||||
{
|
||||
struct pf_listen_ctx *ctx = talloc_get_type_abort(
|
||||
pvt, struct pf_listen_ctx);
|
||||
struct tevent_req *req = ctx->req;
|
||||
struct pf_listen_state *state = tevent_req_data(
|
||||
ctx->req, struct pf_listen_state);
|
||||
struct sockaddr_storage addr = { .ss_family = 0 };
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
int soerr = 0;
|
||||
socklen_t solen = sizeof(soerr);
|
||||
int sd = -1;
|
||||
int ret;
|
||||
|
||||
if ((state->pf->cmds == PF_SRV_MSG_EXIT) &&
|
||||
(state->pf->num_clients <= 0)) {
|
||||
/* We have been asked to exit, so drop here and the next
|
||||
* child will pick it up */
|
||||
state->pf->status = PF_WORKER_EXITING;
|
||||
state->error = EINTR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* before proceeding check that the listening fd is ok */
|
||||
ret = getsockopt(ctx->listen_fd, SOL_SOCKET, SO_ERROR, &soerr, &solen);
|
||||
if (ret == -1) {
|
||||
/* this is a fatal error, we cannot continue listening */
|
||||
state->error = EBADF;
|
||||
goto done;
|
||||
}
|
||||
if (soerr != 0) {
|
||||
/* this is a fatal error, we cannot continue listening */
|
||||
state->error = soerr;
|
||||
goto done;
|
||||
}
|
||||
|
||||
sd = accept(ctx->listen_fd, (struct sockaddr *)&addr, &addrlen);
|
||||
if (sd == -1) {
|
||||
state->error = errno;
|
||||
DEBUG(6, ("Accept failed! (%d, %s)\n",
|
||||
state->error, strerror(state->error)));
|
||||
goto done;
|
||||
}
|
||||
smb_set_close_on_exec(sd);
|
||||
|
||||
state->accept.fd = sd;
|
||||
state->accept.fd_data = ctx->listen_fd_data;
|
||||
|
||||
ret = tsocket_address_bsd_from_sockaddr(state,
|
||||
(struct sockaddr *)(void *)&addr,
|
||||
addrlen, &state->cli_addr);
|
||||
if (ret < 0) {
|
||||
state->error = errno;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ZERO_STRUCT(addr);
|
||||
addrlen = sizeof(addr);
|
||||
ret = getsockname(sd, (struct sockaddr *)(void *)&addr, &addrlen);
|
||||
if (ret < 0) {
|
||||
state->error = errno;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = tsocket_address_bsd_from_sockaddr(state,
|
||||
(struct sockaddr *)(void *)&addr,
|
||||
addrlen, &state->srv_addr);
|
||||
if (ret < 0) {
|
||||
state->error = errno;
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
/* do not track the listen fds anymore */
|
||||
talloc_free(ctx->fde_ctx);
|
||||
tevent_req_done(req);
|
||||
}
|
||||
|
||||
int prefork_listen_recv(struct tevent_req *req,
|
||||
TALLOC_CTX *mem_ctx, int *fd,
|
||||
void **fd_data,
|
||||
struct tsocket_address **srv_addr,
|
||||
struct tsocket_address **cli_addr)
|
||||
{
|
||||
struct pf_listen_state *state = tevent_req_data(
|
||||
req, struct pf_listen_state);
|
||||
int ret = 0;
|
||||
|
||||
if (state->error) {
|
||||
ret = state->error;
|
||||
} else {
|
||||
if (!tevent_req_is_unix_error(req, &ret)) {
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
if (state->accept.fd != -1) {
|
||||
close(state->accept.fd);
|
||||
}
|
||||
} else {
|
||||
*fd = state->accept.fd;
|
||||
if (fd_data != NULL) {
|
||||
*fd_data = state->accept.fd_data;
|
||||
}
|
||||
*srv_addr = talloc_move(mem_ctx, &state->srv_addr);
|
||||
*cli_addr = talloc_move(mem_ctx, &state->cli_addr);
|
||||
state->pf->num_clients++;
|
||||
}
|
||||
if (state->pf->status == PF_WORKER_ACCEPTING) {
|
||||
state->pf->status = PF_WORKER_ALIVE;
|
||||
}
|
||||
|
||||
tevent_req_received(req);
|
||||
return ret;
|
||||
}
|
@ -1,312 +0,0 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Common server globals
|
||||
|
||||
Copyright (C) Simo Sorce <idra@samba.org> 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _SOURCE3_LIB_SERVER_PREFORK_H_
|
||||
#define _SOURCE3_LIB_SERVER_PREFORK_H_
|
||||
|
||||
#include "system/network.h"
|
||||
#include <tevent.h>
|
||||
#include "lib/tsocket/tsocket.h"
|
||||
|
||||
struct prefork_pool;
|
||||
|
||||
enum pf_worker_status {
|
||||
PF_WORKER_NONE = 0,
|
||||
PF_WORKER_ALIVE,
|
||||
PF_WORKER_ACCEPTING,
|
||||
PF_WORKER_EXITING
|
||||
};
|
||||
|
||||
enum pf_server_cmds {
|
||||
PF_SRV_MSG_NONE = 0,
|
||||
PF_SRV_MSG_EXIT
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This structure contains a socket listening for clients and a
|
||||
* private pointer with any data associated to that particular
|
||||
* socket.
|
||||
*/
|
||||
struct pf_listen_fd {
|
||||
/* The socket to listen on */
|
||||
int fd;
|
||||
|
||||
/* The socket associated data */
|
||||
void *fd_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This structure is shared between the controlling parent and the
|
||||
* the child. The parent can only write to the 'cmds' and
|
||||
* 'allowed_clients' variables, while a child is running.
|
||||
* The child can change 'status', and 'num_clients'.
|
||||
* All other variables are initialized by the parent before forking the
|
||||
* child.
|
||||
*/
|
||||
struct pf_worker_data {
|
||||
pid_t pid;
|
||||
enum pf_worker_status status;
|
||||
time_t started;
|
||||
time_t last_used;
|
||||
int num_clients;
|
||||
|
||||
enum pf_server_cmds cmds;
|
||||
int allowed_clients;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This is the 'main' function called by a child right after the fork.
|
||||
* It is daemon specific and should initialize and perform whatever
|
||||
* operation the child is meant to do. Returning from this function will
|
||||
* cause the termination of the child.
|
||||
*
|
||||
* @param ev The event context
|
||||
* @param msg_ctx The messaging context
|
||||
* @param pf The mmaped area used to communicate with parent
|
||||
* @param listen_fd_size The number of file descriptors to monitor
|
||||
* @param listen_fds The array of file descriptors
|
||||
* @param private_data Private data that needs to be passed to the main
|
||||
* function from the calling parent.
|
||||
*
|
||||
* @return Returns the exit status to be reported to the parent via exit()
|
||||
*/
|
||||
typedef int (prefork_main_fn_t)(struct tevent_context *ev,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct pf_worker_data *pf,
|
||||
int child_id,
|
||||
int listen_fd_size,
|
||||
struct pf_listen_fd *pf_listen_fds,
|
||||
void *private_data);
|
||||
|
||||
/**
|
||||
* @brief Callback function for parents that also want to be called on sigchld
|
||||
*
|
||||
* @param ev_ctx The event context
|
||||
* @param pool The pool handler
|
||||
* @param private_data Data private to the parent
|
||||
*/
|
||||
typedef void (prefork_sigchld_fn_t)(struct tevent_context *ev_ctx,
|
||||
struct prefork_pool *pool,
|
||||
void *private_data);
|
||||
|
||||
/* ==== Functions used by controlling process ==== */
|
||||
|
||||
/**
|
||||
* @brief Creates the first pool of preforked processes
|
||||
*
|
||||
* @param mem_ctx The memory context used to hold the pool structure
|
||||
* @param ev_ctx The event context
|
||||
* @param msg_ctx The messaging context
|
||||
* @param listen_fd_size The number of file descriptors to monitor
|
||||
* @param listen_fds The array of file descriptors to monitor
|
||||
* @param min_children Minimum number of children that must be available at
|
||||
* any given time
|
||||
* @param max_children Maximum number of children that can be started. Also
|
||||
* determines the initial size of the pool.
|
||||
* @param main_fn The children 'main' function to be called after fork
|
||||
* @param private_data The children private data.
|
||||
* @param pf_pool The allocated pool.
|
||||
*
|
||||
* @return True if it was successful, False otherwise.
|
||||
*
|
||||
* NOTE: each listen_fd is forced to non-blocking mode once handed over.
|
||||
* You should not touch listen_fds once you hand them to the prefork library.
|
||||
*/
|
||||
bool prefork_create_pool(TALLOC_CTX *mem_ctx,
|
||||
struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
int listen_fd_size, struct pf_listen_fd *listen_fds,
|
||||
int min_children, int max_children,
|
||||
prefork_main_fn_t *main_fn, void *private_data,
|
||||
struct prefork_pool **pf_pool);
|
||||
/**
|
||||
* @brief Function used to attempt to expand the size of children.
|
||||
*
|
||||
* @param pfp The pool structure.
|
||||
* @param new_max The new max number of children.
|
||||
*
|
||||
* @return 0 if operation was successful
|
||||
* ENOSPC if the mmap area could not be grown to the requested size
|
||||
* EINVAL if the new max is invalid.
|
||||
*
|
||||
* NOTE: this function can easily fail if the mmap area cannot be enlarged.
|
||||
* A well behaving parent MUST NOT error out if this happen.
|
||||
*/
|
||||
int prefork_expand_pool(struct prefork_pool *pfp, int new_max);
|
||||
|
||||
/**
|
||||
* @brief Used to prefork a number of new children
|
||||
*
|
||||
* @param ev_ctx The event context
|
||||
* @param msg_ctx The messaging context
|
||||
* @param pfp The pool structure
|
||||
* @param num_children The number of children to be started
|
||||
*
|
||||
* @return The number of new children effectively forked.
|
||||
*
|
||||
* NOTE: This method does not expand the pool, if the max number of children
|
||||
* has already been forked it will do nothing.
|
||||
*/
|
||||
int prefork_add_children(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct prefork_pool *pfp,
|
||||
int num_children);
|
||||
/**
|
||||
* @brief Commands a number of children to stop and exit
|
||||
*
|
||||
* @param msg_ctx The messaging context.
|
||||
* @param pfp The pool.
|
||||
* @param num_children The number of children we need to retire.
|
||||
* @param age_limit The minimum age a child has been active to be
|
||||
* considered for retirement. (Compared against the
|
||||
* 'started' value in the pf_worker_data structure of the
|
||||
* children.
|
||||
*
|
||||
* @return Number of children that were signaled to stop
|
||||
*
|
||||
* NOTE: Only children that have no attached clients can be stopped.
|
||||
* If all the available children are too young or are busy then it
|
||||
* is possible that none will be asked to stop.
|
||||
*/
|
||||
int prefork_retire_children(struct messaging_context *msg_ctx,
|
||||
struct prefork_pool *pfp,
|
||||
int num_children, time_t age_limit);
|
||||
/**
|
||||
* @brief Count the number of children
|
||||
*
|
||||
* @param pfp The pool.
|
||||
* @param active Number of children currently active if not NULL
|
||||
*
|
||||
* @return The total number of children.
|
||||
*/
|
||||
int prefork_count_children(struct prefork_pool *pfp, int *active);
|
||||
|
||||
/**
|
||||
* @brief Count the number of actual connections currently allowed
|
||||
*
|
||||
* @param pfp The pool.
|
||||
*
|
||||
* @return The number of connections that can still be opened by clients
|
||||
* with the current pool of children.
|
||||
*/
|
||||
int prefork_count_allowed_connections(struct prefork_pool *pfp);
|
||||
|
||||
/**
|
||||
* @brief Increase the amount of clients each child is allowed to handle
|
||||
* simultaneaously. It will allow each child to handle more than
|
||||
* one client at a time, up to 'max' (currently set to 100).
|
||||
*
|
||||
* @param pfp The pool.
|
||||
* @param max Max number of allowed connections per child
|
||||
*/
|
||||
void prefork_increase_allowed_clients(struct prefork_pool *pfp, int max);
|
||||
|
||||
/**
|
||||
* @brief Decrease the amount of clients each child is allowed to handle.
|
||||
* Min is 1.
|
||||
*
|
||||
* @param pfp The pool.
|
||||
*/
|
||||
void prefork_decrease_allowed_clients(struct prefork_pool *pfp);
|
||||
|
||||
/**
|
||||
* @brief Reset the maximum allowed clients per child to 1.
|
||||
* Does not reduce the number of clients actually beeing served by
|
||||
* any given child, but prevents children from overcommitting from
|
||||
* now on.
|
||||
*
|
||||
* @param pfp The pool.
|
||||
*/
|
||||
void prefork_reset_allowed_clients(struct prefork_pool *pfp);
|
||||
|
||||
/**
|
||||
* @brief Send a specific signal to all children.
|
||||
* Used to send SIGHUP when a reload of the configuration is needed
|
||||
* for example.
|
||||
*
|
||||
* @param pfp The pool.
|
||||
* @param signal_num The signal number to be sent.
|
||||
*/
|
||||
void prefork_send_signal_to_all(struct prefork_pool *pfp, int signal_num);
|
||||
|
||||
/**
|
||||
* @brief Send a message to all children that the server changed something
|
||||
* in the pool and they may want to take action.
|
||||
*
|
||||
* @param msg_ctx The messaging context.
|
||||
* @param pfp The pool.
|
||||
*/
|
||||
void prefork_warn_active_children(struct messaging_context *msg_ctx,
|
||||
struct prefork_pool *pfp);
|
||||
|
||||
/**
|
||||
* @brief Sets the SIGCHLD callback
|
||||
*
|
||||
* @param pfp The pool handler.
|
||||
* @param sigchld_fn The callback function (pass NULL to unset).
|
||||
* @param private_data Private data for the callback function.
|
||||
*/
|
||||
void prefork_set_sigchld_callback(struct prefork_pool *pfp,
|
||||
prefork_sigchld_fn_t *sigchld_fn,
|
||||
void *private_data);
|
||||
|
||||
/* ==== Functions used by children ==== */
|
||||
|
||||
/**
|
||||
* @brief Try to listen and accept on one of the listening sockets.
|
||||
* Asynchronously tries to grab the lock and perform an accept.
|
||||
* Will automatically update the 'status' of the child and handle
|
||||
* all the locking/unlocking/timingout as necessary.
|
||||
* Changes behavior depending on whether the child already has other
|
||||
* client connections. If not it blocks on the lock call for periods of
|
||||
* time. Otherwise it loops on the lock using a timer in order to allow
|
||||
* processing of the other clients requests.
|
||||
*
|
||||
* @param mem_ctx The memory context on whic to allocate the request
|
||||
* @param ev The event context
|
||||
* @param pf The child/parent shared structure
|
||||
* @param listen_fd_size The number of listening file descriptors
|
||||
* @param listen_fds The array of listening file descriptors
|
||||
*
|
||||
* @return The tevent request pointer or NULL on allocation errors.
|
||||
*/
|
||||
struct tevent_req *prefork_listen_send(TALLOC_CTX *mem_ctx,
|
||||
struct tevent_context *ev,
|
||||
struct pf_worker_data *pf,
|
||||
int listen_fd_size,
|
||||
struct pf_listen_fd *listen_fds);
|
||||
/**
|
||||
* @brief Returns the file descriptor after the new client connection has
|
||||
* been accepted.
|
||||
*
|
||||
* @param req The request
|
||||
* @param mem_ctx The memory context for cli_addr and srv_addr
|
||||
* @param fd The new file descriptor.
|
||||
* @param srv_addr The server address in tsocket_address format
|
||||
* @param cli_addr The client address in tsocket_address format
|
||||
*
|
||||
* @return The error in case the operation failed.
|
||||
*/
|
||||
int prefork_listen_recv(struct tevent_req *req,
|
||||
TALLOC_CTX *mem_ctx, int *fd, void **fd_data,
|
||||
struct tsocket_address **srv_addr,
|
||||
struct tsocket_address **cli_addr);
|
||||
|
||||
#endif /* _SOURCE3_LIB_SERVER_PREFORK_H_ */
|
@ -1,169 +0,0 @@
|
||||
/*
|
||||
Unix SMB/Netbios implementation.
|
||||
Prefork Helpers
|
||||
Copyright (C) Simo Sorce <idra@samba.org> 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/server_prefork.h"
|
||||
#include "lib/server_prefork_util.h"
|
||||
|
||||
void pfh_daemon_config(const char *daemon_name,
|
||||
struct pf_daemon_config *cfg,
|
||||
const struct pf_daemon_config *default_cfg)
|
||||
{
|
||||
int min, max, rate, allow, life;
|
||||
|
||||
min = lp_parm_int(GLOBAL_SECTION_SNUM,
|
||||
daemon_name,
|
||||
"prefork_min_children",
|
||||
default_cfg->min_children);
|
||||
max = lp_parm_int(GLOBAL_SECTION_SNUM,
|
||||
daemon_name,
|
||||
"prefork_max_children",
|
||||
default_cfg->max_children);
|
||||
rate = lp_parm_int(GLOBAL_SECTION_SNUM,
|
||||
daemon_name,
|
||||
"prefork_spawn_rate",
|
||||
default_cfg->spawn_rate);
|
||||
allow = lp_parm_int(GLOBAL_SECTION_SNUM,
|
||||
daemon_name,
|
||||
"prefork_max_allowed_clients",
|
||||
default_cfg->max_allowed_clients);
|
||||
life = lp_parm_int(GLOBAL_SECTION_SNUM,
|
||||
daemon_name,
|
||||
"prefork_child_min_life",
|
||||
default_cfg->child_min_life);
|
||||
|
||||
if (max > cfg->max_children && cfg->max_children != 0) {
|
||||
cfg->prefork_status |= PFH_NEW_MAX;
|
||||
}
|
||||
|
||||
cfg->min_children = min;
|
||||
cfg->max_children = max;
|
||||
cfg->spawn_rate = rate;
|
||||
cfg->max_allowed_clients = allow;
|
||||
cfg->child_min_life = life;
|
||||
}
|
||||
|
||||
void pfh_manage_pool(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct pf_daemon_config *cfg,
|
||||
struct prefork_pool *pool)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
int total, avail;
|
||||
int ret, n;
|
||||
bool msg = false;
|
||||
|
||||
if ((cfg->prefork_status & PFH_NEW_MAX) &&
|
||||
!(cfg->prefork_status & PFH_ENOSPC)) {
|
||||
ret = prefork_expand_pool(pool, cfg->max_children);
|
||||
if (ret == ENOSPC) {
|
||||
cfg->prefork_status |= PFH_ENOSPC;
|
||||
}
|
||||
cfg->prefork_status &= ~PFH_NEW_MAX;
|
||||
}
|
||||
|
||||
total = prefork_count_children(pool, NULL);
|
||||
avail = prefork_count_allowed_connections(pool);
|
||||
DEBUG(10, ("(Pre)Stats: children: %d, allowed connections: %d\n",
|
||||
total, avail));
|
||||
|
||||
if ((total < cfg->max_children) && (avail < cfg->spawn_rate)) {
|
||||
n = prefork_add_children(ev_ctx, msg_ctx,
|
||||
pool, cfg->spawn_rate);
|
||||
if (n < cfg->spawn_rate) {
|
||||
DEBUG(10, ("Attempted to add %d children but only "
|
||||
"%d were actually added!\n",
|
||||
cfg->spawn_rate, n));
|
||||
}
|
||||
} else if ((avail - cfg->min_children) >= cfg->spawn_rate) {
|
||||
/* be a little slower in retiring children, to allow for
|
||||
* double spikes of traffic to be handled more gracefully */
|
||||
n = (cfg->spawn_rate / 2) + 1;
|
||||
if (n > cfg->spawn_rate) {
|
||||
n = cfg->spawn_rate;
|
||||
}
|
||||
if ((total - n) < cfg->min_children) {
|
||||
n = total - cfg->min_children;
|
||||
}
|
||||
if (n >= 0) {
|
||||
prefork_retire_children(msg_ctx, pool, n,
|
||||
now - cfg->child_min_life);
|
||||
}
|
||||
}
|
||||
|
||||
/* total/avail may have just been changed in the above if/else */
|
||||
total = prefork_count_children(pool, NULL);
|
||||
avail = prefork_count_allowed_connections(pool);
|
||||
if ((total == cfg->max_children) && (avail < cfg->spawn_rate)) {
|
||||
n = avail;
|
||||
while (avail < cfg->spawn_rate) {
|
||||
prefork_increase_allowed_clients(pool,
|
||||
cfg->max_allowed_clients);
|
||||
avail = prefork_count_allowed_connections(pool);
|
||||
/* if avail didn't change do not loop forever */
|
||||
if (n == avail) break;
|
||||
n = avail;
|
||||
}
|
||||
msg = true;
|
||||
} else if (avail > total + cfg->spawn_rate) {
|
||||
n = avail;
|
||||
while (avail > total + cfg->spawn_rate) {
|
||||
prefork_decrease_allowed_clients(pool);
|
||||
avail = prefork_count_allowed_connections(pool);
|
||||
/* if avail didn't change do not loop forever */
|
||||
if (n == avail) break;
|
||||
n = avail;
|
||||
}
|
||||
}
|
||||
|
||||
/* send message to all children when we change maximum allowed
|
||||
* connections, so that they can decide to start again to listen to
|
||||
* sockets if they were already topping the number of allowed
|
||||
* clients. Useful only when we increase allowed clients */
|
||||
if (msg) {
|
||||
prefork_warn_active_children(msg_ctx, pool);
|
||||
}
|
||||
|
||||
DEBUG(10, ("Stats: children: %d, allowed connections: %d\n",
|
||||
prefork_count_children(pool, NULL),
|
||||
prefork_count_allowed_connections(pool)));
|
||||
}
|
||||
|
||||
void pfh_client_terminated(struct pf_worker_data *pf)
|
||||
{
|
||||
if (pf->num_clients >= 0) {
|
||||
pf->num_clients--;
|
||||
} else {
|
||||
if (pf->status != PF_WORKER_EXITING) {
|
||||
DEBUG(1, ("Invalid num clients, stopping!\n"));
|
||||
}
|
||||
pf->status = PF_WORKER_EXITING;
|
||||
pf->num_clients = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool pfh_child_allowed_to_accept(struct pf_worker_data *pf)
|
||||
{
|
||||
if (pf->status == PF_WORKER_EXITING ||
|
||||
pf->status == PF_WORKER_ACCEPTING) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (pf->num_clients < pf->allowed_clients);
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Prefork Helpers.
|
||||
|
||||
Copyright (C) Simo Sorce <idra@samba.org> 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _SERVER_PREFORK_UTIL_H_
|
||||
#define _SERVER_PREFORK_UTIL_H_
|
||||
|
||||
struct tevent_context;
|
||||
struct messaging_context;
|
||||
|
||||
#define PFH_INIT 0x00
|
||||
#define PFH_NEW_MAX 0x01
|
||||
#define PFH_ENOSPC 0x02
|
||||
|
||||
struct pf_daemon_config {
|
||||
int prefork_status;
|
||||
int min_children;
|
||||
int max_children;
|
||||
int spawn_rate;
|
||||
int max_allowed_clients;
|
||||
int child_min_life;
|
||||
};
|
||||
|
||||
void pfh_daemon_config(const char *daemon_name,
|
||||
struct pf_daemon_config *cfg,
|
||||
const struct pf_daemon_config *default_cfg);
|
||||
|
||||
void pfh_manage_pool(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct pf_daemon_config *cfg,
|
||||
struct prefork_pool *pool);
|
||||
|
||||
void pfh_client_terminated(struct pf_worker_data *pf);
|
||||
bool pfh_child_allowed_to_accept(struct pf_worker_data *pf);
|
||||
|
||||
#endif /* _SERVER_PREFORK_UTIL_H_ */
|
@ -1,300 +0,0 @@
|
||||
/*
|
||||
* Endpoint Mapper Functions
|
||||
* DCERPC local endpoint mapper client routines
|
||||
* Copyright (c) 2010-2011 Andreas Schneider.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "librpc/rpc/dcerpc.h"
|
||||
#include "librpc/rpc/dcerpc_ep.h"
|
||||
#include "../librpc/gen_ndr/ndr_epmapper_c.h"
|
||||
#include "rpc_client/cli_pipe.h"
|
||||
#include "auth.h"
|
||||
#include "rpc_server/rpc_ncacn_np.h"
|
||||
#include "../lib/tsocket/tsocket.h"
|
||||
|
||||
#include "librpc/rpc/dcesrv_core.h"
|
||||
#include "rpc_server/rpc_config.h"
|
||||
|
||||
#define EPM_MAX_ANNOTATION_SIZE 64
|
||||
|
||||
static NTSTATUS ep_register(TALLOC_CTX *mem_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
const struct dcesrv_interface *iface,
|
||||
const struct GUID *object_guid,
|
||||
const char *annotation,
|
||||
uint32_t replace,
|
||||
uint32_t unregister,
|
||||
struct dcerpc_binding_handle **pbh)
|
||||
{
|
||||
struct rpc_pipe_client *cli = NULL;
|
||||
struct dcerpc_binding_handle *h;
|
||||
struct pipe_auth_data *auth;
|
||||
enum rpc_service_mode_e epmd_mode;
|
||||
struct epm_entry_t *entries = NULL;
|
||||
uint32_t i = 0;
|
||||
TALLOC_CTX *tmp_ctx;
|
||||
uint32_t result = EPMAPPER_STATUS_OK;
|
||||
NTSTATUS status;
|
||||
struct dcesrv_endpoint *ep;
|
||||
bool found = false;
|
||||
|
||||
if (iface == NULL) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (dce_ctx == NULL || iface == NULL) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Check if interface is registered */
|
||||
for (ep = dce_ctx->endpoint_list; ep; ep = ep->next) {
|
||||
struct dcesrv_if_list *ifl;
|
||||
for (ifl = ep->interface_list; ifl; ifl = ifl->next) {
|
||||
if (ndr_syntax_id_equal(&ifl->iface->syntax_id,
|
||||
&iface->syntax_id)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
DBG_ERR("Failed to register interface '%s' in the endpoint "
|
||||
"mapper as it is not registered in any endpoint\n",
|
||||
iface->name);
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
tmp_ctx = talloc_stackframe();
|
||||
|
||||
epmd_mode = rpc_epmapper_mode();
|
||||
|
||||
if (epmd_mode == RPC_SERVICE_MODE_EMBEDDED) {
|
||||
struct tsocket_address *local;
|
||||
int rc;
|
||||
|
||||
rc = tsocket_address_inet_from_strings(tmp_ctx,
|
||||
"ip",
|
||||
"127.0.0.1",
|
||||
0,
|
||||
&local);
|
||||
if (rc < 0) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = rpcint_binding_handle(tmp_ctx,
|
||||
&ndr_table_epmapper,
|
||||
local,
|
||||
NULL,
|
||||
get_session_info_system(),
|
||||
msg_ctx,
|
||||
&h);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(1, ("dcerpc_ep_register: Could not connect to "
|
||||
"epmapper (%s)", nt_errstr(status)));
|
||||
goto done;
|
||||
}
|
||||
} else if (epmd_mode == RPC_SERVICE_MODE_EXTERNAL) {
|
||||
/* Connect to the endpoint mapper locally */
|
||||
status = rpc_pipe_open_ncalrpc(tmp_ctx,
|
||||
&ndr_table_epmapper,
|
||||
&cli);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = rpccli_ncalrpc_bind_data(cli, &auth);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0, ("Failed to initialize anonymous bind.\n"));
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = rpc_pipe_bind(cli, auth);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(2, ("Failed to bind ncalrpc socket.\n"));
|
||||
goto done;
|
||||
}
|
||||
|
||||
h = cli->binding_handle;
|
||||
} else {
|
||||
status = NT_STATUS_INVALID_PARAMETER;
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 0, ep = dce_ctx->endpoint_list; ep; i++, ep = ep->next) {
|
||||
struct dcerpc_binding *map_binding;
|
||||
struct epm_twr_t *map_tower;
|
||||
struct dcesrv_if_list *ifl;
|
||||
|
||||
for (ifl = ep->interface_list; ifl; ifl = ifl->next) {
|
||||
if (!ndr_syntax_id_equal(&ifl->iface->syntax_id,
|
||||
&iface->syntax_id)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* The interface is registered in this endpoint, add it */
|
||||
entries = talloc_realloc(tmp_ctx, entries, struct epm_entry_t,
|
||||
i + 1);
|
||||
if (entries == NULL) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
map_binding = dcerpc_binding_dup(entries, ep->ep_description);
|
||||
if (map_binding == NULL) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = dcerpc_binding_set_abstract_syntax(map_binding,
|
||||
&iface->syntax_id);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
map_tower = talloc_zero(entries, struct epm_twr_t);
|
||||
if (map_tower == NULL) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = dcerpc_binding_build_tower(entries,
|
||||
map_binding,
|
||||
&map_tower->tower);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
TALLOC_FREE(map_binding);
|
||||
|
||||
entries[i].tower = map_tower;
|
||||
if (annotation == NULL) {
|
||||
entries[i].annotation = talloc_strdup(entries, "");
|
||||
} else {
|
||||
entries[i].annotation = talloc_strndup(entries,
|
||||
annotation,
|
||||
EPM_MAX_ANNOTATION_SIZE);
|
||||
}
|
||||
if (entries[i].annotation == NULL) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (object_guid != NULL) {
|
||||
entries[i].object = *object_guid;
|
||||
} else {
|
||||
ZERO_STRUCT(entries[i].object);
|
||||
}
|
||||
}
|
||||
|
||||
if (unregister) {
|
||||
status = dcerpc_epm_Delete(h,
|
||||
tmp_ctx,
|
||||
i,
|
||||
entries,
|
||||
&result);
|
||||
} else {
|
||||
status = dcerpc_epm_Insert(h,
|
||||
tmp_ctx,
|
||||
i,
|
||||
entries,
|
||||
replace,
|
||||
&result);
|
||||
}
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0, ("dcerpc_ep_register: Could not insert tower (%s)\n",
|
||||
nt_errstr(status)));
|
||||
goto done;
|
||||
}
|
||||
if (result != EPMAPPER_STATUS_OK) {
|
||||
DEBUG(0, ("dcerpc_ep_register: Could not insert tower (0x%.8x)\n",
|
||||
result));
|
||||
status = NT_STATUS_UNSUCCESSFUL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (pbh != NULL) {
|
||||
talloc_steal(h, cli);
|
||||
*pbh = talloc_move(mem_ctx, &h);
|
||||
}
|
||||
|
||||
done:
|
||||
talloc_free(tmp_ctx);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
NTSTATUS dcerpc_ep_register(TALLOC_CTX *mem_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
const struct dcesrv_interface *iface,
|
||||
const struct GUID *object_guid,
|
||||
const char *annotation,
|
||||
struct dcerpc_binding_handle **ph)
|
||||
{
|
||||
return ep_register(mem_ctx,
|
||||
msg_ctx,
|
||||
dce_ctx,
|
||||
iface,
|
||||
object_guid,
|
||||
annotation,
|
||||
1,
|
||||
0,
|
||||
ph);
|
||||
}
|
||||
|
||||
NTSTATUS dcerpc_ep_register_noreplace(TALLOC_CTX *mem_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
const struct dcesrv_interface *iface,
|
||||
const struct GUID *object_guid,
|
||||
const char *annotation,
|
||||
struct dcerpc_binding_handle **ph)
|
||||
{
|
||||
return ep_register(mem_ctx,
|
||||
msg_ctx,
|
||||
dce_ctx,
|
||||
iface,
|
||||
object_guid,
|
||||
annotation,
|
||||
0,
|
||||
0,
|
||||
ph);
|
||||
}
|
||||
|
||||
NTSTATUS dcerpc_ep_unregister(struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
const struct dcesrv_interface *iface,
|
||||
const struct GUID *object_guid)
|
||||
{
|
||||
return ep_register(NULL,
|
||||
msg_ctx,
|
||||
dce_ctx,
|
||||
iface,
|
||||
object_guid,
|
||||
NULL,
|
||||
0,
|
||||
1,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */
|
@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Endpoint Mapper Functions
|
||||
* DCERPC local endpoint mapper client routines
|
||||
* Copyright (c) 2010-2011 Andreas Schneider.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _DCERPC_EP_H_
|
||||
#define _DCERPC_EP_H_
|
||||
|
||||
struct dcesrv_context;
|
||||
struct dcesrv_interface;
|
||||
|
||||
/**
|
||||
* @brief Adds server address information in the local endpoint map.
|
||||
*
|
||||
* @param[in] mem_ctx The memory context to use for the binding handle.
|
||||
*
|
||||
* @param[in] dce_ctx The dcerpc server context
|
||||
*
|
||||
* @param[in] iface The interface to register in the endpoint mapper
|
||||
*
|
||||
* @param[in] object_guid The object GUID that the server offers. The server
|
||||
* application constructs this vector.
|
||||
*
|
||||
* @param[in] annotation Defines a character string comment applied to the
|
||||
* element added to the local endpoint map. The string
|
||||
* can be up to 64 characters long, including the null
|
||||
* terminating character. Strings longer than 64
|
||||
* characters are truncated. The application supplies
|
||||
* the value NULL or the string "" to indicate an empty
|
||||
* annotation string.
|
||||
*
|
||||
* When replacing elements, the annotation string
|
||||
* supplied, including an empty annotation string,
|
||||
* replaces any existing annotation string.
|
||||
*
|
||||
* @param[out] ph A pointer to store the binding handle. The memory
|
||||
* context will be the give one. If you free this handle
|
||||
* then the connection will be closed.
|
||||
*
|
||||
* @return An NTSTATUS error code.
|
||||
*/
|
||||
NTSTATUS dcerpc_ep_register(TALLOC_CTX *mem_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
const struct dcesrv_interface *iface,
|
||||
const struct GUID *object_guid,
|
||||
const char *annotation,
|
||||
struct dcerpc_binding_handle **ph);
|
||||
|
||||
NTSTATUS dcerpc_ep_register_noreplace(TALLOC_CTX *mem_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
const struct dcesrv_interface *iface,
|
||||
const struct GUID *object_guid,
|
||||
const char *annotation,
|
||||
struct dcerpc_binding_handle **ph);
|
||||
|
||||
NTSTATUS dcerpc_ep_unregister(struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
const struct dcesrv_interface *iface,
|
||||
const struct GUID *object_guid);
|
||||
|
||||
#endif /* _DCERPC_EP_H_ */
|
@ -1,822 +0,0 @@
|
||||
/*
|
||||
Unix SMB/Netbios implementation.
|
||||
SPOOLSS Daemon
|
||||
Copyright (C) Simo Sorce <idra@samba.org> 2010-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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "includes.h"
|
||||
#include "locking/share_mode_lock.h"
|
||||
#include "smbd/smbd.h"
|
||||
|
||||
#include "messages.h"
|
||||
#include "include/printing.h"
|
||||
#include "printing/nt_printing_migrate_internal.h"
|
||||
#include "printing/queue_process.h"
|
||||
#include "printing/pcap.h"
|
||||
#include "printing/load.h"
|
||||
#include "printing/spoolssd.h"
|
||||
#include "ntdomain.h"
|
||||
#include "librpc/gen_ndr/ndr_winreg_scompat.h"
|
||||
#include "librpc/gen_ndr/ndr_spoolss_scompat.h"
|
||||
#include "rpc_server/rpc_server.h"
|
||||
#include "rpc_server/rpc_service_setup.h"
|
||||
#include "rpc_server/rpc_ep_register.h"
|
||||
#include "rpc_server/rpc_config.h"
|
||||
#include "rpc_server/spoolss/srv_spoolss_nt.h"
|
||||
#include "librpc/rpc/dcerpc_ep.h"
|
||||
#include "librpc/rpc/dcesrv_core.h"
|
||||
#include "lib/server_prefork.h"
|
||||
#include "lib/server_prefork_util.h"
|
||||
#include "lib/global_contexts.h"
|
||||
|
||||
#undef DBGC_CLASS
|
||||
#define DBGC_CLASS DBGC_RPC_SRV
|
||||
|
||||
#define DAEMON_NAME "spoolssd"
|
||||
|
||||
static struct server_id parent_id;
|
||||
static struct prefork_pool *spoolss_pool = NULL;
|
||||
static int spoolss_child_id = 0;
|
||||
|
||||
static const struct pf_daemon_config default_pf_spoolss_cfg = {
|
||||
.prefork_status = PFH_INIT,
|
||||
.min_children = 5,
|
||||
.max_children = 25,
|
||||
.spawn_rate = 5,
|
||||
.max_allowed_clients = 100,
|
||||
.child_min_life = 60 /* 1 minute minimum life time */
|
||||
};
|
||||
static struct pf_daemon_config pf_spoolss_cfg = { 0 };
|
||||
|
||||
static void spoolss_reopen_logs(int child_id)
|
||||
{
|
||||
const struct loadparm_substitution *lp_sub =
|
||||
loadparm_s3_global_substitution();
|
||||
char *lfile = lp_logfile(talloc_tos(), lp_sub);
|
||||
char *ext;
|
||||
int rc;
|
||||
|
||||
if (child_id) {
|
||||
rc = asprintf(&ext, "%s.%d", DAEMON_NAME, child_id);
|
||||
} else {
|
||||
rc = asprintf(&ext, "%s", DAEMON_NAME);
|
||||
}
|
||||
|
||||
if (rc == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
if (lfile == NULL || lfile[0] == '\0') {
|
||||
rc = asprintf(&lfile, "%s/log.%s",
|
||||
get_dyn_LOGFILEBASE(), ext);
|
||||
} else {
|
||||
if (strstr(lfile, ext) == NULL) {
|
||||
if (child_id) {
|
||||
rc = asprintf(&lfile, "%s.%d",
|
||||
lp_logfile(talloc_tos(), lp_sub),
|
||||
child_id);
|
||||
} else {
|
||||
rc = asprintf(&lfile, "%s.%s",
|
||||
lp_logfile(talloc_tos(), lp_sub),
|
||||
ext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rc > 0) {
|
||||
lp_set_logfile(lfile);
|
||||
SAFE_FREE(lfile);
|
||||
}
|
||||
|
||||
SAFE_FREE(ext);
|
||||
|
||||
reopen_logs();
|
||||
}
|
||||
|
||||
static void update_conf(struct tevent_context *ev,
|
||||
struct messaging_context *msg)
|
||||
{
|
||||
change_to_root_user();
|
||||
lp_load_global(get_dyn_CONFIGFILE());
|
||||
load_printers();
|
||||
|
||||
spoolss_reopen_logs(spoolss_child_id);
|
||||
if (spoolss_child_id == 0) {
|
||||
pfh_daemon_config(DAEMON_NAME,
|
||||
&pf_spoolss_cfg,
|
||||
&default_pf_spoolss_cfg);
|
||||
pfh_manage_pool(ev, msg, &pf_spoolss_cfg, spoolss_pool);
|
||||
}
|
||||
}
|
||||
|
||||
static void smb_conf_updated(struct messaging_context *msg,
|
||||
void *private_data,
|
||||
uint32_t msg_type,
|
||||
struct server_id server_id,
|
||||
DATA_BLOB *data)
|
||||
{
|
||||
struct tevent_context *ev_ctx = talloc_get_type_abort(private_data,
|
||||
struct tevent_context);
|
||||
|
||||
DEBUG(10, ("Got message saying smb.conf was updated. Reloading.\n"));
|
||||
update_conf(ev_ctx, msg);
|
||||
}
|
||||
|
||||
static void spoolss_sig_term_handler(struct tevent_context *ev,
|
||||
struct tevent_signal *se,
|
||||
int signum,
|
||||
int count,
|
||||
void *siginfo,
|
||||
void *private_data)
|
||||
{
|
||||
exit_server_cleanly("termination signal");
|
||||
}
|
||||
|
||||
static void spoolss_setup_sig_term_handler(struct tevent_context *ev_ctx)
|
||||
{
|
||||
struct tevent_signal *se;
|
||||
|
||||
se = tevent_add_signal(ev_ctx,
|
||||
ev_ctx,
|
||||
SIGTERM, 0,
|
||||
spoolss_sig_term_handler,
|
||||
NULL);
|
||||
if (!se) {
|
||||
exit_server("failed to setup SIGTERM handler");
|
||||
}
|
||||
}
|
||||
|
||||
static void spoolss_sig_hup_handler(struct tevent_context *ev,
|
||||
struct tevent_signal *se,
|
||||
int signum,
|
||||
int count,
|
||||
void *siginfo,
|
||||
void *pvt)
|
||||
{
|
||||
struct messaging_context *msg_ctx;
|
||||
|
||||
msg_ctx = talloc_get_type_abort(pvt, struct messaging_context);
|
||||
|
||||
DEBUG(1,("Reloading printers after SIGHUP\n"));
|
||||
update_conf(ev, msg_ctx);
|
||||
|
||||
/* relay to all children */
|
||||
if (spoolss_pool) {
|
||||
prefork_send_signal_to_all(spoolss_pool, SIGHUP);
|
||||
}
|
||||
}
|
||||
|
||||
static void spoolss_setup_sig_hup_handler(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
struct tevent_signal *se;
|
||||
|
||||
se = tevent_add_signal(ev_ctx,
|
||||
ev_ctx,
|
||||
SIGHUP, 0,
|
||||
spoolss_sig_hup_handler,
|
||||
msg_ctx);
|
||||
if (!se) {
|
||||
exit_server("failed to setup SIGHUP handler");
|
||||
}
|
||||
}
|
||||
|
||||
/* Children */
|
||||
|
||||
static void spoolss_chld_sig_hup_handler(struct tevent_context *ev,
|
||||
struct tevent_signal *se,
|
||||
int signum,
|
||||
int count,
|
||||
void *siginfo,
|
||||
void *pvt)
|
||||
{
|
||||
change_to_root_user();
|
||||
DEBUG(1,("Reloading printers after SIGHUP\n"));
|
||||
load_printers();
|
||||
spoolss_reopen_logs(spoolss_child_id);
|
||||
}
|
||||
|
||||
static bool spoolss_setup_chld_hup_handler(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct pf_worker_data *pf)
|
||||
{
|
||||
struct tevent_signal *se;
|
||||
|
||||
se = tevent_add_signal(ev_ctx,
|
||||
ev_ctx,
|
||||
SIGHUP, 0,
|
||||
spoolss_chld_sig_hup_handler,
|
||||
msg_ctx);
|
||||
if (!se) {
|
||||
DEBUG(1, ("failed to setup SIGHUP handler"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void parent_ping(struct messaging_context *msg_ctx,
|
||||
void *private_data,
|
||||
uint32_t msg_type,
|
||||
struct server_id server_id,
|
||||
DATA_BLOB *data)
|
||||
{
|
||||
|
||||
/* The fact we received this message is enough to let make the event
|
||||
* loop if it was idle. spoolss_children_main will cycle through
|
||||
* spoolss_next_client at least once. That function will take whatever
|
||||
* action is necessary */
|
||||
|
||||
DEBUG(10, ("Got message that the parent changed status.\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
static bool spoolss_child_init(struct tevent_context *ev_ctx,
|
||||
int child_id, struct pf_worker_data *pf)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct messaging_context *msg_ctx = global_messaging_context();
|
||||
bool ok;
|
||||
|
||||
status = reinit_after_fork(msg_ctx, ev_ctx, true, "spoolssd-child");
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("reinit_after_fork() failed\n"));
|
||||
smb_panic("reinit_after_fork() failed");
|
||||
}
|
||||
|
||||
spoolss_child_id = child_id;
|
||||
spoolss_reopen_logs(child_id);
|
||||
|
||||
ok = spoolss_setup_chld_hup_handler(ev_ctx, msg_ctx, pf);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!locking_init()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
messaging_register(msg_ctx, ev_ctx,
|
||||
MSG_SMB_CONF_UPDATED, smb_conf_updated);
|
||||
messaging_register(msg_ctx, ev_ctx,
|
||||
MSG_PREFORK_PARENT_EVENT, parent_ping);
|
||||
|
||||
/* As soon as messaging is up check if pcap has been loaded already.
|
||||
* If so then we probably missed a message and should load_printers()
|
||||
* ourselves. If pcap has not been loaded yet, then ignore, we will get
|
||||
* a message as soon as the bq process completes the reload. */
|
||||
load_printers();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct spoolss_children_data {
|
||||
struct tevent_context *ev_ctx;
|
||||
struct messaging_context *msg_ctx;
|
||||
struct dcesrv_context *dce_ctx;
|
||||
struct pf_worker_data *pf;
|
||||
int listen_fd_size;
|
||||
struct pf_listen_fd *listen_fds;
|
||||
};
|
||||
|
||||
static void spoolss_next_client(void *pvt);
|
||||
|
||||
static int spoolss_children_main(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct pf_worker_data *pf,
|
||||
int child_id,
|
||||
int listen_fd_size,
|
||||
struct pf_listen_fd *listen_fds,
|
||||
void *private_data)
|
||||
{
|
||||
struct spoolss_children_data *data;
|
||||
bool ok;
|
||||
int ret = 0;
|
||||
struct dcesrv_context *dce_ctx = NULL;
|
||||
|
||||
dce_ctx = talloc_get_type_abort(private_data, struct dcesrv_context);
|
||||
|
||||
ok = spoolss_child_init(ev_ctx, child_id, pf);
|
||||
if (!ok) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
data = talloc(ev_ctx, struct spoolss_children_data);
|
||||
if (!data) {
|
||||
return 1;
|
||||
}
|
||||
data->pf = pf;
|
||||
data->ev_ctx = ev_ctx;
|
||||
data->msg_ctx = msg_ctx;
|
||||
data->dce_ctx = dce_ctx;
|
||||
data->listen_fd_size = listen_fd_size;
|
||||
data->listen_fds = listen_fds;
|
||||
|
||||
/* loop until it is time to exit */
|
||||
while (pf->status != PF_WORKER_EXITING) {
|
||||
/* try to see if it is time to schedule the next client */
|
||||
spoolss_next_client(data);
|
||||
|
||||
ret = tevent_loop_once(ev_ctx);
|
||||
if (ret != 0) {
|
||||
DEBUG(0, ("tevent_loop_once() exited with %d: %s\n",
|
||||
ret, strerror(errno)));
|
||||
pf->status = PF_WORKER_EXITING;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void spoolss_client_terminated(struct dcesrv_connection *conn,
|
||||
void *pvt)
|
||||
{
|
||||
struct spoolss_children_data *data;
|
||||
|
||||
data = talloc_get_type_abort(pvt, struct spoolss_children_data);
|
||||
|
||||
pfh_client_terminated(data->pf);
|
||||
|
||||
spoolss_next_client(pvt);
|
||||
}
|
||||
|
||||
struct spoolss_new_client {
|
||||
struct spoolss_children_data *data;
|
||||
};
|
||||
|
||||
static void spoolss_handle_client(struct tevent_req *req);
|
||||
|
||||
static void spoolss_next_client(void *pvt)
|
||||
{
|
||||
struct tevent_req *req;
|
||||
struct spoolss_children_data *data;
|
||||
struct spoolss_new_client *next;
|
||||
|
||||
data = talloc_get_type_abort(pvt, struct spoolss_children_data);
|
||||
|
||||
if (!pfh_child_allowed_to_accept(data->pf)) {
|
||||
/* nothing to do for now we are already listening
|
||||
* or we are not allowed to listen further */
|
||||
return;
|
||||
}
|
||||
|
||||
next = talloc_zero(data, struct spoolss_new_client);
|
||||
if (!next) {
|
||||
DEBUG(1, ("Out of memory!?\n"));
|
||||
return;
|
||||
}
|
||||
next->data = data;
|
||||
|
||||
req = prefork_listen_send(next, data->ev_ctx, data->pf,
|
||||
data->listen_fd_size,
|
||||
data->listen_fds);
|
||||
if (!req) {
|
||||
DEBUG(1, ("Failed to make listening request!?\n"));
|
||||
talloc_free(next);
|
||||
return;
|
||||
}
|
||||
tevent_req_set_callback(req, spoolss_handle_client, next);
|
||||
}
|
||||
|
||||
static void spoolss_handle_client(struct tevent_req *req)
|
||||
{
|
||||
struct spoolss_children_data *data;
|
||||
struct spoolss_new_client *client;
|
||||
const DATA_BLOB ping = data_blob_null;
|
||||
int ret;
|
||||
int sd;
|
||||
struct tsocket_address *srv_addr = NULL;
|
||||
struct tsocket_address *cli_addr = NULL;
|
||||
void *listen_fd_data = NULL;
|
||||
struct dcesrv_endpoint *ep = NULL;
|
||||
|
||||
client = tevent_req_callback_data(req, struct spoolss_new_client);
|
||||
data = client->data;
|
||||
|
||||
ret = prefork_listen_recv(req, data, &sd, &listen_fd_data,
|
||||
&srv_addr, &cli_addr);
|
||||
|
||||
/* this will free the request too */
|
||||
talloc_free(client);
|
||||
|
||||
if (ret != 0) {
|
||||
DEBUG(6, ("No client connection was available after all!\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
ep = talloc_get_type_abort(listen_fd_data, struct dcesrv_endpoint);
|
||||
|
||||
/* Warn parent that our status changed */
|
||||
messaging_send(data->msg_ctx, parent_id,
|
||||
MSG_PREFORK_CHILD_EVENT, &ping);
|
||||
|
||||
DEBUG(2, ("Spoolss preforked child %d got client connection!\n",
|
||||
(int)(data->pf->pid)));
|
||||
|
||||
dcerpc_ncacn_accept(data->ev_ctx,
|
||||
data->msg_ctx,
|
||||
data->dce_ctx,
|
||||
ep,
|
||||
&cli_addr,
|
||||
&srv_addr,
|
||||
sd,
|
||||
spoolss_client_terminated,
|
||||
data);
|
||||
}
|
||||
|
||||
/* ==== Main Process Functions ==== */
|
||||
|
||||
extern pid_t background_lpq_updater_pid;
|
||||
static char *bq_logfile;
|
||||
|
||||
static void check_updater_child(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
int status;
|
||||
pid_t pid;
|
||||
|
||||
if (background_lpq_updater_pid == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
pid = waitpid(background_lpq_updater_pid, &status, WNOHANG);
|
||||
if (pid > 0) {
|
||||
DEBUG(2, ("The background queue child died... Restarting!\n"));
|
||||
pid = start_background_queue(ev_ctx, msg_ctx, bq_logfile);
|
||||
background_lpq_updater_pid = pid;
|
||||
}
|
||||
}
|
||||
|
||||
static void child_ping(struct messaging_context *msg_ctx,
|
||||
void *private_data,
|
||||
uint32_t msg_type,
|
||||
struct server_id server_id,
|
||||
DATA_BLOB *data)
|
||||
{
|
||||
struct tevent_context *ev_ctx;
|
||||
|
||||
ev_ctx = talloc_get_type_abort(private_data, struct tevent_context);
|
||||
|
||||
DEBUG(10, ("Got message that a child changed status.\n"));
|
||||
pfh_manage_pool(ev_ctx, msg_ctx, &pf_spoolss_cfg, spoolss_pool);
|
||||
}
|
||||
|
||||
static bool spoolssd_schedule_check(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct timeval current_time);
|
||||
static void spoolssd_check_children(struct tevent_context *ev_ctx,
|
||||
struct tevent_timer *te,
|
||||
struct timeval current_time,
|
||||
void *pvt);
|
||||
|
||||
static void spoolssd_sigchld_handler(struct tevent_context *ev_ctx,
|
||||
struct prefork_pool *pfp,
|
||||
void *pvt)
|
||||
{
|
||||
struct messaging_context *msg_ctx;
|
||||
|
||||
msg_ctx = talloc_get_type_abort(pvt, struct messaging_context);
|
||||
|
||||
/* run pool management so we can fork/retire or increase
|
||||
* the allowed connections per child based on load */
|
||||
pfh_manage_pool(ev_ctx, msg_ctx, &pf_spoolss_cfg, spoolss_pool);
|
||||
|
||||
/* also check if the updater child is alive and well */
|
||||
check_updater_child(ev_ctx, msg_ctx);
|
||||
}
|
||||
|
||||
static bool spoolssd_setup_children_monitor(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
/* add our oun sigchld callback */
|
||||
prefork_set_sigchld_callback(spoolss_pool,
|
||||
spoolssd_sigchld_handler, msg_ctx);
|
||||
|
||||
ok = spoolssd_schedule_check(ev_ctx, msg_ctx,
|
||||
tevent_timeval_current());
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool spoolssd_schedule_check(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct timeval current_time)
|
||||
{
|
||||
struct tevent_timer *te;
|
||||
struct timeval next_event;
|
||||
|
||||
/* check situation again in 10 seconds */
|
||||
next_event = tevent_timeval_current_ofs(10, 0);
|
||||
|
||||
/* TODO: check when the socket becomes readable, so that children
|
||||
* are checked only when there is some activity ? */
|
||||
te = tevent_add_timer(ev_ctx, spoolss_pool, next_event,
|
||||
spoolssd_check_children, msg_ctx);
|
||||
if (!te) {
|
||||
DEBUG(2, ("Failed to set up children monitoring!\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void spoolssd_check_children(struct tevent_context *ev_ctx,
|
||||
struct tevent_timer *te,
|
||||
struct timeval current_time,
|
||||
void *pvt)
|
||||
{
|
||||
struct messaging_context *msg_ctx;
|
||||
|
||||
msg_ctx = talloc_get_type_abort(pvt, struct messaging_context);
|
||||
|
||||
pfh_manage_pool(ev_ctx, msg_ctx, &pf_spoolss_cfg, spoolss_pool);
|
||||
|
||||
spoolssd_schedule_check(ev_ctx, msg_ctx, current_time);
|
||||
}
|
||||
|
||||
static void print_queue_forward(struct messaging_context *msg,
|
||||
void *private_data,
|
||||
uint32_t msg_type,
|
||||
struct server_id server_id,
|
||||
DATA_BLOB *data)
|
||||
{
|
||||
send_to_bgqd(msg, msg_type, data->data, data->length);
|
||||
}
|
||||
|
||||
static char *get_bq_logfile(void)
|
||||
{
|
||||
const struct loadparm_substitution *lp_sub =
|
||||
loadparm_s3_global_substitution();
|
||||
char *lfile = lp_logfile(talloc_tos(), lp_sub);
|
||||
int rc;
|
||||
|
||||
if (lfile == NULL || lfile[0] == '\0') {
|
||||
rc = asprintf(&lfile, "%s/log.%s.bq",
|
||||
get_dyn_LOGFILEBASE(), DAEMON_NAME);
|
||||
} else {
|
||||
rc = asprintf(&lfile, "%s.bq", lp_logfile(talloc_tos(), lp_sub));
|
||||
}
|
||||
if (rc == -1) {
|
||||
lfile = NULL;
|
||||
}
|
||||
return lfile;
|
||||
}
|
||||
|
||||
static NTSTATUS spoolssd_create_sockets(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct pf_listen_fd **plisten_fds,
|
||||
size_t *pnum_listen_fds)
|
||||
{
|
||||
NTSTATUS status;
|
||||
int rc;
|
||||
enum rpc_service_mode_e epm_mode = rpc_epmapper_mode();
|
||||
size_t i, num_fds;
|
||||
struct pf_listen_fd *fds = NULL;
|
||||
struct dcesrv_endpoint *e = dce_ctx->endpoint_list;
|
||||
|
||||
DBG_INFO("Initializing DCE/RPC connection endpoints\n");
|
||||
|
||||
status = dcesrv_create_endpoint_list_pf_listen_fds(
|
||||
ev_ctx, msg_ctx, dce_ctx, e, mem_ctx, &num_fds, &fds);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_fds; i++) {
|
||||
rc = listen(fds[i].fd, pf_spoolss_cfg.max_allowed_clients);
|
||||
if (rc == -1) {
|
||||
char *ep_string = NULL;
|
||||
e = fds[i].fd_data;
|
||||
|
||||
ep_string = dcerpc_binding_string(dce_ctx,
|
||||
e->ep_description);
|
||||
DBG_ERR("Failed to listen on endpoint '%s': %s\n",
|
||||
ep_string, strerror(errno));
|
||||
status = map_nt_error_from_unix(errno);
|
||||
TALLOC_FREE(ep_string);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (epm_mode != RPC_SERVICE_MODE_DISABLED &&
|
||||
(lp_parm_bool(-1, "rpc_server", "register_embedded_np", false))) {
|
||||
for (e = dce_ctx->endpoint_list; e; e = e->next) {
|
||||
struct dcesrv_if_list *ifl = NULL;
|
||||
for (ifl = e->interface_list; ifl; ifl = ifl->next) {
|
||||
status = rpc_ep_register(ev_ctx,
|
||||
msg_ctx,
|
||||
dce_ctx,
|
||||
ifl->iface);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register interface"
|
||||
" in endpoint mapper: %s\n",
|
||||
nt_errstr(status));
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*plisten_fds = fds;
|
||||
*pnum_listen_fds = num_fds;
|
||||
|
||||
status = NT_STATUS_OK;
|
||||
done:
|
||||
return status;
|
||||
}
|
||||
|
||||
pid_t start_spoolssd(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx)
|
||||
{
|
||||
pid_t pid;
|
||||
NTSTATUS status;
|
||||
struct pf_listen_fd *listen_fds = NULL;
|
||||
size_t listen_fds_size = 0;
|
||||
int ret;
|
||||
bool ok;
|
||||
const struct dcesrv_endpoint_server *ep_server = NULL;
|
||||
const char *ep_servers[] = { "winreg", "spoolss", NULL };
|
||||
|
||||
DEBUG(1, ("Forking SPOOLSS Daemon\n"));
|
||||
|
||||
/*
|
||||
* Block signals before forking child as it will have to
|
||||
* set its own handlers. Child will re-enable SIGHUP as
|
||||
* soon as the handlers are set up.
|
||||
*/
|
||||
BlockSignals(true, SIGTERM);
|
||||
BlockSignals(true, SIGHUP);
|
||||
|
||||
pid = fork();
|
||||
|
||||
if (pid == -1) {
|
||||
DEBUG(0, ("Failed to fork SPOOLSS [%s]\n",
|
||||
strerror(errno)));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* parent or error */
|
||||
if (pid != 0) {
|
||||
|
||||
/* Re-enable SIGHUP before returnig */
|
||||
BlockSignals(false, SIGTERM);
|
||||
BlockSignals(false, SIGHUP);
|
||||
return pid;
|
||||
}
|
||||
|
||||
status = smbd_reinit_after_fork(msg_ctx, ev_ctx, true,
|
||||
"spoolssd-master");
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("reinit_after_fork() failed\n"));
|
||||
smb_panic("reinit_after_fork() failed");
|
||||
}
|
||||
|
||||
/* save the parent process id so the children can use it later */
|
||||
parent_id = messaging_server_id(msg_ctx);
|
||||
|
||||
spoolss_reopen_logs(0);
|
||||
pfh_daemon_config(DAEMON_NAME,
|
||||
&pf_spoolss_cfg,
|
||||
&default_pf_spoolss_cfg);
|
||||
|
||||
spoolss_setup_sig_term_handler(ev_ctx);
|
||||
spoolss_setup_sig_hup_handler(ev_ctx, msg_ctx);
|
||||
|
||||
BlockSignals(false, SIGTERM);
|
||||
BlockSignals(false, SIGHUP);
|
||||
|
||||
/* always start the backgroundqueue listner in spoolssd */
|
||||
bq_logfile = get_bq_logfile();
|
||||
pid = start_background_queue(ev_ctx, msg_ctx, bq_logfile);
|
||||
if (pid > 0) {
|
||||
background_lpq_updater_pid = pid;
|
||||
}
|
||||
|
||||
DBG_INFO("Registering DCE/RPC endpoint servers\n");
|
||||
|
||||
ep_server = winreg_get_ep_server();
|
||||
if (ep_server == NULL) {
|
||||
DBG_ERR("Failed to get 'winreg' endpoint server\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
status = dcerpc_register_ep_server(ep_server);
|
||||
if (!NT_STATUS_IS_OK(status) &&
|
||||
!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
|
||||
DBG_ERR("Failed to register 'winreg' endpoint server: %s\n",
|
||||
nt_errstr(status));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ep_server = spoolss_get_ep_server();
|
||||
if (ep_server == NULL) {
|
||||
DBG_ERR("Failed to get 'spoolss' endpoint server\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
status = dcerpc_register_ep_server(ep_server);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register 'spoolss' endpoint server: %s\n",
|
||||
nt_errstr(status));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DBG_INFO("Reinitializing DCE/RPC server context\n");
|
||||
|
||||
status = dcesrv_reinit_context(dce_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to reinit DCE/RPC context: %s\n",
|
||||
nt_errstr(status));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DBG_INFO("Initializing DCE/RPC registered endpoint servers\n");
|
||||
|
||||
/* Init ep servers */
|
||||
status = dcesrv_init_ep_servers(dce_ctx, ep_servers);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to init DCE/RPC endpoint server: %s\n",
|
||||
nt_errstr(status));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* the listening fd must be created before the children are actually
|
||||
* forked out. */
|
||||
status = spoolssd_create_sockets(ev_ctx,
|
||||
msg_ctx,
|
||||
dce_ctx,
|
||||
dce_ctx,
|
||||
&listen_fds,
|
||||
&listen_fds_size);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to create sockets: %s\n",
|
||||
nt_errstr(status));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* start children before any more initialization is done */
|
||||
ok = prefork_create_pool(ev_ctx, /* mem_ctx */
|
||||
ev_ctx, msg_ctx,
|
||||
listen_fds_size, listen_fds,
|
||||
pf_spoolss_cfg.min_children,
|
||||
pf_spoolss_cfg.max_children,
|
||||
&spoolss_children_main, dce_ctx,
|
||||
&spoolss_pool);
|
||||
TALLOC_FREE(listen_fds);
|
||||
if (!ok) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!locking_init()) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
messaging_register(msg_ctx, ev_ctx,
|
||||
MSG_SMB_CONF_UPDATED, smb_conf_updated);
|
||||
messaging_register(msg_ctx, NULL, MSG_PRINTER_UPDATE,
|
||||
print_queue_forward);
|
||||
messaging_register(msg_ctx, ev_ctx,
|
||||
MSG_PREFORK_CHILD_EVENT, child_ping);
|
||||
|
||||
/*
|
||||
* As soon as messaging is up check if pcap has been loaded already.
|
||||
* If pcap has not been loaded yet, then ignore, as we will reload on
|
||||
* client enumeration anyway.
|
||||
*/
|
||||
load_printers();
|
||||
|
||||
ok = spoolssd_setup_children_monitor(ev_ctx, msg_ctx);
|
||||
if (!ok) {
|
||||
DEBUG(0, ("Failed to setup children monitoring!\n"));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DEBUG(1, ("SPOOLSS Daemon Started (%u)\n", (unsigned int)getpid()));
|
||||
|
||||
pfh_manage_pool(ev_ctx, msg_ctx, &pf_spoolss_cfg, spoolss_pool);
|
||||
|
||||
/* loop forever */
|
||||
ret = tevent_loop_wait(ev_ctx);
|
||||
|
||||
/* should not be reached */
|
||||
DEBUG(0,("spoolssd tevent_loop_wait() exited with %d - %s\n",
|
||||
ret, (ret == 0) ? "out of events" : strerror(errno)));
|
||||
exit(1);
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
/*
|
||||
Unix SMB/Netbios implementation.
|
||||
SPOOLSS Daemon
|
||||
Copyright (C) Simo Sorce <idra@samba.org> 2010-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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _SOURCE3_PRINTING_SPOOLSSD_H_
|
||||
#define _SOURCE3_PRINTING_SPOOLSSD_H_
|
||||
|
||||
#include "replace.h"
|
||||
#include "messages.h"
|
||||
|
||||
struct dcesrv_context;
|
||||
|
||||
pid_t start_spoolssd(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx);
|
||||
|
||||
#endif /* _SOURCE3_PRINTING_SPOOLSSD_H_ */
|
@ -72,13 +72,6 @@ struct dcesrv_epm_endpoint {
|
||||
struct dcesrv_iface_list *iface_list;
|
||||
};
|
||||
|
||||
struct dcesrv_ep_entry_list {
|
||||
struct dcesrv_ep_entry_list *next, *prev;
|
||||
|
||||
uint32_t num_ents;
|
||||
struct epm_entry_t *entries;
|
||||
};
|
||||
|
||||
struct rpc_eps {
|
||||
struct dcesrv_ep_iface *e;
|
||||
uint32_t count;
|
||||
|
@ -1,246 +0,0 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
*
|
||||
* SMBD RPC service callbacks
|
||||
*
|
||||
* Copyright (c) 2011 Andreas Schneider <asn@samba.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#include "ntdomain.h"
|
||||
#include "messages.h"
|
||||
|
||||
#include "librpc/rpc/dcerpc_ep.h"
|
||||
|
||||
#include "librpc/rpc/dcesrv_core.h"
|
||||
#include "librpc/gen_ndr/ndr_epmapper_scompat.h"
|
||||
|
||||
#include "rpc_server/rpc_server.h"
|
||||
#include "rpc_server/rpc_service_setup.h"
|
||||
#include "rpc_server/rpc_sock_helper.h"
|
||||
#include "rpc_server/epmd.h"
|
||||
|
||||
#undef DBGC_CLASS
|
||||
#define DBGC_CLASS DBGC_RPC_SRV
|
||||
|
||||
#define DAEMON_NAME "epmd"
|
||||
|
||||
static void epmd_reopen_logs(void)
|
||||
{
|
||||
const struct loadparm_substitution *lp_sub =
|
||||
loadparm_s3_global_substitution();
|
||||
char *lfile = lp_logfile(talloc_tos(), lp_sub);
|
||||
int rc;
|
||||
|
||||
if (lfile == NULL || lfile[0] == '\0') {
|
||||
rc = asprintf(&lfile, "%s/log.%s", get_dyn_LOGFILEBASE(), DAEMON_NAME);
|
||||
if (rc > 0) {
|
||||
lp_set_logfile(lfile);
|
||||
SAFE_FREE(lfile);
|
||||
}
|
||||
} else {
|
||||
if (strstr(lfile, DAEMON_NAME) == NULL) {
|
||||
rc = asprintf(&lfile, "%s.%s",
|
||||
lp_logfile(talloc_tos(), lp_sub), DAEMON_NAME);
|
||||
if (rc > 0) {
|
||||
lp_set_logfile(lfile);
|
||||
SAFE_FREE(lfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reopen_logs();
|
||||
}
|
||||
|
||||
static void epmd_smb_conf_updated(struct messaging_context *msg,
|
||||
void *private_data,
|
||||
uint32_t msg_type,
|
||||
struct server_id server_id,
|
||||
DATA_BLOB *data)
|
||||
{
|
||||
DEBUG(10, ("Got message saying smb.conf was updated. Reloading.\n"));
|
||||
change_to_root_user();
|
||||
epmd_reopen_logs();
|
||||
}
|
||||
|
||||
static void epmd_sig_term_handler(struct tevent_context *ev,
|
||||
struct tevent_signal *se,
|
||||
int signum,
|
||||
int count,
|
||||
void *siginfo,
|
||||
void *private_data)
|
||||
{
|
||||
exit_server_cleanly("termination signal");
|
||||
}
|
||||
|
||||
static void epmd_setup_sig_term_handler(struct tevent_context *ev_ctx)
|
||||
{
|
||||
struct tevent_signal *se;
|
||||
|
||||
se = tevent_add_signal(ev_ctx,
|
||||
ev_ctx,
|
||||
SIGTERM, 0,
|
||||
epmd_sig_term_handler,
|
||||
NULL);
|
||||
if (se == NULL) {
|
||||
exit_server("failed to setup SIGTERM handler");
|
||||
}
|
||||
}
|
||||
|
||||
static void epmd_sig_hup_handler(struct tevent_context *ev,
|
||||
struct tevent_signal *se,
|
||||
int signum,
|
||||
int count,
|
||||
void *siginfo,
|
||||
void *private_data)
|
||||
{
|
||||
change_to_root_user();
|
||||
|
||||
DEBUG(1,("Reloading printers after SIGHUP\n"));
|
||||
epmd_reopen_logs();
|
||||
}
|
||||
|
||||
static void epmd_setup_sig_hup_handler(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
struct tevent_signal *se;
|
||||
|
||||
se = tevent_add_signal(ev_ctx,
|
||||
ev_ctx,
|
||||
SIGHUP, 0,
|
||||
epmd_sig_hup_handler,
|
||||
msg_ctx);
|
||||
if (se == NULL) {
|
||||
exit_server("failed to setup SIGHUP handler");
|
||||
}
|
||||
}
|
||||
|
||||
void start_epmd(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx)
|
||||
{
|
||||
NTSTATUS status;
|
||||
pid_t pid;
|
||||
int rc;
|
||||
const struct dcesrv_endpoint_server *ep_server = NULL;
|
||||
struct dcesrv_endpoint *e = NULL;
|
||||
|
||||
DEBUG(1, ("Forking Endpoint Mapper Daemon\n"));
|
||||
|
||||
pid = fork();
|
||||
|
||||
if (pid == -1) {
|
||||
DEBUG(0, ("Failed to fork Endpoint Mapper [%s], aborting ...\n",
|
||||
strerror(errno)));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (pid) {
|
||||
/* parent */
|
||||
return;
|
||||
}
|
||||
|
||||
status = smbd_reinit_after_fork(msg_ctx, ev_ctx, true, "epmd");
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("reinit_after_fork() failed\n"));
|
||||
smb_panic("reinit_after_fork() failed");
|
||||
}
|
||||
|
||||
epmd_reopen_logs();
|
||||
|
||||
epmd_setup_sig_term_handler(ev_ctx);
|
||||
epmd_setup_sig_hup_handler(ev_ctx, msg_ctx);
|
||||
|
||||
messaging_register(msg_ctx,
|
||||
ev_ctx,
|
||||
MSG_SMB_CONF_UPDATED,
|
||||
epmd_smb_conf_updated);
|
||||
|
||||
DBG_INFO("Registering DCE/RPC endpoint servers\n");
|
||||
|
||||
/* Register the endpoint server in DCERPC core */
|
||||
ep_server = epmapper_get_ep_server();
|
||||
if (ep_server == NULL) {
|
||||
DBG_ERR("Failed to get 'epmapper' endpoint server\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
status = dcerpc_register_ep_server(ep_server);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register 'epmapper' endpoint server: %s\n",
|
||||
nt_errstr(status));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DBG_INFO("Reinitializing DCE/RPC server context\n");
|
||||
|
||||
status = dcesrv_reinit_context(dce_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to reinit DCE/RPC context: %s\n",
|
||||
nt_errstr(status));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DBG_INFO("Initializing DCE/RPC registered endpoint servers\n");
|
||||
|
||||
status = dcesrv_init_ep_server(dce_ctx, "epmapper");
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to init DCE/RPC endpoint server: %s\n",
|
||||
nt_errstr(status));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DBG_INFO("Initializing DCE/RPC connection endpoints\n");
|
||||
|
||||
for (e = dce_ctx->endpoint_list; e; e = e->next) {
|
||||
enum dcerpc_transport_t transport =
|
||||
dcerpc_binding_get_transport(e->ep_description);
|
||||
dcerpc_ncacn_termination_fn term_fn = NULL;
|
||||
|
||||
if (transport == NCACN_HTTP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
status = dcesrv_setup_endpoint_sockets(ev_ctx,
|
||||
msg_ctx,
|
||||
dce_ctx,
|
||||
e,
|
||||
term_fn,
|
||||
NULL); /* termination_data */
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
char *ep_string = dcerpc_binding_string(
|
||||
dce_ctx, e->ep_description);
|
||||
DBG_ERR("Failed to setup endpoint '%s': %s\n",
|
||||
ep_string, nt_errstr(status));
|
||||
TALLOC_FREE(ep_string);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG(1, ("Endpoint Mapper Daemon Started (%u)\n", (unsigned int)getpid()));
|
||||
|
||||
/* loop forever */
|
||||
rc = tevent_loop_wait(ev_ctx);
|
||||
|
||||
/* should not be reached */
|
||||
DEBUG(0,("background_queue: tevent_loop_wait() exited with %d - %s\n",
|
||||
rc, (rc == 0) ? "out of events" : strerror(errno)));
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* vim: set ts=8 sw=8 noet cindent ft=c.doxygen: */
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
*
|
||||
* EPMD header file
|
||||
*
|
||||
* Copyright (c) 2018 Volker Lendecke <vl@samba.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __RPC_SERVER_EPMD_H__
|
||||
#define __RPC_SERVER_EPMD_H__
|
||||
|
||||
#include "replace.h"
|
||||
#include "messages.h"
|
||||
|
||||
struct dcesrv_context;
|
||||
|
||||
void start_epmd(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx);
|
||||
|
||||
#endif
|
@ -1,238 +0,0 @@
|
||||
/*
|
||||
* File Server Shadow-Copy Daemon
|
||||
*
|
||||
* Copyright (C) David Disseldorp 2012-2015
|
||||
*
|
||||
* Based on epmd.c:
|
||||
* Copyright (c) 2011 Andreas Schneider <asn@samba.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#include "ntdomain.h"
|
||||
#include "messages.h"
|
||||
|
||||
#include "librpc/rpc/dcerpc_ep.h"
|
||||
|
||||
#include "librpc/rpc/dcesrv_core.h"
|
||||
#include "librpc/gen_ndr/ndr_fsrvp_scompat.h"
|
||||
|
||||
#include "rpc_server/rpc_server.h"
|
||||
#include "rpc_server/rpc_service_setup.h"
|
||||
#include "rpc_server/rpc_sock_helper.h"
|
||||
#include "rpc_server/fssd.h"
|
||||
|
||||
#undef DBGC_CLASS
|
||||
#define DBGC_CLASS DBGC_RPC_SRV
|
||||
|
||||
#define DAEMON_NAME "fssd"
|
||||
|
||||
static void fssd_reopen_logs(void)
|
||||
{
|
||||
const struct loadparm_substitution *lp_sub =
|
||||
loadparm_s3_global_substitution();
|
||||
char *lfile = lp_logfile(NULL, lp_sub);
|
||||
int rc;
|
||||
|
||||
if (lfile == NULL || lfile[0] == '\0') {
|
||||
rc = asprintf(&lfile, "%s/log.%s", get_dyn_LOGFILEBASE(), DAEMON_NAME);
|
||||
if (rc > 0) {
|
||||
lp_set_logfile(lfile);
|
||||
SAFE_FREE(lfile);
|
||||
}
|
||||
} else {
|
||||
if (strstr(lfile, DAEMON_NAME) == NULL) {
|
||||
rc = asprintf(&lfile, "%s.%s", lp_logfile(NULL, lp_sub), DAEMON_NAME);
|
||||
if (rc > 0) {
|
||||
lp_set_logfile(lfile);
|
||||
SAFE_FREE(lfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reopen_logs();
|
||||
}
|
||||
|
||||
static void fssd_smb_conf_updated(struct messaging_context *msg,
|
||||
void *private_data,
|
||||
uint32_t msg_type,
|
||||
struct server_id server_id,
|
||||
DATA_BLOB *data)
|
||||
{
|
||||
DEBUG(10, ("Got message saying smb.conf was updated. Reloading.\n"));
|
||||
change_to_root_user();
|
||||
fssd_reopen_logs();
|
||||
}
|
||||
|
||||
static void fssd_sig_term_handler(struct tevent_context *ev,
|
||||
struct tevent_signal *se,
|
||||
int signum,
|
||||
int count,
|
||||
void *siginfo,
|
||||
void *private_data)
|
||||
{
|
||||
exit_server_cleanly("termination signal");
|
||||
}
|
||||
|
||||
static void fssd_setup_sig_term_handler(struct tevent_context *ev_ctx)
|
||||
{
|
||||
struct tevent_signal *se;
|
||||
|
||||
se = tevent_add_signal(ev_ctx,
|
||||
ev_ctx,
|
||||
SIGTERM, 0,
|
||||
fssd_sig_term_handler,
|
||||
NULL);
|
||||
if (se == NULL) {
|
||||
exit_server("failed to setup SIGTERM handler");
|
||||
}
|
||||
}
|
||||
|
||||
static void fssd_sig_hup_handler(struct tevent_context *ev,
|
||||
struct tevent_signal *se,
|
||||
int signum,
|
||||
int count,
|
||||
void *siginfo,
|
||||
void *private_data)
|
||||
{
|
||||
change_to_root_user();
|
||||
|
||||
DEBUG(1,("reopening logs after SIGHUP\n"));
|
||||
fssd_reopen_logs();
|
||||
}
|
||||
|
||||
static void fssd_setup_sig_hup_handler(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
struct tevent_signal *se;
|
||||
|
||||
se = tevent_add_signal(ev_ctx,
|
||||
ev_ctx,
|
||||
SIGHUP, 0,
|
||||
fssd_sig_hup_handler,
|
||||
msg_ctx);
|
||||
if (se == NULL) {
|
||||
exit_server("failed to setup SIGHUP handler");
|
||||
}
|
||||
}
|
||||
|
||||
void start_fssd(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx)
|
||||
{
|
||||
NTSTATUS status;
|
||||
pid_t pid;
|
||||
int rc;
|
||||
const struct dcesrv_endpoint_server *ep_server = NULL;
|
||||
struct dcesrv_endpoint *e = NULL;
|
||||
|
||||
DEBUG(1, ("Forking File Server Shadow-copy Daemon\n"));
|
||||
|
||||
pid = fork();
|
||||
|
||||
if (pid == -1) {
|
||||
DEBUG(0, ("failed to fork file server shadow-copy daemon [%s], "
|
||||
"aborting ...\n", strerror(errno)));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (pid) {
|
||||
/* parent */
|
||||
return;
|
||||
}
|
||||
|
||||
/* child */
|
||||
status = smbd_reinit_after_fork(msg_ctx, ev_ctx, true, NULL);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("reinit_after_fork() failed\n"));
|
||||
smb_panic("reinit_after_fork() failed");
|
||||
}
|
||||
|
||||
fssd_reopen_logs();
|
||||
|
||||
fssd_setup_sig_term_handler(ev_ctx);
|
||||
fssd_setup_sig_hup_handler(ev_ctx, msg_ctx);
|
||||
|
||||
messaging_register(msg_ctx,
|
||||
ev_ctx,
|
||||
MSG_SMB_CONF_UPDATED,
|
||||
fssd_smb_conf_updated);
|
||||
|
||||
DBG_INFO("Registering DCE/RPC endpoint servers\n");
|
||||
|
||||
ep_server = FileServerVssAgent_get_ep_server();
|
||||
if (ep_server == NULL) {
|
||||
DBG_ERR("Failed to get 'FileServerVssAgent' endpoint "
|
||||
"server\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
status = dcerpc_register_ep_server(ep_server);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register 'FileServerVssAgent' endpoint "
|
||||
"server: %s\n", nt_errstr(status));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DBG_INFO("Reinitializing DCE/RPC server context\n");
|
||||
|
||||
status = dcesrv_reinit_context(dce_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to reinit DCE/RPC context: %s\n",
|
||||
nt_errstr(status));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DBG_INFO("Initializing DCE/RPC registered endpoint servers\n");
|
||||
|
||||
status = dcesrv_init_ep_server(dce_ctx, "FileServerVssAgent");
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to init DCE/RPC endpoint server: %s\n",
|
||||
nt_errstr(status));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DBG_INFO("Initializing DCE/RPC connection endpoints\n");
|
||||
|
||||
for (e = dce_ctx->endpoint_list; e; e = e->next) {
|
||||
status = dcesrv_setup_endpoint_sockets(ev_ctx,
|
||||
msg_ctx,
|
||||
dce_ctx,
|
||||
e,
|
||||
NULL, /* termination function */
|
||||
NULL); /* termination data */
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
char *ep_string = dcerpc_binding_string(
|
||||
dce_ctx, e->ep_description);
|
||||
DBG_ERR("Failed to setup endpoint '%s': %s\n",
|
||||
ep_string, nt_errstr(status));
|
||||
TALLOC_FREE(ep_string);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG(1, ("File Server Shadow-copy Daemon Started (%d)\n",
|
||||
(int)getpid()));
|
||||
|
||||
/* loop forever */
|
||||
rc = tevent_loop_wait(ev_ctx);
|
||||
|
||||
/* should not be reached */
|
||||
DEBUG(0,("tevent_loop_wait() exited with %d - %s\n",
|
||||
rc, (rc == 0) ? "out of events" : strerror(errno)));
|
||||
|
||||
exit(1);
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
*
|
||||
* FSSD header file
|
||||
*
|
||||
* Copyright (c) 2018 Volker Lendecke <vl@samba.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __RPC_SERVER_FSSD_H__
|
||||
#define __RPC_SERVER_FSSD_H__
|
||||
|
||||
#include "replace.h"
|
||||
#include "messages.h"
|
||||
|
||||
struct dcesrv_context;
|
||||
|
||||
void start_fssd(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx);
|
||||
|
||||
#endif
|
@ -1,775 +0,0 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
*
|
||||
* LSA service daemon
|
||||
*
|
||||
* Copyright (c) 2011 Andreas Schneider <asn@samba.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "messages.h"
|
||||
#include "ntdomain.h"
|
||||
#include "passdb.h"
|
||||
|
||||
#include "lib/id_cache.h"
|
||||
|
||||
#include "../lib/tsocket/tsocket.h"
|
||||
#include "lib/server_prefork.h"
|
||||
#include "lib/server_prefork_util.h"
|
||||
#include "librpc/rpc/dcerpc_ep.h"
|
||||
#include "librpc/rpc/dcesrv_core.h"
|
||||
|
||||
#include "rpc_server/rpc_server.h"
|
||||
#include "rpc_server/rpc_ep_register.h"
|
||||
#include "rpc_server/rpc_sock_helper.h"
|
||||
#include "rpc_server/rpc_service_setup.h"
|
||||
|
||||
#include "rpc_server/lsasd.h"
|
||||
|
||||
#include "librpc/gen_ndr/ndr_lsa_scompat.h"
|
||||
#include "librpc/gen_ndr/ndr_samr_scompat.h"
|
||||
#include "librpc/gen_ndr/ndr_netlogon_scompat.h"
|
||||
#include "lib/global_contexts.h"
|
||||
|
||||
#undef DBGC_CLASS
|
||||
#define DBGC_CLASS DBGC_RPC_SRV
|
||||
|
||||
#define DAEMON_NAME "lsasd"
|
||||
|
||||
static struct server_id parent_id;
|
||||
static struct prefork_pool *lsasd_pool = NULL;
|
||||
static int lsasd_child_id = 0;
|
||||
|
||||
static const struct pf_daemon_config default_pf_lsasd_cfg = {
|
||||
.prefork_status = PFH_INIT,
|
||||
.min_children = 5,
|
||||
.max_children = 25,
|
||||
.spawn_rate = 5,
|
||||
.max_allowed_clients = 100,
|
||||
.child_min_life = 60 /* 1 minute minimum life time */
|
||||
};
|
||||
static struct pf_daemon_config pf_lsasd_cfg = { 0 };
|
||||
|
||||
static void lsasd_reopen_logs(int child_id)
|
||||
{
|
||||
const struct loadparm_substitution *lp_sub =
|
||||
loadparm_s3_global_substitution();
|
||||
char *lfile = lp_logfile(talloc_tos(), lp_sub);
|
||||
char *extension;
|
||||
int rc;
|
||||
|
||||
if (child_id) {
|
||||
rc = asprintf(&extension, "%s.%d", DAEMON_NAME, child_id);
|
||||
} else {
|
||||
rc = asprintf(&extension, "%s", DAEMON_NAME);
|
||||
}
|
||||
if (rc == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
if (lfile == NULL || lfile[0] == '\0') {
|
||||
rc = asprintf(&lfile, "%s/log.%s",
|
||||
get_dyn_LOGFILEBASE(), extension);
|
||||
} else {
|
||||
if (strstr(lfile, extension) == NULL) {
|
||||
if (child_id) {
|
||||
rc = asprintf(&lfile, "%s.%d",
|
||||
lp_logfile(talloc_tos(), lp_sub),
|
||||
child_id);
|
||||
} else {
|
||||
rc = asprintf(&lfile, "%s.%s",
|
||||
lp_logfile(talloc_tos(), lp_sub),
|
||||
extension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rc > 0) {
|
||||
lp_set_logfile(lfile);
|
||||
SAFE_FREE(lfile);
|
||||
}
|
||||
|
||||
SAFE_FREE(extension);
|
||||
|
||||
reopen_logs();
|
||||
}
|
||||
|
||||
static void lsasd_smb_conf_updated(struct messaging_context *msg,
|
||||
void *private_data,
|
||||
uint32_t msg_type,
|
||||
struct server_id server_id,
|
||||
DATA_BLOB *data)
|
||||
{
|
||||
struct tevent_context *ev_ctx;
|
||||
|
||||
DEBUG(10, ("Got message saying smb.conf was updated. Reloading.\n"));
|
||||
ev_ctx = talloc_get_type_abort(private_data, struct tevent_context);
|
||||
|
||||
change_to_root_user();
|
||||
lp_load_global(get_dyn_CONFIGFILE());
|
||||
|
||||
lsasd_reopen_logs(lsasd_child_id);
|
||||
if (lsasd_child_id == 0) {
|
||||
pfh_daemon_config(DAEMON_NAME,
|
||||
&pf_lsasd_cfg,
|
||||
&default_pf_lsasd_cfg);
|
||||
pfh_manage_pool(ev_ctx, msg, &pf_lsasd_cfg, lsasd_pool);
|
||||
}
|
||||
}
|
||||
|
||||
static void lsasd_sig_term_handler(struct tevent_context *ev,
|
||||
struct tevent_signal *se,
|
||||
int signum,
|
||||
int count,
|
||||
void *siginfo,
|
||||
void *private_data)
|
||||
{
|
||||
exit_server_cleanly("termination signal");
|
||||
}
|
||||
|
||||
static void lsasd_setup_sig_term_handler(struct tevent_context *ev_ctx)
|
||||
{
|
||||
struct tevent_signal *se;
|
||||
|
||||
se = tevent_add_signal(ev_ctx,
|
||||
ev_ctx,
|
||||
SIGTERM, 0,
|
||||
lsasd_sig_term_handler,
|
||||
NULL);
|
||||
if (!se) {
|
||||
exit_server("failed to setup SIGTERM handler");
|
||||
}
|
||||
}
|
||||
|
||||
static void lsasd_sig_hup_handler(struct tevent_context *ev,
|
||||
struct tevent_signal *se,
|
||||
int signum,
|
||||
int count,
|
||||
void *siginfo,
|
||||
void *pvt)
|
||||
{
|
||||
|
||||
change_to_root_user();
|
||||
lp_load_global(get_dyn_CONFIGFILE());
|
||||
|
||||
lsasd_reopen_logs(lsasd_child_id);
|
||||
pfh_daemon_config(DAEMON_NAME,
|
||||
&pf_lsasd_cfg,
|
||||
&default_pf_lsasd_cfg);
|
||||
|
||||
/* relay to all children */
|
||||
prefork_send_signal_to_all(lsasd_pool, SIGHUP);
|
||||
}
|
||||
|
||||
static void lsasd_setup_sig_hup_handler(struct tevent_context *ev_ctx)
|
||||
{
|
||||
struct tevent_signal *se;
|
||||
|
||||
se = tevent_add_signal(ev_ctx,
|
||||
ev_ctx,
|
||||
SIGHUP, 0,
|
||||
lsasd_sig_hup_handler,
|
||||
NULL);
|
||||
if (!se) {
|
||||
DEBUG(0, ("failed to setup SIGHUP handler\n"));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
* Children
|
||||
**********************************************************/
|
||||
|
||||
static void lsasd_chld_sig_hup_handler(struct tevent_context *ev,
|
||||
struct tevent_signal *se,
|
||||
int signum,
|
||||
int count,
|
||||
void *siginfo,
|
||||
void *pvt)
|
||||
{
|
||||
change_to_root_user();
|
||||
lsasd_reopen_logs(lsasd_child_id);
|
||||
}
|
||||
|
||||
static bool lsasd_setup_chld_hup_handler(struct tevent_context *ev_ctx)
|
||||
{
|
||||
struct tevent_signal *se;
|
||||
|
||||
se = tevent_add_signal(ev_ctx,
|
||||
ev_ctx,
|
||||
SIGHUP, 0,
|
||||
lsasd_chld_sig_hup_handler,
|
||||
NULL);
|
||||
if (!se) {
|
||||
DEBUG(1, ("failed to setup SIGHUP handler"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void parent_ping(struct messaging_context *msg_ctx,
|
||||
void *private_data,
|
||||
uint32_t msg_type,
|
||||
struct server_id server_id,
|
||||
DATA_BLOB *data)
|
||||
{
|
||||
|
||||
/* The fact we received this message is enough to let make the event
|
||||
* loop if it was idle. lsasd_children_main will cycle through
|
||||
* lsasd_next_client at least once. That function will take whatever
|
||||
* action is necessary */
|
||||
|
||||
DEBUG(10, ("Got message that the parent changed status.\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
static bool lsasd_child_init(struct tevent_context *ev_ctx,
|
||||
int child_id,
|
||||
struct pf_worker_data *pf)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct messaging_context *msg_ctx = global_messaging_context();
|
||||
bool ok;
|
||||
|
||||
status = reinit_after_fork(msg_ctx, ev_ctx,
|
||||
true, "lsasd-child");
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("reinit_after_fork() failed\n"));
|
||||
smb_panic("reinit_after_fork() failed");
|
||||
}
|
||||
initialize_password_db(true, ev_ctx);
|
||||
|
||||
lsasd_child_id = child_id;
|
||||
lsasd_reopen_logs(child_id);
|
||||
|
||||
ok = lsasd_setup_chld_hup_handler(ev_ctx);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
messaging_register(msg_ctx, ev_ctx,
|
||||
MSG_SMB_CONF_UPDATED, lsasd_smb_conf_updated);
|
||||
messaging_register(msg_ctx, ev_ctx,
|
||||
MSG_PREFORK_PARENT_EVENT, parent_ping);
|
||||
id_cache_register_msgs(msg_ctx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct lsasd_children_data {
|
||||
struct tevent_context *ev_ctx;
|
||||
struct messaging_context *msg_ctx;
|
||||
struct dcesrv_context *dce_ctx;
|
||||
struct pf_worker_data *pf;
|
||||
int listen_fd_size;
|
||||
struct pf_listen_fd *listen_fds;
|
||||
};
|
||||
|
||||
static void lsasd_next_client(void *pvt);
|
||||
|
||||
static int lsasd_children_main(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct pf_worker_data *pf,
|
||||
int child_id,
|
||||
int listen_fd_size,
|
||||
struct pf_listen_fd *listen_fds,
|
||||
void *private_data)
|
||||
{
|
||||
struct lsasd_children_data *data;
|
||||
bool ok;
|
||||
int ret = 0;
|
||||
struct dcesrv_context *dce_ctx = NULL;
|
||||
|
||||
dce_ctx = talloc_get_type_abort(private_data, struct dcesrv_context);
|
||||
|
||||
ok = lsasd_child_init(ev_ctx, child_id, pf);
|
||||
if (!ok) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
data = talloc(ev_ctx, struct lsasd_children_data);
|
||||
if (!data) {
|
||||
return 1;
|
||||
}
|
||||
data->pf = pf;
|
||||
data->ev_ctx = ev_ctx;
|
||||
data->msg_ctx = msg_ctx;
|
||||
data->dce_ctx = dce_ctx;
|
||||
data->listen_fd_size = listen_fd_size;
|
||||
data->listen_fds = listen_fds;
|
||||
|
||||
/* loop until it is time to exit */
|
||||
while (pf->status != PF_WORKER_EXITING) {
|
||||
/* try to see if it is time to schedule the next client */
|
||||
lsasd_next_client(data);
|
||||
|
||||
ret = tevent_loop_once(ev_ctx);
|
||||
if (ret != 0) {
|
||||
DEBUG(0, ("tevent_loop_once() exited with %d: %s\n",
|
||||
ret, strerror(errno)));
|
||||
pf->status = PF_WORKER_EXITING;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void lsasd_client_terminated(struct dcesrv_connection *conn, void *pvt)
|
||||
{
|
||||
struct lsasd_children_data *data;
|
||||
|
||||
data = talloc_get_type_abort(pvt, struct lsasd_children_data);
|
||||
|
||||
pfh_client_terminated(data->pf);
|
||||
|
||||
lsasd_next_client(pvt);
|
||||
}
|
||||
|
||||
struct lsasd_new_client {
|
||||
struct lsasd_children_data *data;
|
||||
};
|
||||
|
||||
static void lsasd_handle_client(struct tevent_req *req);
|
||||
|
||||
static void lsasd_next_client(void *pvt)
|
||||
{
|
||||
struct tevent_req *req;
|
||||
struct lsasd_children_data *data;
|
||||
struct lsasd_new_client *next;
|
||||
|
||||
data = talloc_get_type_abort(pvt, struct lsasd_children_data);
|
||||
|
||||
if (!pfh_child_allowed_to_accept(data->pf)) {
|
||||
/* nothing to do for now we are already listening
|
||||
* or we are not allowed to listen further */
|
||||
return;
|
||||
}
|
||||
|
||||
next = talloc_zero(data, struct lsasd_new_client);
|
||||
if (!next) {
|
||||
DEBUG(1, ("Out of memory!?\n"));
|
||||
return;
|
||||
}
|
||||
next->data = data;
|
||||
|
||||
req = prefork_listen_send(next,
|
||||
data->ev_ctx,
|
||||
data->pf,
|
||||
data->listen_fd_size,
|
||||
data->listen_fds);
|
||||
if (!req) {
|
||||
DEBUG(1, ("Failed to make listening request!?\n"));
|
||||
talloc_free(next);
|
||||
return;
|
||||
}
|
||||
tevent_req_set_callback(req, lsasd_handle_client, next);
|
||||
}
|
||||
|
||||
static void lsasd_handle_client(struct tevent_req *req)
|
||||
{
|
||||
struct lsasd_children_data *data;
|
||||
struct lsasd_new_client *client;
|
||||
const DATA_BLOB ping = data_blob_null;
|
||||
int rc;
|
||||
int sd;
|
||||
TALLOC_CTX *tmp_ctx;
|
||||
struct tsocket_address *srv_addr;
|
||||
struct tsocket_address *cli_addr;
|
||||
void *listen_fd_data = NULL;
|
||||
struct dcesrv_endpoint *ep = NULL;
|
||||
enum dcerpc_transport_t transport;
|
||||
dcerpc_ncacn_termination_fn term_fn = NULL;
|
||||
void *term_fn_data = NULL;
|
||||
|
||||
client = tevent_req_callback_data(req, struct lsasd_new_client);
|
||||
data = client->data;
|
||||
|
||||
tmp_ctx = talloc_stackframe();
|
||||
if (tmp_ctx == NULL) {
|
||||
DEBUG(1, ("Failed to allocate stackframe!\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
rc = prefork_listen_recv(req,
|
||||
tmp_ctx,
|
||||
&sd,
|
||||
&listen_fd_data,
|
||||
&srv_addr,
|
||||
&cli_addr);
|
||||
|
||||
/* this will free the request too */
|
||||
talloc_free(client);
|
||||
|
||||
if (rc != 0) {
|
||||
DEBUG(6, ("No client connection was available after all!\n"));
|
||||
goto done;
|
||||
}
|
||||
|
||||
ep = talloc_get_type_abort(listen_fd_data, struct dcesrv_endpoint);
|
||||
transport = dcerpc_binding_get_transport(ep->ep_description);
|
||||
if (transport == NCACN_NP) {
|
||||
term_fn = lsasd_client_terminated;
|
||||
term_fn_data = data;
|
||||
}
|
||||
|
||||
/* Warn parent that our status changed */
|
||||
messaging_send(data->msg_ctx, parent_id,
|
||||
MSG_PREFORK_CHILD_EVENT, &ping);
|
||||
|
||||
DBG_INFO("LSASD preforked child %d got client connection on '%s'\n",
|
||||
(int)(data->pf->pid), dcerpc_binding_string(tmp_ctx,
|
||||
ep->ep_description));
|
||||
|
||||
dcerpc_ncacn_accept(data->ev_ctx,
|
||||
data->msg_ctx,
|
||||
data->dce_ctx,
|
||||
ep,
|
||||
&cli_addr,
|
||||
&srv_addr,
|
||||
sd,
|
||||
term_fn,
|
||||
term_fn_data);
|
||||
|
||||
done:
|
||||
talloc_free(tmp_ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* MAIN
|
||||
*/
|
||||
|
||||
static void child_ping(struct messaging_context *msg_ctx,
|
||||
void *private_data,
|
||||
uint32_t msg_type,
|
||||
struct server_id server_id,
|
||||
DATA_BLOB *data)
|
||||
{
|
||||
struct tevent_context *ev_ctx;
|
||||
|
||||
ev_ctx = talloc_get_type_abort(private_data, struct tevent_context);
|
||||
|
||||
DEBUG(10, ("Got message that a child changed status.\n"));
|
||||
pfh_manage_pool(ev_ctx, msg_ctx, &pf_lsasd_cfg, lsasd_pool);
|
||||
}
|
||||
|
||||
static bool lsasd_schedule_check(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct timeval current_time);
|
||||
|
||||
static void lsasd_check_children(struct tevent_context *ev_ctx,
|
||||
struct tevent_timer *te,
|
||||
struct timeval current_time,
|
||||
void *pvt);
|
||||
|
||||
static void lsasd_sigchld_handler(struct tevent_context *ev_ctx,
|
||||
struct prefork_pool *pfp,
|
||||
void *pvt)
|
||||
{
|
||||
struct messaging_context *msg_ctx;
|
||||
|
||||
msg_ctx = talloc_get_type_abort(pvt, struct messaging_context);
|
||||
|
||||
/* run pool management so we can fork/retire or increase
|
||||
* the allowed connections per child based on load */
|
||||
pfh_manage_pool(ev_ctx, msg_ctx, &pf_lsasd_cfg, lsasd_pool);
|
||||
}
|
||||
|
||||
static bool lsasd_setup_children_monitor(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
/* add our oun sigchld callback */
|
||||
prefork_set_sigchld_callback(lsasd_pool, lsasd_sigchld_handler, msg_ctx);
|
||||
|
||||
ok = lsasd_schedule_check(ev_ctx, msg_ctx, tevent_timeval_current());
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool lsasd_schedule_check(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct timeval current_time)
|
||||
{
|
||||
struct tevent_timer *te;
|
||||
struct timeval next_event;
|
||||
|
||||
/* check situation again in 10 seconds */
|
||||
next_event = tevent_timeval_current_ofs(10, 0);
|
||||
|
||||
/* TODO: check when the socket becomes readable, so that children
|
||||
* are checked only when there is some activity ? */
|
||||
te = tevent_add_timer(ev_ctx, lsasd_pool, next_event,
|
||||
lsasd_check_children, msg_ctx);
|
||||
if (!te) {
|
||||
DEBUG(2, ("Failed to set up children monitoring!\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void lsasd_check_children(struct tevent_context *ev_ctx,
|
||||
struct tevent_timer *te,
|
||||
struct timeval current_time,
|
||||
void *pvt)
|
||||
{
|
||||
struct messaging_context *msg_ctx;
|
||||
|
||||
msg_ctx = talloc_get_type_abort(pvt, struct messaging_context);
|
||||
|
||||
pfh_manage_pool(ev_ctx, msg_ctx, &pf_lsasd_cfg, lsasd_pool);
|
||||
|
||||
lsasd_schedule_check(ev_ctx, msg_ctx, current_time);
|
||||
}
|
||||
|
||||
/*
|
||||
* start it up
|
||||
*/
|
||||
|
||||
static NTSTATUS lsasd_create_sockets(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct pf_listen_fd **plisten_fds,
|
||||
size_t *pnum_listen_fds)
|
||||
{
|
||||
NTSTATUS status;
|
||||
size_t i, num_fds;
|
||||
struct pf_listen_fd *fds = NULL;
|
||||
int rc;
|
||||
struct dcesrv_endpoint *e = dce_ctx->endpoint_list;
|
||||
|
||||
DBG_INFO("Initializing DCE/RPC connection endpoints\n");
|
||||
|
||||
status = dcesrv_create_endpoint_list_pf_listen_fds(
|
||||
ev_ctx, msg_ctx, dce_ctx, e, mem_ctx, &num_fds, &fds);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_fds; i++) {
|
||||
rc = listen(fds[i].fd, pf_lsasd_cfg.max_allowed_clients);
|
||||
if (rc == -1) {
|
||||
char *ep_string = NULL;
|
||||
e = fds[i].fd_data;
|
||||
|
||||
ep_string = dcerpc_binding_string(dce_ctx,
|
||||
e->ep_description);
|
||||
DBG_ERR("Failed to listen on endpoint '%s': %s\n",
|
||||
ep_string, strerror(errno));
|
||||
status = map_nt_error_from_unix(errno);
|
||||
TALLOC_FREE(ep_string);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
for (e = dce_ctx->endpoint_list; e; e = e->next) {
|
||||
struct dcesrv_if_list *ifl = NULL;
|
||||
for (ifl = e->interface_list; ifl; ifl = ifl->next) {
|
||||
status = rpc_ep_register(ev_ctx,
|
||||
msg_ctx,
|
||||
dce_ctx,
|
||||
ifl->iface);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register interface in "
|
||||
"endpoint mapper: %s",
|
||||
nt_errstr(status));
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*plisten_fds = fds;
|
||||
*pnum_listen_fds = num_fds;
|
||||
|
||||
status = NT_STATUS_OK;
|
||||
done:
|
||||
return status;
|
||||
}
|
||||
|
||||
void start_lsasd(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct pf_listen_fd *listen_fd = NULL;
|
||||
size_t listen_fd_size = 0;
|
||||
pid_t pid;
|
||||
int rc;
|
||||
bool ok;
|
||||
const struct dcesrv_endpoint_server *ep_server = NULL;
|
||||
const char *ep_servers[] = { "lsarpc", "samr", "netlogon", NULL };
|
||||
|
||||
DEBUG(1, ("Forking LSA Service Daemon\n"));
|
||||
|
||||
/*
|
||||
* Block signals before forking child as it will have to
|
||||
* set its own handlers. Child will re-enable SIGHUP as
|
||||
* soon as the handlers are set up.
|
||||
*/
|
||||
BlockSignals(true, SIGTERM);
|
||||
BlockSignals(true, SIGHUP);
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1) {
|
||||
DEBUG(0, ("Failed to fork LSASD [%s], aborting ...\n",
|
||||
strerror(errno)));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* parent or error */
|
||||
if (pid != 0) {
|
||||
|
||||
/* Re-enable SIGHUP before returnig */
|
||||
BlockSignals(false, SIGTERM);
|
||||
BlockSignals(false, SIGHUP);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
status = smbd_reinit_after_fork(msg_ctx, ev_ctx, true, "lsasd-master");
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("reinit_after_fork() failed\n"));
|
||||
smb_panic("reinit_after_fork() failed");
|
||||
}
|
||||
initialize_password_db(true, ev_ctx);
|
||||
|
||||
/* save the parent process id so the children can use it later */
|
||||
parent_id = messaging_server_id(msg_ctx);
|
||||
|
||||
lsasd_reopen_logs(0);
|
||||
pfh_daemon_config(DAEMON_NAME,
|
||||
&pf_lsasd_cfg,
|
||||
&default_pf_lsasd_cfg);
|
||||
|
||||
lsasd_setup_sig_term_handler(ev_ctx);
|
||||
lsasd_setup_sig_hup_handler(ev_ctx);
|
||||
|
||||
BlockSignals(false, SIGTERM);
|
||||
BlockSignals(false, SIGHUP);
|
||||
|
||||
DBG_INFO("Registering DCE/RPC endpoint servers\n");
|
||||
|
||||
ep_server = lsarpc_get_ep_server();
|
||||
if (ep_server == NULL) {
|
||||
DBG_ERR("Failed to get 'lsarpc' endpoint server\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
status = dcerpc_register_ep_server(ep_server);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register 'lsarpc' endpoint server: %s\n",
|
||||
nt_errstr(status));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ep_server = samr_get_ep_server();
|
||||
if (ep_server == NULL) {
|
||||
DBG_ERR("Failed to get 'samr' endpoint server\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
status = dcerpc_register_ep_server(ep_server);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register 'samr' endpoint server: %s\n",
|
||||
nt_errstr(status));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ep_server = netlogon_get_ep_server();
|
||||
if (ep_server == NULL) {
|
||||
DBG_ERR("Failed to get 'netlogon' endpoint server\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
status = dcerpc_register_ep_server(ep_server);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register 'netlogon' endpoint server: %s\n",
|
||||
nt_errstr(status));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DBG_INFO("Reinitializing DCE/RPC server context\n");
|
||||
|
||||
status = dcesrv_reinit_context(dce_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to reinit DCE/RPC context: %s\n",
|
||||
nt_errstr(status));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DBG_INFO("Initializing DCE/RPC registered endpoint servers\n");
|
||||
|
||||
/* Init ep servers */
|
||||
status = dcesrv_init_ep_servers(dce_ctx, ep_servers);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to init DCE/RPC endpoint server: %s\n",
|
||||
nt_errstr(status));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
status = lsasd_create_sockets(ev_ctx,
|
||||
msg_ctx,
|
||||
dce_ctx,
|
||||
dce_ctx,
|
||||
&listen_fd,
|
||||
&listen_fd_size);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* start children before any more initialization is done */
|
||||
ok = prefork_create_pool(ev_ctx, /* mem_ctx */
|
||||
ev_ctx,
|
||||
msg_ctx,
|
||||
listen_fd_size,
|
||||
listen_fd,
|
||||
pf_lsasd_cfg.min_children,
|
||||
pf_lsasd_cfg.max_children,
|
||||
&lsasd_children_main,
|
||||
dce_ctx,
|
||||
&lsasd_pool);
|
||||
TALLOC_FREE(listen_fd);
|
||||
if (!ok) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
messaging_register(msg_ctx,
|
||||
ev_ctx,
|
||||
MSG_SMB_CONF_UPDATED,
|
||||
lsasd_smb_conf_updated);
|
||||
messaging_register(msg_ctx, ev_ctx,
|
||||
MSG_PREFORK_CHILD_EVENT, child_ping);
|
||||
|
||||
ok = lsasd_setup_children_monitor(ev_ctx, msg_ctx);
|
||||
if (!ok) {
|
||||
DEBUG(0, ("Failed to setup children monitoring!\n"));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DEBUG(1, ("LSASD Daemon Started (%u)\n", (unsigned int)getpid()));
|
||||
|
||||
/* loop forever */
|
||||
rc = tevent_loop_wait(ev_ctx);
|
||||
|
||||
/* should not be reached */
|
||||
DEBUG(0,("lsasd: tevent_loop_wait() exited with %d - %s\n",
|
||||
rc, (rc == 0) ? "out of events" : strerror(errno)));
|
||||
exit(1);
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
*
|
||||
* LSASD header file
|
||||
*
|
||||
* Copyright (c) 2018 Volker Lendecke <vl@samba.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __RPC_SERVER_LSASD_H__
|
||||
#define __RPC_SERVER_LSASD_H__
|
||||
|
||||
#include "replace.h"
|
||||
#include "messages.h"
|
||||
|
||||
struct dcesrv_context;
|
||||
|
||||
void start_lsasd(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx);
|
||||
|
||||
#endif
|
@ -1,691 +0,0 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
*
|
||||
* mds service daemon
|
||||
*
|
||||
* Copyright (c) 2014 Ralph Boehme <rb@sernet.de>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "messages.h"
|
||||
#include "ntdomain.h"
|
||||
|
||||
#include "lib/id_cache.h"
|
||||
|
||||
#include "../lib/tsocket/tsocket.h"
|
||||
#include "lib/server_prefork.h"
|
||||
#include "lib/server_prefork_util.h"
|
||||
#include "librpc/rpc/dcerpc_ep.h"
|
||||
#include "librpc/rpc/dcesrv_core.h"
|
||||
|
||||
#include "rpc_server/rpc_server.h"
|
||||
#include "rpc_server/rpc_service_setup.h"
|
||||
#include "rpc_server/rpc_ep_register.h"
|
||||
#include "rpc_server/rpc_sock_helper.h"
|
||||
#include "rpc_server/rpc_modules.h"
|
||||
|
||||
#include "rpc_server/mdssvc/srv_mdssvc_nt.h"
|
||||
#include "rpc_server/mdssd.h"
|
||||
#include "lib/global_contexts.h"
|
||||
|
||||
#undef DBGC_CLASS
|
||||
#define DBGC_CLASS DBGC_RPC_SRV
|
||||
|
||||
#define DAEMON_NAME "mdssd"
|
||||
|
||||
static struct server_id parent_id;
|
||||
static struct prefork_pool *mdssd_pool = NULL;
|
||||
static int mdssd_child_id = 0;
|
||||
|
||||
static const struct pf_daemon_config default_pf_mdssd_cfg = {
|
||||
.prefork_status = PFH_INIT,
|
||||
.min_children = 5,
|
||||
.max_children = 25,
|
||||
.spawn_rate = 5,
|
||||
.max_allowed_clients = 1000,
|
||||
.child_min_life = 60 /* 1 minute minimum life time */
|
||||
};
|
||||
static struct pf_daemon_config pf_mdssd_cfg = { 0 };
|
||||
|
||||
static void mdssd_smb_conf_updated(struct messaging_context *msg,
|
||||
void *private_data,
|
||||
uint32_t msg_type,
|
||||
struct server_id server_id,
|
||||
DATA_BLOB *data)
|
||||
{
|
||||
struct tevent_context *ev_ctx;
|
||||
|
||||
DEBUG(10, ("Got message saying smb.conf was updated. Reloading.\n"));
|
||||
ev_ctx = talloc_get_type_abort(private_data, struct tevent_context);
|
||||
|
||||
change_to_root_user();
|
||||
lp_load_global(get_dyn_CONFIGFILE());
|
||||
|
||||
reopen_logs();
|
||||
if (mdssd_child_id == 0) {
|
||||
pfh_daemon_config(DAEMON_NAME,
|
||||
&pf_mdssd_cfg,
|
||||
&default_pf_mdssd_cfg);
|
||||
pfh_manage_pool(ev_ctx, msg, &pf_mdssd_cfg, mdssd_pool);
|
||||
}
|
||||
}
|
||||
|
||||
static void mdssd_sig_term_handler(struct tevent_context *ev,
|
||||
struct tevent_signal *se,
|
||||
int signum,
|
||||
int count,
|
||||
void *siginfo,
|
||||
void *private_data)
|
||||
{
|
||||
exit_server_cleanly("termination signal");
|
||||
}
|
||||
|
||||
static void mdssd_setup_sig_term_handler(struct tevent_context *ev_ctx)
|
||||
{
|
||||
struct tevent_signal *se;
|
||||
|
||||
se = tevent_add_signal(ev_ctx,
|
||||
ev_ctx,
|
||||
SIGTERM, 0,
|
||||
mdssd_sig_term_handler,
|
||||
NULL);
|
||||
if (!se) {
|
||||
exit_server("failed to setup SIGTERM handler");
|
||||
}
|
||||
}
|
||||
|
||||
static void mdssd_sig_hup_handler(struct tevent_context *ev,
|
||||
struct tevent_signal *se,
|
||||
int signum,
|
||||
int count,
|
||||
void *siginfo,
|
||||
void *pvt)
|
||||
{
|
||||
|
||||
change_to_root_user();
|
||||
lp_load_global(get_dyn_CONFIGFILE());
|
||||
|
||||
reopen_logs();
|
||||
pfh_daemon_config(DAEMON_NAME,
|
||||
&pf_mdssd_cfg,
|
||||
&default_pf_mdssd_cfg);
|
||||
|
||||
/* relay to all children */
|
||||
prefork_send_signal_to_all(mdssd_pool, SIGHUP);
|
||||
}
|
||||
|
||||
static void mdssd_setup_sig_hup_handler(struct tevent_context *ev_ctx)
|
||||
{
|
||||
struct tevent_signal *se;
|
||||
|
||||
se = tevent_add_signal(ev_ctx,
|
||||
ev_ctx,
|
||||
SIGHUP, 0,
|
||||
mdssd_sig_hup_handler,
|
||||
NULL);
|
||||
if (!se) {
|
||||
DEBUG(0, ("failed to setup SIGHUP handler\n"));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************
|
||||
* Children
|
||||
**********************************************************/
|
||||
|
||||
static void mdssd_chld_sig_hup_handler(struct tevent_context *ev,
|
||||
struct tevent_signal *se,
|
||||
int signum,
|
||||
int count,
|
||||
void *siginfo,
|
||||
void *pvt)
|
||||
{
|
||||
change_to_root_user();
|
||||
reopen_logs();
|
||||
}
|
||||
|
||||
static bool mdssd_setup_chld_hup_handler(struct tevent_context *ev_ctx)
|
||||
{
|
||||
struct tevent_signal *se;
|
||||
|
||||
se = tevent_add_signal(ev_ctx,
|
||||
ev_ctx,
|
||||
SIGHUP, 0,
|
||||
mdssd_chld_sig_hup_handler,
|
||||
NULL);
|
||||
if (!se) {
|
||||
DEBUG(1, ("failed to setup SIGHUP handler"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void parent_ping(struct messaging_context *msg_ctx,
|
||||
void *private_data,
|
||||
uint32_t msg_type,
|
||||
struct server_id server_id,
|
||||
DATA_BLOB *data)
|
||||
{
|
||||
/*
|
||||
* The fact we received this message is enough to let make the
|
||||
* event loop if it was idle. mdssd_children_main will cycle
|
||||
* through mdssd_next_client at least once. That function will
|
||||
* take whatever action is necessary
|
||||
*/
|
||||
DEBUG(10, ("Got message that the parent changed status.\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
static bool mdssd_child_init(struct tevent_context *ev_ctx,
|
||||
int child_id,
|
||||
struct pf_worker_data *pf)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct messaging_context *msg_ctx = global_messaging_context();
|
||||
bool ok;
|
||||
|
||||
status = reinit_after_fork(msg_ctx, ev_ctx,
|
||||
true, "mdssd-child");
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("reinit_after_fork() failed\n"));
|
||||
smb_panic("reinit_after_fork() failed");
|
||||
}
|
||||
|
||||
mdssd_child_id = child_id;
|
||||
reopen_logs();
|
||||
|
||||
ok = mdssd_setup_chld_hup_handler(ev_ctx);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
messaging_register(msg_ctx, ev_ctx,
|
||||
MSG_SMB_CONF_UPDATED, mdssd_smb_conf_updated);
|
||||
messaging_register(msg_ctx, ev_ctx,
|
||||
MSG_PREFORK_PARENT_EVENT, parent_ping);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct mdssd_children_data {
|
||||
struct tevent_context *ev_ctx;
|
||||
struct messaging_context *msg_ctx;
|
||||
struct dcesrv_context *dce_ctx;
|
||||
struct pf_worker_data *pf;
|
||||
int listen_fd_size;
|
||||
struct pf_listen_fd *listen_fds;
|
||||
};
|
||||
|
||||
static void mdssd_next_client(void *pvt);
|
||||
|
||||
static int mdssd_children_main(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct pf_worker_data *pf,
|
||||
int child_id,
|
||||
int listen_fd_size,
|
||||
struct pf_listen_fd *listen_fds,
|
||||
void *private_data)
|
||||
{
|
||||
struct mdssd_children_data *data;
|
||||
bool ok;
|
||||
int ret = 0;
|
||||
struct dcesrv_context *dce_ctx = NULL;
|
||||
|
||||
dce_ctx = talloc_get_type_abort(private_data, struct dcesrv_context);
|
||||
|
||||
ok = mdssd_child_init(ev_ctx, child_id, pf);
|
||||
if (!ok) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
data = talloc(ev_ctx, struct mdssd_children_data);
|
||||
if (!data) {
|
||||
return 1;
|
||||
}
|
||||
data->pf = pf;
|
||||
data->ev_ctx = ev_ctx;
|
||||
data->msg_ctx = msg_ctx;
|
||||
data->dce_ctx = dce_ctx;
|
||||
data->listen_fd_size = listen_fd_size;
|
||||
data->listen_fds = listen_fds;
|
||||
|
||||
/* loop until it is time to exit */
|
||||
while (pf->status != PF_WORKER_EXITING) {
|
||||
/* try to see if it is time to schedule the next client */
|
||||
mdssd_next_client(data);
|
||||
|
||||
ret = tevent_loop_once(ev_ctx);
|
||||
if (ret != 0) {
|
||||
DEBUG(0, ("tevent_loop_once() exited with %d: %s\n",
|
||||
ret, strerror(errno)));
|
||||
pf->status = PF_WORKER_EXITING;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mdssd_client_terminated(struct dcesrv_connection *conn, void *pvt)
|
||||
{
|
||||
struct mdssd_children_data *data;
|
||||
|
||||
data = talloc_get_type_abort(pvt, struct mdssd_children_data);
|
||||
|
||||
pfh_client_terminated(data->pf);
|
||||
|
||||
mdssd_next_client(pvt);
|
||||
}
|
||||
|
||||
struct mdssd_new_client {
|
||||
struct mdssd_children_data *data;
|
||||
};
|
||||
|
||||
static void mdssd_handle_client(struct tevent_req *req);
|
||||
|
||||
static void mdssd_next_client(void *pvt)
|
||||
{
|
||||
struct tevent_req *req;
|
||||
struct mdssd_children_data *data;
|
||||
struct mdssd_new_client *next;
|
||||
|
||||
data = talloc_get_type_abort(pvt, struct mdssd_children_data);
|
||||
|
||||
if (!pfh_child_allowed_to_accept(data->pf)) {
|
||||
/* nothing to do for now we are already listening
|
||||
* or we are not allowed to listen further */
|
||||
return;
|
||||
}
|
||||
|
||||
next = talloc_zero(data, struct mdssd_new_client);
|
||||
if (!next) {
|
||||
DEBUG(1, ("Out of memory!?\n"));
|
||||
return;
|
||||
}
|
||||
next->data = data;
|
||||
|
||||
req = prefork_listen_send(next,
|
||||
data->ev_ctx,
|
||||
data->pf,
|
||||
data->listen_fd_size,
|
||||
data->listen_fds);
|
||||
if (!req) {
|
||||
DEBUG(1, ("Failed to make listening request!?\n"));
|
||||
talloc_free(next);
|
||||
return;
|
||||
}
|
||||
tevent_req_set_callback(req, mdssd_handle_client, next);
|
||||
}
|
||||
|
||||
static void mdssd_handle_client(struct tevent_req *req)
|
||||
{
|
||||
struct mdssd_children_data *data;
|
||||
struct mdssd_new_client *client;
|
||||
const DATA_BLOB ping = data_blob_null;
|
||||
int rc;
|
||||
int sd;
|
||||
TALLOC_CTX *tmp_ctx;
|
||||
struct tsocket_address *srv_addr;
|
||||
struct tsocket_address *cli_addr;
|
||||
void *listen_fd_data = NULL;
|
||||
struct dcesrv_endpoint *ep = NULL;
|
||||
enum dcerpc_transport_t transport;
|
||||
dcerpc_ncacn_termination_fn term_fn = NULL;
|
||||
void *term_fn_data = NULL;
|
||||
|
||||
client = tevent_req_callback_data(req, struct mdssd_new_client);
|
||||
data = client->data;
|
||||
|
||||
tmp_ctx = talloc_stackframe();
|
||||
if (tmp_ctx == NULL) {
|
||||
DEBUG(1, ("Failed to allocate stackframe!\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
rc = prefork_listen_recv(req,
|
||||
tmp_ctx,
|
||||
&sd,
|
||||
&listen_fd_data,
|
||||
&srv_addr,
|
||||
&cli_addr);
|
||||
|
||||
/* this will free the request too */
|
||||
talloc_free(client);
|
||||
|
||||
if (rc != 0) {
|
||||
DEBUG(6, ("No client connection was available after all!\n"));
|
||||
goto done;
|
||||
}
|
||||
|
||||
ep = talloc_get_type_abort(listen_fd_data, struct dcesrv_endpoint);
|
||||
transport = dcerpc_binding_get_transport(ep->ep_description);
|
||||
if (transport == NCACN_NP) {
|
||||
term_fn = mdssd_client_terminated;
|
||||
term_fn_data = data;
|
||||
}
|
||||
|
||||
/* Warn parent that our status changed */
|
||||
messaging_send(data->msg_ctx, parent_id,
|
||||
MSG_PREFORK_CHILD_EVENT, &ping);
|
||||
|
||||
DBG_INFO("MDSSD preforked child %d got client connection on '%s'\n",
|
||||
(int)(data->pf->pid), dcerpc_binding_string(tmp_ctx,
|
||||
ep->ep_description));
|
||||
|
||||
dcerpc_ncacn_accept(data->ev_ctx,
|
||||
data->msg_ctx,
|
||||
data->dce_ctx,
|
||||
ep,
|
||||
&cli_addr,
|
||||
&srv_addr,
|
||||
sd,
|
||||
term_fn,
|
||||
term_fn_data);
|
||||
|
||||
done:
|
||||
talloc_free(tmp_ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* MAIN
|
||||
*/
|
||||
|
||||
static void child_ping(struct messaging_context *msg_ctx,
|
||||
void *private_data,
|
||||
uint32_t msg_type,
|
||||
struct server_id server_id,
|
||||
DATA_BLOB *data)
|
||||
{
|
||||
struct tevent_context *ev_ctx;
|
||||
|
||||
ev_ctx = talloc_get_type_abort(private_data, struct tevent_context);
|
||||
|
||||
DEBUG(10, ("Got message that a child changed status.\n"));
|
||||
pfh_manage_pool(ev_ctx, msg_ctx, &pf_mdssd_cfg, mdssd_pool);
|
||||
}
|
||||
|
||||
static bool mdssd_schedule_check(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct timeval current_time);
|
||||
|
||||
static void mdssd_check_children(struct tevent_context *ev_ctx,
|
||||
struct tevent_timer *te,
|
||||
struct timeval current_time,
|
||||
void *pvt);
|
||||
|
||||
static void mdssd_sigchld_handler(struct tevent_context *ev_ctx,
|
||||
struct prefork_pool *pfp,
|
||||
void *pvt)
|
||||
{
|
||||
struct messaging_context *msg_ctx;
|
||||
|
||||
msg_ctx = talloc_get_type_abort(pvt, struct messaging_context);
|
||||
|
||||
/* run pool management so we can fork/retire or increase
|
||||
* the allowed connections per child based on load */
|
||||
pfh_manage_pool(ev_ctx, msg_ctx, &pf_mdssd_cfg, mdssd_pool);
|
||||
}
|
||||
|
||||
static bool mdssd_setup_children_monitor(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
/* add our oun sigchld callback */
|
||||
prefork_set_sigchld_callback(mdssd_pool, mdssd_sigchld_handler, msg_ctx);
|
||||
|
||||
ok = mdssd_schedule_check(ev_ctx, msg_ctx, tevent_timeval_current());
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool mdssd_schedule_check(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct timeval current_time)
|
||||
{
|
||||
struct tevent_timer *te;
|
||||
struct timeval next_event;
|
||||
|
||||
/* check situation again in 10 seconds */
|
||||
next_event = tevent_timeval_current_ofs(10, 0);
|
||||
|
||||
/* TODO: check when the socket becomes readable, so that children
|
||||
* are checked only when there is some activity ? */
|
||||
te = tevent_add_timer(ev_ctx, mdssd_pool, next_event,
|
||||
mdssd_check_children, msg_ctx);
|
||||
if (!te) {
|
||||
DEBUG(2, ("Failed to set up children monitoring!\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void mdssd_check_children(struct tevent_context *ev_ctx,
|
||||
struct tevent_timer *te,
|
||||
struct timeval current_time,
|
||||
void *pvt)
|
||||
{
|
||||
struct messaging_context *msg_ctx;
|
||||
|
||||
msg_ctx = talloc_get_type_abort(pvt, struct messaging_context);
|
||||
|
||||
pfh_manage_pool(ev_ctx, msg_ctx, &pf_mdssd_cfg, mdssd_pool);
|
||||
|
||||
mdssd_schedule_check(ev_ctx, msg_ctx, current_time);
|
||||
}
|
||||
|
||||
/*
|
||||
* start it up
|
||||
*/
|
||||
|
||||
static NTSTATUS mdssd_create_sockets(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct pf_listen_fd **plisten_fds,
|
||||
size_t *pnum_listen_fds)
|
||||
{
|
||||
NTSTATUS status;
|
||||
size_t i, num_fds;
|
||||
struct pf_listen_fd *fds = NULL;
|
||||
int rc;
|
||||
struct dcesrv_endpoint *e = dce_ctx->endpoint_list;
|
||||
|
||||
DBG_INFO("Initializing DCE/RPC connection endpoints\n");
|
||||
|
||||
status = dcesrv_create_endpoint_list_pf_listen_fds(
|
||||
ev_ctx, msg_ctx, dce_ctx, e, mem_ctx, &num_fds, &fds);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_fds; i++) {
|
||||
rc = listen(fds[i].fd, pf_mdssd_cfg.max_allowed_clients);
|
||||
if (rc == -1) {
|
||||
char *ep_string = NULL;
|
||||
e = fds[i].fd_data;
|
||||
|
||||
ep_string = dcerpc_binding_string(dce_ctx,
|
||||
e->ep_description);
|
||||
DBG_ERR("Failed to listen on endpoint '%s': %s\n",
|
||||
ep_string, strerror(errno));
|
||||
status = map_nt_error_from_unix(errno);
|
||||
TALLOC_FREE(ep_string);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
for (e = dce_ctx->endpoint_list; e; e = e->next) {
|
||||
struct dcesrv_if_list *ifl = NULL;
|
||||
for (ifl = e->interface_list; ifl; ifl = ifl->next) {
|
||||
status = rpc_ep_register(ev_ctx,
|
||||
msg_ctx,
|
||||
dce_ctx,
|
||||
ifl->iface);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register interface in "
|
||||
"endpoint mapper: %s",
|
||||
nt_errstr(status));
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*plisten_fds = fds;
|
||||
*pnum_listen_fds = num_fds;
|
||||
|
||||
status = NT_STATUS_OK;
|
||||
done:
|
||||
return status;
|
||||
}
|
||||
|
||||
void start_mdssd(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct pf_listen_fd *listen_fd = NULL;
|
||||
size_t listen_fd_size = 0;
|
||||
pid_t pid;
|
||||
int rc;
|
||||
bool ok;
|
||||
|
||||
DEBUG(1, ("Forking Metadata Service Daemon\n"));
|
||||
|
||||
/*
|
||||
* Block signals before forking child as it will have to
|
||||
* set its own handlers. Child will re-enable SIGHUP as
|
||||
* soon as the handlers are set up.
|
||||
*/
|
||||
BlockSignals(true, SIGTERM);
|
||||
BlockSignals(true, SIGHUP);
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1) {
|
||||
DEBUG(0, ("Failed to fork mdssd [%s], aborting ...\n",
|
||||
strerror(errno)));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* parent or error */
|
||||
if (pid != 0) {
|
||||
|
||||
/* Re-enable SIGHUP before returnig */
|
||||
BlockSignals(false, SIGTERM);
|
||||
BlockSignals(false, SIGHUP);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
status = smbd_reinit_after_fork(msg_ctx, ev_ctx, true, "mdssd-master");
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("reinit_after_fork() failed\n"));
|
||||
smb_panic("reinit_after_fork() failed");
|
||||
}
|
||||
|
||||
reopen_logs();
|
||||
|
||||
/* save the parent process id so the children can use it later */
|
||||
parent_id = messaging_server_id(msg_ctx);
|
||||
|
||||
pfh_daemon_config(DAEMON_NAME,
|
||||
&pf_mdssd_cfg,
|
||||
&default_pf_mdssd_cfg);
|
||||
|
||||
mdssd_setup_sig_term_handler(ev_ctx);
|
||||
mdssd_setup_sig_hup_handler(ev_ctx);
|
||||
|
||||
BlockSignals(false, SIGTERM);
|
||||
BlockSignals(false, SIGHUP);
|
||||
|
||||
/* The module setup function will register the endpoint server,
|
||||
* necessary to setup the endpoints below. It is not possible to
|
||||
* register it here because MDS service is built as a module.
|
||||
*/
|
||||
ok = setup_rpc_module(ev_ctx, msg_ctx, "mdssvc");
|
||||
if (!ok) {
|
||||
DBG_ERR("Failed to setup DCE/RPC module\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DBG_INFO("Reinitializing DCE/RPC server context\n");
|
||||
|
||||
status = dcesrv_reinit_context(dce_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to reinit DCE/RPC context: %s\n",
|
||||
nt_errstr(status));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DBG_INFO("Initializing DCE/RPC registered endpoint servers\n");
|
||||
|
||||
/* Init ep servers */
|
||||
status = dcesrv_init_ep_server(dce_ctx, "mdssvc");
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to init DCE/RPC endpoint server: %s\n",
|
||||
nt_errstr(status));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
status = mdssd_create_sockets(ev_ctx,
|
||||
msg_ctx,
|
||||
dce_ctx,
|
||||
dce_ctx,
|
||||
&listen_fd,
|
||||
&listen_fd_size);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* start children before any more initialization is done */
|
||||
ok = prefork_create_pool(ev_ctx, /* mem_ctx */
|
||||
ev_ctx,
|
||||
msg_ctx,
|
||||
listen_fd_size,
|
||||
listen_fd,
|
||||
pf_mdssd_cfg.min_children,
|
||||
pf_mdssd_cfg.max_children,
|
||||
&mdssd_children_main,
|
||||
dce_ctx,
|
||||
&mdssd_pool);
|
||||
TALLOC_FREE(listen_fd);
|
||||
if (!ok) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
messaging_register(msg_ctx,
|
||||
ev_ctx,
|
||||
MSG_SMB_CONF_UPDATED,
|
||||
mdssd_smb_conf_updated);
|
||||
messaging_register(msg_ctx, ev_ctx,
|
||||
MSG_PREFORK_CHILD_EVENT, child_ping);
|
||||
|
||||
ok = mdssd_setup_children_monitor(ev_ctx, msg_ctx);
|
||||
if (!ok) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DEBUG(1, ("mdssd Daemon Started (%u)\n", (unsigned int)getpid()));
|
||||
|
||||
/* loop forever */
|
||||
rc = tevent_loop_wait(ev_ctx);
|
||||
|
||||
/* should not be reached */
|
||||
DEBUG(0,("mdssd: tevent_loop_wait() exited with %d - %s\n",
|
||||
rc, (rc == 0) ? "out of events" : strerror(errno)));
|
||||
exit(1);
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
*
|
||||
* MDSSD header file
|
||||
*
|
||||
* Copyright (c) 2018 Volker Lendecke <vl@samba.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __RPC_SERVER_MDSSD_H__
|
||||
#define __RPC_SERVER_MDSSD_H__
|
||||
|
||||
#include "replace.h"
|
||||
#include "messages.h"
|
||||
|
||||
struct dcesrv_context;
|
||||
|
||||
void start_mdssd(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx);
|
||||
|
||||
#endif
|
@ -73,131 +73,3 @@ void global_dcesrv_context_free(void)
|
||||
{
|
||||
TALLOC_FREE(global_dcesrv_ctx);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* the default is "embedded" so this table
|
||||
* lists only services that are not using
|
||||
* the default in order to keep enumerating it
|
||||
* in rpc_service_mode() as short as possible
|
||||
*/
|
||||
struct rpc_service_defaults {
|
||||
const char *name;
|
||||
const char *def_mode;
|
||||
} rpc_service_defaults[] = {
|
||||
{ "epmapper", "disabled" },
|
||||
/* { "mdssvc", "embedded" }, */
|
||||
/* { "spoolss", "embedded" }, */
|
||||
/* { "lsarpc", "embedded" }, */
|
||||
/* { "samr", "embedded" }, */
|
||||
/* { "netlogon", "embedded" }, */
|
||||
{ "fssagentrpc", "external" },
|
||||
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
enum rpc_service_mode_e rpc_service_mode(const char *name)
|
||||
{
|
||||
const char *pipe_name = name;
|
||||
const char *rpcsrv_type;
|
||||
enum rpc_service_mode_e state;
|
||||
const char *def;
|
||||
enum server_role server_role = lp_server_role();
|
||||
int i;
|
||||
|
||||
/* Handle pipes with multiple names */
|
||||
if (strcmp(pipe_name, "lsass") == 0) {
|
||||
pipe_name = "lsarpc";
|
||||
} else if (strcmp(pipe_name, "plugplay") == 0) {
|
||||
pipe_name = "ntsvcs";
|
||||
}
|
||||
|
||||
def = lp_parm_const_string(GLOBAL_SECTION_SNUM,
|
||||
"rpc_server", "default", NULL);
|
||||
if (def == NULL) {
|
||||
for (i = 0; rpc_service_defaults[i].name; i++) {
|
||||
if (strcasecmp_m(pipe_name, rpc_service_defaults[i].name) == 0) {
|
||||
def = rpc_service_defaults[i].def_mode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* if the default is unspecified then use 'embedded' */
|
||||
if (def == NULL) {
|
||||
def = "embedded";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Only enable the netlogon server by default if we are a
|
||||
* classic/NT4 domain controller
|
||||
*/
|
||||
if (strcasecmp_m(name, "netlogon") == 0) {
|
||||
switch (server_role) {
|
||||
case ROLE_STANDALONE:
|
||||
case ROLE_DOMAIN_MEMBER:
|
||||
def = "disabled";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rpcsrv_type = lp_parm_const_string(GLOBAL_SECTION_SNUM,
|
||||
"rpc_server", pipe_name, def);
|
||||
|
||||
if (strcasecmp_m(rpcsrv_type, "embedded") == 0) {
|
||||
state = RPC_SERVICE_MODE_EMBEDDED;
|
||||
} else if (strcasecmp_m(rpcsrv_type, "external") == 0) {
|
||||
state = RPC_SERVICE_MODE_EXTERNAL;
|
||||
} else {
|
||||
state = RPC_SERVICE_MODE_DISABLED;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
/* the default is "embedded" so this table
|
||||
* lists only daemons that are not using
|
||||
* the default in order to keep enumerating it
|
||||
* in rpc_daemon_type() as short as possible
|
||||
*/
|
||||
struct rpc_daemon_defaults {
|
||||
const char *name;
|
||||
const char *def_type;
|
||||
} rpc_daemon_defaults[] = {
|
||||
{ "epmd", "disabled" },
|
||||
/* { "spoolssd", "embedded" }, */
|
||||
/* { "lsasd", "embedded" }, */
|
||||
{ "fssd", "disabled" },
|
||||
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
enum rpc_daemon_type_e rpc_daemon_type(const char *name)
|
||||
{
|
||||
const char *rpcsrv_type;
|
||||
enum rpc_daemon_type_e type;
|
||||
const char *def;
|
||||
int i;
|
||||
|
||||
def = "embedded";
|
||||
for (i = 0; rpc_daemon_defaults[i].name; i++) {
|
||||
if (strcasecmp_m(name, rpc_daemon_defaults[i].name) == 0) {
|
||||
def = rpc_daemon_defaults[i].def_type;
|
||||
}
|
||||
}
|
||||
|
||||
rpcsrv_type = lp_parm_const_string(GLOBAL_SECTION_SNUM,
|
||||
"rpc_daemon", name, def);
|
||||
|
||||
if (strcasecmp_m(rpcsrv_type, "embedded") == 0) {
|
||||
type = RPC_DAEMON_EMBEDDED;
|
||||
} else if (strcasecmp_m(rpcsrv_type, "fork") == 0) {
|
||||
type = RPC_DAEMON_FORK;
|
||||
} else {
|
||||
type = RPC_DAEMON_DISABLED;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
#endif
|
||||
|
@ -23,54 +23,6 @@
|
||||
#ifndef _RPC_CONFIG_H
|
||||
#define _RPC_CONFIG_H
|
||||
|
||||
#if 0
|
||||
enum rpc_service_mode_e {
|
||||
RPC_SERVICE_MODE_DISABLED = 0,
|
||||
RPC_SERVICE_MODE_EMBEDDED,
|
||||
RPC_SERVICE_MODE_EXTERNAL
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get the mode in which service pipes are configured.
|
||||
*
|
||||
* @param name Name of the service
|
||||
*
|
||||
* @return The actual configured mode.
|
||||
*/
|
||||
enum rpc_service_mode_e rpc_service_mode(const char *name);
|
||||
|
||||
#define rpc_epmapper_mode() rpc_service_mode("epmapper")
|
||||
#define rpc_spoolss_mode() rpc_service_mode("spoolss")
|
||||
#define rpc_lsarpc_mode() rpc_service_mode("lsarpc")
|
||||
#define rpc_samr_mode() rpc_service_mode("samr")
|
||||
#define rpc_netlogon_mode() rpc_service_mode("netlogon")
|
||||
#define rpc_fssagentrpc_mode() rpc_service_mode("fssagentrpc")
|
||||
#define rpc_mdssvc_mode() rpc_service_mode("mdssvc")
|
||||
|
||||
|
||||
|
||||
enum rpc_daemon_type_e {
|
||||
RPC_DAEMON_DISABLED = 0,
|
||||
RPC_DAEMON_EMBEDDED,
|
||||
RPC_DAEMON_FORK
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get the mode in which a server is started.
|
||||
*
|
||||
* @param name Name of the rpc server
|
||||
*
|
||||
* @return The actual configured type.
|
||||
*/
|
||||
enum rpc_daemon_type_e rpc_daemon_type(const char *name);
|
||||
|
||||
#define rpc_epmapper_daemon() rpc_daemon_type("epmd")
|
||||
#define rpc_spoolss_daemon() rpc_daemon_type("spoolssd")
|
||||
#define rpc_lsasd_daemon() rpc_daemon_type("lsasd")
|
||||
#define rpc_fss_daemon() rpc_daemon_type("fssd")
|
||||
#define rpc_mdssd_daemon() rpc_daemon_type("mdssd")
|
||||
#endif
|
||||
|
||||
struct dcesrv_context;
|
||||
struct dcesrv_context *global_dcesrv_context(void);
|
||||
void global_dcesrv_context_free(void);
|
||||
|
@ -1,279 +0,0 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
*
|
||||
* RPC Endpoint Registration
|
||||
*
|
||||
* Copyright (c) 2011 Andreas Schneider <asn@samba.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ntdomain.h"
|
||||
|
||||
#include "../librpc/gen_ndr/ndr_epmapper_c.h"
|
||||
|
||||
#include "librpc/rpc/dcerpc_ep.h"
|
||||
#include "librpc/rpc/dcesrv_core.h"
|
||||
#include "rpc_server/rpc_ep_register.h"
|
||||
|
||||
#undef DBGC_CLASS
|
||||
#define DBGC_CLASS DBGC_RPC_SRV
|
||||
|
||||
static void rpc_ep_register_loop(struct tevent_req *subreq);
|
||||
static NTSTATUS rpc_ep_try_register(TALLOC_CTX *mem_ctx,
|
||||
struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
const struct dcesrv_interface *iface,
|
||||
struct dcerpc_binding_handle **pbh);
|
||||
|
||||
struct rpc_ep_register_state {
|
||||
struct dcerpc_binding_handle *h;
|
||||
|
||||
struct tevent_context *ev_ctx;
|
||||
struct messaging_context *msg_ctx;
|
||||
struct dcesrv_context *dce_ctx;
|
||||
|
||||
const struct dcesrv_interface *iface;
|
||||
|
||||
uint32_t wait_time;
|
||||
};
|
||||
|
||||
NTSTATUS rpc_ep_register(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
const struct dcesrv_interface *iface)
|
||||
{
|
||||
struct rpc_ep_register_state *state;
|
||||
struct tevent_req *req;
|
||||
|
||||
/* Allocate under iface to stop the loop if the interface is
|
||||
* removed from server */
|
||||
state = talloc_zero(iface, struct rpc_ep_register_state);
|
||||
if (state == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
state->wait_time = 1;
|
||||
state->ev_ctx = ev_ctx;
|
||||
state->msg_ctx = msg_ctx;
|
||||
state->dce_ctx = dce_ctx;
|
||||
state->iface = iface;
|
||||
|
||||
req = tevent_wakeup_send(state,
|
||||
state->ev_ctx,
|
||||
timeval_current_ofs(1, 0));
|
||||
if (req == NULL) {
|
||||
talloc_free(state);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
tevent_req_set_callback(req, rpc_ep_register_loop, state);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
#define MONITOR_WAIT_TIME 30
|
||||
static void rpc_ep_monitor_loop(struct tevent_req *subreq);
|
||||
|
||||
static void rpc_ep_register_loop(struct tevent_req *subreq)
|
||||
{
|
||||
struct rpc_ep_register_state *state =
|
||||
tevent_req_callback_data(subreq, struct rpc_ep_register_state);
|
||||
NTSTATUS status;
|
||||
bool ok;
|
||||
|
||||
ok = tevent_wakeup_recv(subreq);
|
||||
TALLOC_FREE(subreq);
|
||||
if (!ok) {
|
||||
talloc_free(state);
|
||||
return;
|
||||
}
|
||||
|
||||
status = rpc_ep_try_register(state,
|
||||
state->ev_ctx,
|
||||
state->msg_ctx,
|
||||
state->dce_ctx,
|
||||
state->iface,
|
||||
&state->h);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
/* endpoint registered, monitor the connnection. */
|
||||
subreq = tevent_wakeup_send(state,
|
||||
state->ev_ctx,
|
||||
timeval_current_ofs(MONITOR_WAIT_TIME, 0));
|
||||
if (subreq == NULL) {
|
||||
talloc_free(state);
|
||||
return;
|
||||
}
|
||||
|
||||
tevent_req_set_callback(subreq, rpc_ep_monitor_loop, state);
|
||||
return;
|
||||
}
|
||||
|
||||
state->wait_time = state->wait_time * 2;
|
||||
if (state->wait_time > 16) {
|
||||
DEBUG(0, ("Failed to register endpoint '%s'!\n",
|
||||
state->iface->name));
|
||||
state->wait_time = 16;
|
||||
}
|
||||
|
||||
subreq = tevent_wakeup_send(state,
|
||||
state->ev_ctx,
|
||||
timeval_current_ofs(state->wait_time, 0));
|
||||
if (subreq == NULL) {
|
||||
talloc_free(state);
|
||||
return;
|
||||
}
|
||||
|
||||
tevent_req_set_callback(subreq, rpc_ep_register_loop, state);
|
||||
return;
|
||||
}
|
||||
|
||||
static NTSTATUS rpc_ep_try_register(TALLOC_CTX *mem_ctx,
|
||||
struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
const struct dcesrv_interface *iface,
|
||||
struct dcerpc_binding_handle **pbh)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
status = dcerpc_ep_register(mem_ctx,
|
||||
msg_ctx,
|
||||
dce_ctx,
|
||||
iface,
|
||||
&iface->syntax_id.uuid,
|
||||
iface->name,
|
||||
pbh);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Monitor the connection to the endpoint mapper and if it goes away, try to
|
||||
* register the endpoint.
|
||||
*/
|
||||
static void rpc_ep_monitor_loop(struct tevent_req *subreq)
|
||||
{
|
||||
struct rpc_ep_register_state *state =
|
||||
tevent_req_callback_data(subreq, struct rpc_ep_register_state);
|
||||
struct policy_handle entry_handle;
|
||||
struct dcerpc_binding *map_binding;
|
||||
struct epm_twr_p_t towers[10];
|
||||
struct epm_twr_t *map_tower;
|
||||
uint32_t num_towers = 0;
|
||||
struct GUID object;
|
||||
NTSTATUS status;
|
||||
uint32_t result = EPMAPPER_STATUS_CANT_PERFORM_OP;
|
||||
TALLOC_CTX *tmp_ctx;
|
||||
bool ok;
|
||||
|
||||
ZERO_STRUCT(object);
|
||||
ZERO_STRUCT(entry_handle);
|
||||
|
||||
tmp_ctx = talloc_stackframe();
|
||||
if (tmp_ctx == NULL) {
|
||||
talloc_free(state);
|
||||
return;
|
||||
}
|
||||
|
||||
ok = tevent_wakeup_recv(subreq);
|
||||
TALLOC_FREE(subreq);
|
||||
if (!ok) {
|
||||
talloc_free(tmp_ctx);
|
||||
talloc_free(state);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create map tower */
|
||||
status = dcerpc_parse_binding(tmp_ctx, "ncacn_np:", &map_binding);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
talloc_free(state);
|
||||
return;
|
||||
}
|
||||
|
||||
status = dcerpc_binding_set_abstract_syntax(map_binding,
|
||||
&state->iface->syntax_id);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
talloc_free(state);
|
||||
return;
|
||||
}
|
||||
|
||||
map_tower = talloc_zero(tmp_ctx, struct epm_twr_t);
|
||||
if (map_tower == NULL) {
|
||||
talloc_free(tmp_ctx);
|
||||
talloc_free(state);
|
||||
return;
|
||||
}
|
||||
|
||||
status = dcerpc_binding_build_tower(map_tower, map_binding,
|
||||
&map_tower->tower);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
talloc_free(state);
|
||||
return;
|
||||
}
|
||||
|
||||
ok = false;
|
||||
status = dcerpc_epm_Map(state->h,
|
||||
tmp_ctx,
|
||||
&object,
|
||||
map_tower,
|
||||
&entry_handle,
|
||||
10,
|
||||
&num_towers,
|
||||
towers,
|
||||
&result);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
ok = true;
|
||||
}
|
||||
if (result == EPMAPPER_STATUS_OK ||
|
||||
result == EPMAPPER_STATUS_NO_MORE_ENTRIES) {
|
||||
ok = true;
|
||||
}
|
||||
if (num_towers == 0) {
|
||||
ok = false;
|
||||
}
|
||||
|
||||
dcerpc_epm_LookupHandleFree(state->h,
|
||||
tmp_ctx,
|
||||
&entry_handle,
|
||||
&result);
|
||||
talloc_free(tmp_ctx);
|
||||
|
||||
subreq = tevent_wakeup_send(state,
|
||||
state->ev_ctx,
|
||||
timeval_current_ofs(MONITOR_WAIT_TIME, 0));
|
||||
if (subreq == NULL) {
|
||||
talloc_free(state);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
tevent_req_set_callback(subreq, rpc_ep_monitor_loop, state);
|
||||
} else {
|
||||
TALLOC_FREE(state->h);
|
||||
state->wait_time = 1;
|
||||
|
||||
tevent_req_set_callback(subreq, rpc_ep_register_loop, state);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
*
|
||||
* RPC Endpoint Registration
|
||||
*
|
||||
* Copyright (c) 2011 Andreas Schneider <asn@samba.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _RPC_EP_REGISTER_H
|
||||
#define _RPC_EP_REGISTER_H
|
||||
|
||||
struct dcesrv_context;
|
||||
struct dcesrv_interface;
|
||||
|
||||
/**
|
||||
* @brief Register an endpoint at the endpoint mapper.
|
||||
*
|
||||
* This just sets up a register and monitor loop to try to regsiter the
|
||||
* endpoint at the endpoint mapper.
|
||||
*
|
||||
* @param[in] ev_ctx The event context to setup the loop.
|
||||
*
|
||||
* @param[in] msg_ctx The messaging context to use for the connnection.
|
||||
*
|
||||
* @param[in] iface The interface table to register.
|
||||
*
|
||||
* @param[in] v The binding vector to register.
|
||||
*
|
||||
* @return NT_STATUS_OK on success or a corresponding error code.
|
||||
*/
|
||||
NTSTATUS rpc_ep_register(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
const struct dcesrv_interface *iface);
|
||||
|
||||
#endif /* _RPC_EP_REGISTER_H */
|
||||
|
||||
/* vim: set ts=8 sw=8 noet cindent ft=c.doxygen: */
|
@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
*
|
||||
* SMBD RPC modules
|
||||
*
|
||||
* Copyright (c) 2015 Ralph Boehme <slow@samba.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "rpc_server/rpc_modules.h"
|
||||
|
||||
#undef DBGC_CLASS
|
||||
#define DBGC_CLASS DBGC_RPC_SRV
|
||||
|
||||
static struct rpc_module *rpc_modules;
|
||||
|
||||
struct rpc_module {
|
||||
struct rpc_module *prev, *next;
|
||||
char *name;
|
||||
struct rpc_module_fns *fns;
|
||||
};
|
||||
|
||||
static struct rpc_module *find_rpc_module(const char *name)
|
||||
{
|
||||
struct rpc_module *module = NULL;
|
||||
|
||||
for (module = rpc_modules; module != NULL; module = module->next) {
|
||||
if (strequal(module->name, name)) {
|
||||
return module;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NTSTATUS register_rpc_module(struct rpc_module_fns *fns,
|
||||
const char *name)
|
||||
{
|
||||
struct rpc_module *module = find_rpc_module(name);
|
||||
|
||||
if (module != NULL) {
|
||||
DBG_ERR("RPC module %s already loaded!\n", name);
|
||||
return NT_STATUS_OBJECT_NAME_COLLISION;
|
||||
}
|
||||
|
||||
module = SMB_XMALLOC_P(struct rpc_module);
|
||||
module->name = smb_xstrdup(name);
|
||||
module->fns = fns;
|
||||
|
||||
DLIST_ADD(rpc_modules, module);
|
||||
DBG_NOTICE("Successfully added RPC module '%s'\n", name);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
bool setup_rpc_module(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
const char *name)
|
||||
{
|
||||
bool ok;
|
||||
struct rpc_module *module = find_rpc_module(name);
|
||||
|
||||
if (module == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ok = module->fns->setup(ev_ctx, msg_ctx);
|
||||
if (!ok) {
|
||||
DBG_ERR("calling setup for %s failed\n", name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setup_rpc_modules(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
bool ok;
|
||||
struct rpc_module *module = rpc_modules;
|
||||
|
||||
for (module = rpc_modules; module; module = module->next) {
|
||||
ok = module->fns->setup(ev_ctx, msg_ctx);
|
||||
if (!ok) {
|
||||
DBG_ERR("calling setup for %s failed\n", module->name);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
*
|
||||
* SMBD RPC modules
|
||||
*
|
||||
* Copyright (c) 2015 Ralph Boehme <slow@samba.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _RPC_MODULES_H
|
||||
#define _RPC_MODULES_H
|
||||
|
||||
struct rpc_module_fns {
|
||||
bool (*setup)(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx);
|
||||
};
|
||||
|
||||
NTSTATUS register_rpc_module(struct rpc_module_fns *fns,
|
||||
const char *name);
|
||||
|
||||
bool setup_rpc_modules(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx);
|
||||
|
||||
bool setup_rpc_module(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
const char *name);
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -26,7 +26,6 @@
|
||||
|
||||
#include "source3/librpc/rpc/dcerpc.h"
|
||||
|
||||
struct dcesrv_ep_entry_list;
|
||||
struct tsocket_address;
|
||||
struct pipes_struct;
|
||||
struct dcesrv_context;
|
||||
@ -46,8 +45,6 @@ struct pipes_struct {
|
||||
struct auth_session_info *session_info;
|
||||
struct messaging_context *msg_ctx;
|
||||
|
||||
struct dcesrv_ep_entry_list *ep_entries;
|
||||
|
||||
struct pipe_auth_data auth;
|
||||
|
||||
/*
|
||||
|
@ -51,157 +51,6 @@ struct dcerpc_ncacn_listen_state {
|
||||
void *termination_data;
|
||||
};
|
||||
|
||||
#if 0
|
||||
static void dcesrv_ncacn_listener(
|
||||
struct tevent_context *ev,
|
||||
struct tevent_fd *fde,
|
||||
uint16_t flags,
|
||||
void *private_data);
|
||||
|
||||
int dcesrv_setup_ncacn_listener(
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_endpoint *e,
|
||||
int *fd,
|
||||
dcerpc_ncacn_termination_fn term_fn,
|
||||
void *termination_data,
|
||||
struct dcerpc_ncacn_listen_state **listen_state)
|
||||
{
|
||||
struct dcerpc_ncacn_listen_state *state = NULL;
|
||||
struct tevent_fd *fde = NULL;
|
||||
int rc, err = ENOMEM;
|
||||
|
||||
state = talloc_zero(mem_ctx, struct dcerpc_ncacn_listen_state);
|
||||
if (state == NULL) {
|
||||
DBG_ERR("Out of memory\n");
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
state->fd = *fd;
|
||||
state->ev_ctx = ev_ctx;
|
||||
state->msg_ctx = msg_ctx;
|
||||
state->dce_ctx = dce_ctx;
|
||||
state->endpoint = e;
|
||||
state->termination_fn = term_fn;
|
||||
state->termination_data = termination_data;
|
||||
|
||||
rc = listen(state->fd, SMBD_LISTEN_BACKLOG);
|
||||
if (rc < 0) {
|
||||
err = errno;
|
||||
DBG_ERR("listen(%d) failed: %s\n",
|
||||
state->fd,
|
||||
strerror(err));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Set server socket to non-blocking for the accept. */
|
||||
rc = set_blocking(state->fd, false);
|
||||
if (rc < 0) {
|
||||
err = errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fde = tevent_add_fd(
|
||||
state->ev_ctx,
|
||||
state,
|
||||
state->fd,
|
||||
TEVENT_FD_READ,
|
||||
dcesrv_ncacn_listener,
|
||||
state);
|
||||
if (fde == NULL) {
|
||||
err = errno;
|
||||
DBG_ERR("tevent_add_fd for %d failed: %s\n",
|
||||
state->fd,
|
||||
strerror(err));
|
||||
goto fail;
|
||||
}
|
||||
tevent_fd_set_auto_close(fde);
|
||||
*fd = -1;
|
||||
|
||||
*listen_state = state;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
TALLOC_FREE(state);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void dcesrv_ncacn_listener(
|
||||
struct tevent_context *ev,
|
||||
struct tevent_fd *fde,
|
||||
uint16_t flags,
|
||||
void *private_data)
|
||||
{
|
||||
struct dcerpc_ncacn_listen_state *state = talloc_get_type_abort(
|
||||
private_data, struct dcerpc_ncacn_listen_state);
|
||||
struct tsocket_address *cli_addr = NULL, *srv_addr = NULL;
|
||||
struct samba_sockaddr addr = {
|
||||
.sa_socklen = sizeof(struct samba_sockaddr),
|
||||
};
|
||||
int sd = -1;
|
||||
int rc;
|
||||
|
||||
sd = accept(state->fd, &addr.u.sa, &addr.sa_socklen);
|
||||
if (sd == -1) {
|
||||
if (errno != EINTR) {
|
||||
DBG_ERR("Failed to accept: %s\n", strerror(errno));
|
||||
}
|
||||
return;
|
||||
}
|
||||
smb_set_close_on_exec(sd);
|
||||
|
||||
rc = tsocket_address_bsd_from_samba_sockaddr(state, &addr, &cli_addr);
|
||||
if (rc < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = getsockname(sd, &addr.u.sa, &addr.sa_socklen);
|
||||
if (rc < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = tsocket_address_bsd_from_samba_sockaddr(state, &addr, &srv_addr);
|
||||
if (rc < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dcerpc_ncacn_accept(
|
||||
state->ev_ctx,
|
||||
state->msg_ctx,
|
||||
state->dce_ctx,
|
||||
state->endpoint,
|
||||
&cli_addr,
|
||||
&srv_addr,
|
||||
sd,
|
||||
state->termination_fn,
|
||||
state->termination_data);
|
||||
return;
|
||||
|
||||
fail:
|
||||
TALLOC_FREE(cli_addr);
|
||||
TALLOC_FREE(srv_addr);
|
||||
if (sd != -1) {
|
||||
close(sd);
|
||||
}
|
||||
}
|
||||
|
||||
static int dcesrv_connection_destructor(struct dcesrv_connection *conn)
|
||||
{
|
||||
struct dcerpc_ncacn_conn *ncacn_conn = talloc_get_type_abort(
|
||||
conn->transport.private_data,
|
||||
struct dcerpc_ncacn_conn);
|
||||
|
||||
if (ncacn_conn->termination_fn != NULL) {
|
||||
ncacn_conn->termination_fn(conn, ncacn_conn->termination_data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
NTSTATUS dcerpc_ncacn_conn_init(TALLOC_CTX *mem_ctx,
|
||||
struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
@ -231,331 +80,9 @@ NTSTATUS dcerpc_ncacn_conn_init(TALLOC_CTX *mem_ctx,
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void dcesrv_ncacn_np_accept_done(struct tevent_req *subreq);
|
||||
static void dcesrv_ncacn_accept_step2(struct dcerpc_ncacn_conn *ncacn_conn);
|
||||
#endif
|
||||
|
||||
static void ncacn_terminate_connection(struct dcerpc_ncacn_conn *conn,
|
||||
const char *reason);
|
||||
|
||||
#if 0
|
||||
void dcerpc_ncacn_accept(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
struct dcesrv_endpoint *e,
|
||||
struct tsocket_address **cli_addr,
|
||||
struct tsocket_address **srv_addr,
|
||||
int s,
|
||||
dcerpc_ncacn_termination_fn termination_fn,
|
||||
void *termination_data)
|
||||
{
|
||||
enum dcerpc_transport_t transport =
|
||||
dcerpc_binding_get_transport(e->ep_description);
|
||||
struct dcerpc_ncacn_conn *ncacn_conn;
|
||||
NTSTATUS status;
|
||||
int rc;
|
||||
|
||||
DBG_DEBUG("dcerpc_ncacn_accept\n");
|
||||
|
||||
status = dcerpc_ncacn_conn_init(ev_ctx,
|
||||
ev_ctx,
|
||||
msg_ctx,
|
||||
dce_ctx,
|
||||
e,
|
||||
termination_fn,
|
||||
termination_data,
|
||||
&ncacn_conn);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to initialize dcerpc_ncacn_connection: %s\n",
|
||||
nt_errstr(status));
|
||||
close(s);
|
||||
return;
|
||||
}
|
||||
|
||||
ncacn_conn->sock = s;
|
||||
|
||||
if ((cli_addr != NULL) && (*cli_addr != NULL)) {
|
||||
ncacn_conn->remote_client_addr = talloc_move(
|
||||
ncacn_conn, cli_addr);
|
||||
|
||||
if (tsocket_address_is_inet(ncacn_conn->remote_client_addr, "ip")) {
|
||||
ncacn_conn->remote_client_name =
|
||||
tsocket_address_inet_addr_string(ncacn_conn->remote_client_addr,
|
||||
ncacn_conn);
|
||||
} else {
|
||||
ncacn_conn->remote_client_name =
|
||||
tsocket_address_unix_path(ncacn_conn->remote_client_addr,
|
||||
ncacn_conn);
|
||||
}
|
||||
|
||||
if (ncacn_conn->remote_client_name == NULL) {
|
||||
DBG_ERR("Out of memory obtaining remote socket address as a string!\n");
|
||||
ncacn_terminate_connection(ncacn_conn, "No memory");
|
||||
close(s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ((srv_addr != NULL) && (*srv_addr != NULL)) {
|
||||
ncacn_conn->local_server_addr = talloc_move(
|
||||
ncacn_conn, srv_addr);
|
||||
|
||||
if (tsocket_address_is_inet(ncacn_conn->local_server_addr, "ip")) {
|
||||
ncacn_conn->local_server_name =
|
||||
tsocket_address_inet_addr_string(ncacn_conn->local_server_addr,
|
||||
ncacn_conn);
|
||||
} else {
|
||||
ncacn_conn->local_server_name =
|
||||
tsocket_address_unix_path(ncacn_conn->local_server_addr,
|
||||
ncacn_conn);
|
||||
}
|
||||
if (ncacn_conn->local_server_name == NULL) {
|
||||
DBG_ERR("No memory\n");
|
||||
ncacn_terminate_connection(ncacn_conn, "No memory");
|
||||
close(s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
rc = set_blocking(s, false);
|
||||
if (rc < 0) {
|
||||
DBG_WARNING("Failed to set dcerpc socket to non-blocking\n");
|
||||
ncacn_terminate_connection(ncacn_conn, strerror(errno));
|
||||
close(s);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* As soon as we have tstream_bsd_existing_socket set up it will
|
||||
* take care of closing the socket.
|
||||
*/
|
||||
rc = tstream_bsd_existing_socket(ncacn_conn, s, &ncacn_conn->tstream);
|
||||
if (rc < 0) {
|
||||
DBG_WARNING("Failed to create tstream socket for dcerpc\n");
|
||||
ncacn_terminate_connection(ncacn_conn, "No memory");
|
||||
close(s);
|
||||
return;
|
||||
}
|
||||
|
||||
if (transport == NCACN_NP) {
|
||||
struct tevent_req *subreq = NULL;
|
||||
uint64_t allocation_size = 4096;
|
||||
uint16_t device_state = 0xff | 0x0400 | 0x0100;
|
||||
uint16_t file_type = FILE_TYPE_MESSAGE_MODE_PIPE;
|
||||
|
||||
subreq = tstream_npa_accept_existing_send(ncacn_conn,
|
||||
ncacn_conn->ev_ctx,
|
||||
ncacn_conn->tstream,
|
||||
file_type,
|
||||
device_state,
|
||||
allocation_size);
|
||||
if (subreq == NULL) {
|
||||
ncacn_terminate_connection(ncacn_conn, "No memory");
|
||||
return;
|
||||
}
|
||||
tevent_req_set_callback(subreq, dcesrv_ncacn_np_accept_done,
|
||||
ncacn_conn);
|
||||
return;
|
||||
}
|
||||
|
||||
dcesrv_ncacn_accept_step2(ncacn_conn);
|
||||
}
|
||||
|
||||
static void dcesrv_ncacn_np_accept_done(struct tevent_req *subreq)
|
||||
{
|
||||
struct dcerpc_ncacn_conn *ncacn_conn = tevent_req_callback_data(
|
||||
subreq, struct dcerpc_ncacn_conn);
|
||||
struct auth_session_info_transport *session_info_transport = NULL;
|
||||
enum dcerpc_transport_t transport;
|
||||
int error;
|
||||
int ret;
|
||||
|
||||
ret = tstream_npa_accept_existing_recv(subreq, &error, ncacn_conn,
|
||||
&ncacn_conn->tstream,
|
||||
NULL,
|
||||
&transport,
|
||||
&ncacn_conn->remote_client_addr,
|
||||
&ncacn_conn->remote_client_name,
|
||||
&ncacn_conn->local_server_addr,
|
||||
&ncacn_conn->local_server_name,
|
||||
&session_info_transport);
|
||||
ncacn_conn->session_info = talloc_move(ncacn_conn,
|
||||
&session_info_transport->session_info);
|
||||
|
||||
if (transport != NCACN_NP) {
|
||||
ncacn_terminate_connection(
|
||||
ncacn_conn,
|
||||
"Only allow NCACN_NP transport on named pipes\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (security_token_is_system(
|
||||
ncacn_conn->session_info->security_token)) {
|
||||
ncacn_terminate_connection(
|
||||
ncacn_conn,
|
||||
"No system token via NCACN_NP allowed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
TALLOC_FREE(subreq);
|
||||
if (ret != 0) {
|
||||
DBG_ERR("Failed to accept named pipe connection: %s\n",
|
||||
strerror(error));
|
||||
ncacn_terminate_connection(ncacn_conn, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
dcesrv_ncacn_accept_step2(ncacn_conn);
|
||||
}
|
||||
|
||||
static void dcesrv_ncacn_accept_step2(struct dcerpc_ncacn_conn *ncacn_conn)
|
||||
{
|
||||
char *pipe_name = NULL;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
int rc;
|
||||
enum dcerpc_transport_t transport = dcerpc_binding_get_transport(
|
||||
ncacn_conn->endpoint->ep_description);
|
||||
const char *endpoint = dcerpc_binding_get_string_option(
|
||||
ncacn_conn->endpoint->ep_description, "endpoint");
|
||||
struct dcesrv_connection *dcesrv_conn = NULL;
|
||||
NTSTATUS status;
|
||||
|
||||
switch (transport) {
|
||||
case NCACN_IP_TCP:
|
||||
pipe_name = tsocket_address_string(ncacn_conn->remote_client_addr,
|
||||
ncacn_conn);
|
||||
if (pipe_name == NULL) {
|
||||
DBG_ERR("No memory\n");
|
||||
ncacn_terminate_connection(ncacn_conn, "No memory");
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
case NCALRPC:
|
||||
rc = getpeereid(ncacn_conn->sock, &uid, &gid);
|
||||
if (rc < 0) {
|
||||
DEBUG(2, ("Failed to get ncalrpc connecting "
|
||||
"uid - %s!\n", strerror(errno)));
|
||||
} else {
|
||||
if (uid == sec_initial_uid()) {
|
||||
TALLOC_FREE(ncacn_conn->remote_client_addr);
|
||||
|
||||
rc = tsocket_address_unix_from_path(ncacn_conn,
|
||||
AS_SYSTEM_MAGIC_PATH_TOKEN,
|
||||
&ncacn_conn->remote_client_addr);
|
||||
if (rc < 0) {
|
||||
DBG_ERR("No memory\n");
|
||||
ncacn_terminate_connection(ncacn_conn, "No memory");
|
||||
return;
|
||||
}
|
||||
|
||||
TALLOC_FREE(ncacn_conn->remote_client_name);
|
||||
ncacn_conn->remote_client_name
|
||||
= tsocket_address_unix_path(ncacn_conn->remote_client_addr,
|
||||
ncacn_conn);
|
||||
if (ncacn_conn->remote_client_name == NULL) {
|
||||
DBG_ERR("No memory\n");
|
||||
ncacn_terminate_connection(ncacn_conn, "No memory");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FALL_THROUGH;
|
||||
case NCACN_NP:
|
||||
pipe_name = talloc_strdup(ncacn_conn, endpoint);
|
||||
if (pipe_name == NULL) {
|
||||
DBG_ERR("No memory\n");
|
||||
ncacn_terminate_connection(ncacn_conn, "No memory");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DBG_ERR("unknown dcerpc transport: %u!\n", transport);
|
||||
ncacn_terminate_connection(ncacn_conn,
|
||||
"Unknown DCE/RPC transport");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ncacn_conn->session_info == NULL) {
|
||||
status = make_session_info_anonymous(ncacn_conn,
|
||||
&ncacn_conn->session_info);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to create anonymous session info: "
|
||||
"%s\n", nt_errstr(status));
|
||||
ncacn_terminate_connection(ncacn_conn,
|
||||
nt_errstr(status));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
rc = make_base_pipes_struct(ncacn_conn,
|
||||
ncacn_conn->msg_ctx,
|
||||
pipe_name,
|
||||
transport,
|
||||
ncacn_conn->remote_client_addr,
|
||||
ncacn_conn->local_server_addr,
|
||||
&ncacn_conn->p);
|
||||
if (rc != 0) {
|
||||
const char *errstr = strerror(rc);
|
||||
DBG_ERR("Failed to create pipe struct: %s\n", errstr);
|
||||
ncacn_terminate_connection(ncacn_conn, errstr);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* This fills in dcesrv_conn->endpoint with the endpoint
|
||||
* associated with the socket. From this point on we know
|
||||
* which (group of) services we are handling, but not the
|
||||
* specific interface.
|
||||
*/
|
||||
status = dcesrv_endpoint_connect(ncacn_conn->dce_ctx,
|
||||
ncacn_conn,
|
||||
ncacn_conn->endpoint,
|
||||
ncacn_conn->session_info,
|
||||
ncacn_conn->ev_ctx,
|
||||
DCESRV_CALL_STATE_FLAG_MAY_ASYNC,
|
||||
&dcesrv_conn);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to connect to endpoint: %s\n",
|
||||
nt_errstr(status));
|
||||
ncacn_terminate_connection(ncacn_conn, nt_errstr(status));
|
||||
return;
|
||||
}
|
||||
talloc_set_destructor(dcesrv_conn, dcesrv_connection_destructor);
|
||||
|
||||
dcesrv_conn->transport.private_data = ncacn_conn;
|
||||
dcesrv_conn->transport.report_output_data =
|
||||
dcesrv_sock_report_output_data;
|
||||
dcesrv_conn->transport.terminate_connection =
|
||||
dcesrv_transport_terminate_connection;
|
||||
dcesrv_conn->send_queue = tevent_queue_create(dcesrv_conn,
|
||||
"dcesrv send queue");
|
||||
if (dcesrv_conn->send_queue == NULL) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
DBG_ERR("Failed to create send queue: %s\n",
|
||||
nt_errstr(status));
|
||||
ncacn_terminate_connection(ncacn_conn, nt_errstr(status));
|
||||
return;
|
||||
}
|
||||
|
||||
dcesrv_conn->stream = talloc_move(dcesrv_conn, &ncacn_conn->tstream);
|
||||
dcesrv_conn->local_address = ncacn_conn->local_server_addr;
|
||||
dcesrv_conn->remote_address = ncacn_conn->remote_client_addr;
|
||||
status = dcesrv_connection_loop_start(dcesrv_conn);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to start dcesrv_connection loop: %s\n",
|
||||
nt_errstr(status));
|
||||
ncacn_terminate_connection(ncacn_conn, nt_errstr(status));
|
||||
}
|
||||
DBG_DEBUG("dcerpc_ncacn_accept done\n");
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
NTSTATUS dcesrv_auth_gensec_prepare(
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct dcesrv_call_state *call,
|
||||
|
@ -1,941 +0,0 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
*
|
||||
* SMBD RPC service callbacks
|
||||
*
|
||||
* Copyright (c) 2011 Andreas Schneider <asn@samba.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ntdomain.h"
|
||||
|
||||
#include "librpc/gen_ndr/ndr_winreg_scompat.h"
|
||||
#include "librpc/gen_ndr/ndr_srvsvc_scompat.h"
|
||||
#include "librpc/gen_ndr/ndr_lsa_scompat.h"
|
||||
#include "librpc/gen_ndr/ndr_samr_scompat.h"
|
||||
#include "librpc/gen_ndr/ndr_netlogon_scompat.h"
|
||||
#include "librpc/gen_ndr/ndr_dfs_scompat.h"
|
||||
#include "librpc/gen_ndr/ndr_echo_scompat.h"
|
||||
#include "librpc/gen_ndr/ndr_dssetup_scompat.h"
|
||||
#include "librpc/gen_ndr/ndr_wkssvc_scompat.h"
|
||||
#include "librpc/gen_ndr/ndr_spoolss_scompat.h"
|
||||
#include "librpc/gen_ndr/ndr_svcctl_scompat.h"
|
||||
#include "librpc/gen_ndr/ndr_ntsvcs_scompat.h"
|
||||
#include "librpc/gen_ndr/ndr_eventlog_scompat.h"
|
||||
#include "librpc/gen_ndr/ndr_initshutdown_scompat.h"
|
||||
|
||||
#include "printing/nt_printing_migrate_internal.h"
|
||||
#include "rpc_server/eventlog/srv_eventlog_reg.h"
|
||||
#include "rpc_server/svcctl/srv_svcctl_reg.h"
|
||||
#include "rpc_server/spoolss/srv_spoolss_nt.h"
|
||||
#include "rpc_server/svcctl/srv_svcctl_nt.h"
|
||||
|
||||
#include "lib/server_prefork.h"
|
||||
#include "librpc/rpc/dcesrv_core.h"
|
||||
#include "librpc/rpc/dcerpc_ep.h"
|
||||
#include "rpc_server/rpc_sock_helper.h"
|
||||
#include "rpc_server/rpc_service_setup.h"
|
||||
#include "rpc_server/rpc_ep_register.h"
|
||||
#include "rpc_server/rpc_server.h"
|
||||
#include "rpc_server/rpc_config.h"
|
||||
#include "rpc_server/rpc_modules.h"
|
||||
|
||||
#undef DBGC_CLASS
|
||||
#define DBGC_CLASS DBGC_RPC_SRV
|
||||
|
||||
static_decl_rpc;
|
||||
|
||||
/* Common routine for embedded RPC servers */
|
||||
NTSTATUS rpc_setup_embedded(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
const struct dcesrv_interface *iface)
|
||||
{
|
||||
enum rpc_service_mode_e epm_mode = rpc_epmapper_mode();
|
||||
NTSTATUS status;
|
||||
|
||||
/* Registration of ncacn_np services is problematic. The
|
||||
* ev_ctx passed in here is passed down to all children of the
|
||||
* smbd process, and if the end point mapper ever goes away,
|
||||
* they will all attempt to re-register. But we want to test
|
||||
* the code for now, so it is enabled in on environment in
|
||||
* make test */
|
||||
if (epm_mode != RPC_SERVICE_MODE_DISABLED &&
|
||||
(lp_parm_bool(-1, "rpc_server", "register_embedded_np", false))) {
|
||||
status = rpc_ep_register(ev_ctx, msg_ctx, dce_ctx, iface);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS dcesrv_create_endpoint_sockets(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_endpoint *e,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
size_t *pnum_fds,
|
||||
int **pfds)
|
||||
{
|
||||
struct dcerpc_binding *b = e->ep_description;
|
||||
char *binding = NULL;
|
||||
int *fds = NULL;
|
||||
size_t num_fds;
|
||||
NTSTATUS status;
|
||||
|
||||
binding = dcerpc_binding_string(mem_ctx, b);
|
||||
if (binding == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
DBG_DEBUG("Creating endpoint '%s'\n", binding);
|
||||
TALLOC_FREE(binding);
|
||||
|
||||
status = dcesrv_create_binding_sockets(b, mem_ctx, &num_fds, &fds);
|
||||
|
||||
/* Build binding string again as the endpoint may have changed by
|
||||
* dcesrv_create_<transport>_socket functions */
|
||||
binding = dcerpc_binding_string(mem_ctx, b);
|
||||
if (binding == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
struct dcesrv_if_list *iface = NULL;
|
||||
DBG_ERR("Failed to create '%s' sockets for ", binding);
|
||||
for (iface = e->interface_list; iface; iface = iface->next) {
|
||||
DEBUGADD(DBGLVL_ERR, ("'%s' ", iface->iface->name));
|
||||
}
|
||||
DEBUGADD(DBGLVL_ERR, (": %s\n", nt_errstr(status)));
|
||||
return status;
|
||||
} else {
|
||||
struct dcesrv_if_list *iface = NULL;
|
||||
DBG_INFO("Successfully listening on '%s' for ", binding);
|
||||
for (iface = e->interface_list; iface; iface = iface->next) {
|
||||
DEBUGADD(DBGLVL_INFO, ("'%s' ", iface->iface->name));
|
||||
}
|
||||
DEBUGADD(DBGLVL_INFO, ("\n"));
|
||||
}
|
||||
|
||||
TALLOC_FREE(binding);
|
||||
|
||||
*pnum_fds = num_fds;
|
||||
*pfds = fds;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
NTSTATUS dcesrv_create_endpoint_list_pf_listen_fds(
|
||||
struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
struct dcesrv_endpoint *e,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
size_t *pnum_fds,
|
||||
struct pf_listen_fd **pfds)
|
||||
{
|
||||
struct pf_listen_fd *fds = NULL;
|
||||
size_t num_fds = 0;
|
||||
NTSTATUS status;
|
||||
|
||||
for (; e != NULL; e = e->next) {
|
||||
int *ep_fds = NULL;
|
||||
struct pf_listen_fd *tmp = NULL;
|
||||
size_t i, num_ep_fds;
|
||||
|
||||
status = dcesrv_create_endpoint_sockets(
|
||||
ev_ctx,
|
||||
msg_ctx,
|
||||
e,
|
||||
mem_ctx,
|
||||
&num_ep_fds,
|
||||
&ep_fds);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
char *ep_string = dcerpc_binding_string(
|
||||
dce_ctx, e->ep_description);
|
||||
DBG_ERR("Failed to create endpoint '%s': %s\n",
|
||||
ep_string, nt_errstr(status));
|
||||
TALLOC_FREE(ep_string);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (num_fds + num_ep_fds < num_fds) {
|
||||
/* overflow */
|
||||
status = NT_STATUS_INTEGER_OVERFLOW;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
tmp = talloc_realloc(
|
||||
mem_ctx,
|
||||
fds,
|
||||
struct pf_listen_fd,
|
||||
num_fds + num_ep_fds);
|
||||
if (tmp == NULL) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto fail;
|
||||
}
|
||||
fds = tmp;
|
||||
|
||||
for (i=0; i<num_ep_fds; i++) {
|
||||
fds[num_fds].fd = ep_fds[i];
|
||||
fds[num_fds].fd_data = e;
|
||||
num_fds += 1;
|
||||
}
|
||||
|
||||
TALLOC_FREE(ep_fds);
|
||||
}
|
||||
|
||||
*pnum_fds = num_fds;
|
||||
*pfds = fds;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
fail:
|
||||
{
|
||||
size_t i;
|
||||
for (i=0; i<num_fds; i++) {
|
||||
close(fds[i].fd);
|
||||
}
|
||||
}
|
||||
|
||||
TALLOC_FREE(fds);
|
||||
return status;
|
||||
}
|
||||
|
||||
NTSTATUS dcesrv_setup_endpoint_sockets(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
struct dcesrv_endpoint *e,
|
||||
dcerpc_ncacn_termination_fn term_fn,
|
||||
void *term_data)
|
||||
{
|
||||
TALLOC_CTX *frame = talloc_stackframe();
|
||||
struct dcerpc_binding *b = e->ep_description;
|
||||
char *binding = NULL;
|
||||
NTSTATUS status = NT_STATUS_NO_MEMORY;
|
||||
struct dcesrv_if_list *iface = NULL;
|
||||
int *fds = NULL;
|
||||
size_t i, num_fds = 0;
|
||||
struct dcerpc_ncacn_listen_state **listen_states = NULL;
|
||||
|
||||
binding = dcerpc_binding_string(frame, b);
|
||||
if (binding == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DBG_DEBUG("Setting up endpoint '%s'\n", binding);
|
||||
TALLOC_FREE(binding);
|
||||
|
||||
status = dcesrv_create_binding_sockets(b, frame, &num_fds, &fds);
|
||||
|
||||
/* Build binding string again as the endpoint may have changed by
|
||||
* dcesrv_create_<transport>_socket functions */
|
||||
binding = dcerpc_binding_string(frame, b);
|
||||
if (binding == NULL) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to setup '%s' sockets for ", binding);
|
||||
for (iface = e->interface_list; iface; iface = iface->next) {
|
||||
DEBUGADD(DBGLVL_ERR, ("'%s' ", iface->iface->name));
|
||||
}
|
||||
DEBUGADD(DBGLVL_ERR, (": %s\n", nt_errstr(status)));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
listen_states = talloc_array(
|
||||
frame, struct dcerpc_ncacn_listen_state *, num_fds);
|
||||
if (listen_states == NULL) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i=0; i<num_fds; i++) {
|
||||
int ret = dcesrv_setup_ncacn_listener(
|
||||
listen_states,
|
||||
dce_ctx,
|
||||
ev_ctx,
|
||||
msg_ctx,
|
||||
e,
|
||||
&fds[i],
|
||||
term_fn,
|
||||
term_data,
|
||||
&listen_states[i]);
|
||||
if (ret != 0) {
|
||||
DBG_ERR("dcesrv_setup_ncacn_listener failed for "
|
||||
"socket %d: %s\n",
|
||||
fds[i],
|
||||
strerror(ret));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < num_fds) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i=0; i<num_fds; i++) {
|
||||
/*
|
||||
* Make the listener states including the tevent_fd's
|
||||
* talloc children of the endpoint. If the endpoint is
|
||||
* freed (for example when forked daemons reinit) the
|
||||
* dcesrv_context, the tevent_fd listener will be
|
||||
* stopped and the socket closed.
|
||||
*
|
||||
* Do this in a loop separate from the one doing the
|
||||
* dcesrv_setup_ncacn_listener() that can't fail
|
||||
* anymore.
|
||||
*/
|
||||
talloc_move(e, &listen_states[i]);
|
||||
}
|
||||
|
||||
DBG_INFO("Successfully listening on '%s' for ", binding);
|
||||
for (iface = e->interface_list; iface; iface = iface->next) {
|
||||
DEBUGADD(DBGLVL_INFO, ("'%s' ", iface->iface->name));
|
||||
}
|
||||
DEBUGADD(DBGLVL_INFO, ("\n"));
|
||||
|
||||
TALLOC_FREE(frame);
|
||||
return NT_STATUS_OK;
|
||||
|
||||
fail:
|
||||
for (i=0; i<num_fds; i++) {
|
||||
if (fds[i] != -1) {
|
||||
close(fds[i]);
|
||||
}
|
||||
}
|
||||
TALLOC_FREE(frame);
|
||||
return status;
|
||||
}
|
||||
|
||||
static NTSTATUS dcesrv_init_endpoints(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx)
|
||||
{
|
||||
struct dcesrv_endpoint *e = NULL;
|
||||
NTSTATUS status;
|
||||
|
||||
for (e = dce_ctx->endpoint_list; e; e = e->next) {
|
||||
enum dcerpc_transport_t transport =
|
||||
dcerpc_binding_get_transport(e->ep_description);
|
||||
|
||||
if (transport == NCACN_HTTP) {
|
||||
/*
|
||||
* We don't support ncacn_http yet
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
|
||||
status = dcesrv_setup_endpoint_sockets(ev_ctx,
|
||||
msg_ctx,
|
||||
dce_ctx,
|
||||
e,
|
||||
NULL,
|
||||
NULL);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Register only NCACN_NP for embedded services */
|
||||
if (transport == NCACN_NP) {
|
||||
struct dcesrv_if_list *ifl = NULL;
|
||||
for (ifl = e->interface_list; ifl; ifl = ifl->next) {
|
||||
status = rpc_setup_embedded(ev_ctx,
|
||||
msg_ctx,
|
||||
dce_ctx,
|
||||
ifl->iface);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register embedded "
|
||||
"interface in endpoint mapper "
|
||||
": %s", nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS rpc_setup_winreg(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
NTSTATUS status;
|
||||
enum rpc_service_mode_e service_mode;
|
||||
const struct dcesrv_endpoint_server *ep_server = NULL;
|
||||
|
||||
/* Register the endpoint server in DCERPC core */
|
||||
ep_server = winreg_get_ep_server();
|
||||
if (ep_server == NULL) {
|
||||
DBG_ERR("Failed to get 'winreg' endpoint server\n");
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
service_mode = rpc_service_mode(ep_server->name);
|
||||
if (service_mode != RPC_SERVICE_MODE_EMBEDDED) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
status = dcerpc_register_ep_server(ep_server);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register '%s' endpoint server: "
|
||||
"%s\n", ep_server->name, nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS rpc_setup_srvsvc(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
NTSTATUS status;
|
||||
enum rpc_service_mode_e service_mode;
|
||||
const struct dcesrv_endpoint_server *ep_server = NULL;
|
||||
|
||||
/* Register the endpoint server in DCERPC core */
|
||||
ep_server = srvsvc_get_ep_server();
|
||||
if (ep_server == NULL) {
|
||||
DBG_ERR("Failed to get 'srvsvc' endpoint server\n");
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
service_mode = rpc_service_mode(ep_server->name);
|
||||
if (service_mode != RPC_SERVICE_MODE_EMBEDDED) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
status = dcerpc_register_ep_server(ep_server);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register '%s' endpoint server: "
|
||||
"%s\n", ep_server->name, nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS rpc_setup_lsarpc(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
enum rpc_daemon_type_e lsasd_type = rpc_lsasd_daemon();
|
||||
NTSTATUS status;
|
||||
enum rpc_service_mode_e service_mode;
|
||||
const struct dcesrv_endpoint_server *ep_server = NULL;
|
||||
|
||||
/* Register the endpoint server in DCERPC core */
|
||||
ep_server = lsarpc_get_ep_server();
|
||||
if (ep_server == NULL) {
|
||||
DBG_ERR("Failed to get 'lsarpc' endpoint server\n");
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
service_mode = rpc_service_mode(ep_server->name);
|
||||
if (service_mode != RPC_SERVICE_MODE_EMBEDDED ||
|
||||
lsasd_type != RPC_DAEMON_EMBEDDED) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
status = dcerpc_register_ep_server(ep_server);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register '%s' endpoint server: "
|
||||
"%s\n", ep_server->name, nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS rpc_setup_samr(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
enum rpc_daemon_type_e lsasd_type = rpc_lsasd_daemon();
|
||||
NTSTATUS status;
|
||||
enum rpc_service_mode_e service_mode;
|
||||
const struct dcesrv_endpoint_server *ep_server = NULL;
|
||||
|
||||
/* Register the endpoint server in DCERPC core */
|
||||
ep_server = samr_get_ep_server();
|
||||
if (ep_server == NULL) {
|
||||
DBG_ERR("Failed to get 'samr' endpoint server\n");
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
service_mode = rpc_service_mode(ep_server->name);
|
||||
if (service_mode != RPC_SERVICE_MODE_EMBEDDED ||
|
||||
lsasd_type != RPC_DAEMON_EMBEDDED) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
status = dcerpc_register_ep_server(ep_server);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register '%s' endpoint server: "
|
||||
"%s\n", ep_server->name, nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS rpc_setup_netlogon(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
enum rpc_daemon_type_e lsasd_type = rpc_lsasd_daemon();
|
||||
NTSTATUS status;
|
||||
enum rpc_service_mode_e service_mode;
|
||||
const struct dcesrv_endpoint_server *ep_server = NULL;
|
||||
|
||||
/* Register the endpoint server in DCERPC core */
|
||||
ep_server = netlogon_get_ep_server();
|
||||
if (ep_server == NULL) {
|
||||
DBG_ERR("Failed to get 'netlogon' endpoint server\n");
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
service_mode = rpc_service_mode(ep_server->name);
|
||||
if (service_mode != RPC_SERVICE_MODE_EMBEDDED ||
|
||||
lsasd_type != RPC_DAEMON_EMBEDDED) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
status = dcerpc_register_ep_server(ep_server);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register '%s' endpoint server: "
|
||||
"%s\n", ep_server->name, nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS rpc_setup_netdfs(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
NTSTATUS status;
|
||||
enum rpc_service_mode_e service_mode;
|
||||
const struct dcesrv_endpoint_server *ep_server = NULL;
|
||||
|
||||
/* Register the endpoint server in DCERPC core */
|
||||
ep_server = netdfs_get_ep_server();
|
||||
if (ep_server == NULL) {
|
||||
DBG_ERR("Failed to get 'netdfs' endpoint server\n");
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
service_mode = rpc_service_mode(ep_server->name);
|
||||
if (service_mode != RPC_SERVICE_MODE_EMBEDDED) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
status = dcerpc_register_ep_server(ep_server);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register '%s' endpoint server: "
|
||||
"%s\n", ep_server->name, nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
#ifdef DEVELOPER
|
||||
static NTSTATUS rpc_setup_rpcecho(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
NTSTATUS status;
|
||||
enum rpc_service_mode_e service_mode;
|
||||
const struct dcesrv_endpoint_server *ep_server = NULL;
|
||||
|
||||
/* Register the endpoint server in DCERPC core */
|
||||
ep_server = rpcecho_get_ep_server();
|
||||
if (ep_server == NULL) {
|
||||
DBG_ERR("Failed to get 'rpcecho' endpoint server\n");
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
service_mode = rpc_service_mode(ep_server->name);
|
||||
if (service_mode != RPC_SERVICE_MODE_EMBEDDED) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
status = dcerpc_register_ep_server(ep_server);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register '%s' endpoint server: "
|
||||
"%s\n", ep_server->name, nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
static NTSTATUS rpc_setup_dssetup(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
NTSTATUS status;
|
||||
enum rpc_service_mode_e service_mode;
|
||||
const struct dcesrv_endpoint_server *ep_server = NULL;
|
||||
|
||||
/* Register the endpoint server in DCERPC core */
|
||||
ep_server = dssetup_get_ep_server();
|
||||
if (ep_server == NULL) {
|
||||
DBG_ERR("Failed to get 'dssetup' endpoint server\n");
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
service_mode = rpc_service_mode(ep_server->name);
|
||||
if (service_mode != RPC_SERVICE_MODE_EMBEDDED) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
status = dcerpc_register_ep_server(ep_server);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register '%s' endpoint server: "
|
||||
"%s\n", ep_server->name, nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS rpc_setup_wkssvc(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
NTSTATUS status;
|
||||
enum rpc_service_mode_e service_mode;
|
||||
const struct dcesrv_endpoint_server *ep_server = NULL;
|
||||
|
||||
/* Register the endpoint server in DCERPC core */
|
||||
ep_server = wkssvc_get_ep_server();
|
||||
if (ep_server == NULL) {
|
||||
DBG_ERR("Failed to get 'wkssvc' endpoint server\n");
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
service_mode = rpc_service_mode(ep_server->name);
|
||||
if (service_mode != RPC_SERVICE_MODE_EMBEDDED) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
status = dcerpc_register_ep_server(ep_server);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register '%s' endpoint server: "
|
||||
"%s\n", ep_server->name, nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS rpc_setup_spoolss(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
enum rpc_daemon_type_e spoolss_type = rpc_spoolss_daemon();
|
||||
NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
|
||||
enum rpc_service_mode_e service_mode;
|
||||
const struct dcesrv_endpoint_server *ep_server = NULL;
|
||||
|
||||
if (lp__disable_spoolss()) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* Register the endpoint server in DCERPC core */
|
||||
ep_server = spoolss_get_ep_server();
|
||||
if (ep_server == NULL) {
|
||||
DBG_ERR("Failed to get 'spoolss' endpoint server\n");
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
service_mode = rpc_service_mode(ep_server->name);
|
||||
if (service_mode != RPC_SERVICE_MODE_EMBEDDED ||
|
||||
spoolss_type != RPC_DAEMON_EMBEDDED) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
status = dcerpc_register_ep_server(ep_server);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register '%s' endpoint server"
|
||||
": %s\n", ep_server->name, nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS rpc_setup_svcctl(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
NTSTATUS status;
|
||||
enum rpc_service_mode_e service_mode;
|
||||
const struct dcesrv_endpoint_server *ep_server = NULL;
|
||||
|
||||
/* Register the endpoint server in DCERPC core */
|
||||
ep_server = svcctl_get_ep_server();
|
||||
if (ep_server == NULL) {
|
||||
DBG_ERR("Failed to get 'svcctl' endpoint server\n");
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
service_mode = rpc_service_mode(ep_server->name);
|
||||
if (service_mode != RPC_SERVICE_MODE_EMBEDDED) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
status = dcerpc_register_ep_server(ep_server);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register '%s' endpoint server"
|
||||
": %s\n", ep_server->name, nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS rpc_setup_ntsvcs(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
NTSTATUS status;
|
||||
enum rpc_service_mode_e service_mode;
|
||||
const struct dcesrv_endpoint_server *ep_server = NULL;
|
||||
|
||||
/* Register the endpoint server in DCERPC core */
|
||||
ep_server = ntsvcs_get_ep_server();
|
||||
if (ep_server == NULL) {
|
||||
DBG_ERR("Failed to get 'ntsvcs' endpoint server\n");
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
service_mode = rpc_service_mode(ep_server->name);
|
||||
if (service_mode != RPC_SERVICE_MODE_EMBEDDED) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
status = dcerpc_register_ep_server(ep_server);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register '%s' endpoint server"
|
||||
": %s\n", ep_server->name, nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS rpc_setup_eventlog(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
NTSTATUS status;
|
||||
enum rpc_service_mode_e service_mode;
|
||||
const struct dcesrv_endpoint_server *ep_server = NULL;
|
||||
|
||||
/* Register the endpoint server in DCERPC core */
|
||||
ep_server = eventlog_get_ep_server();
|
||||
if (ep_server == NULL) {
|
||||
DBG_ERR("Failed to get 'eventlog' endpoint server\n");
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
service_mode = rpc_service_mode(ep_server->name);
|
||||
if (service_mode != RPC_SERVICE_MODE_EMBEDDED) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
status = dcerpc_register_ep_server(ep_server);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register '%s' endpoint server"
|
||||
": %s\n", ep_server->name, nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS rpc_setup_initshutdown(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx)
|
||||
{
|
||||
NTSTATUS status;
|
||||
enum rpc_service_mode_e service_mode;
|
||||
const struct dcesrv_endpoint_server *ep_server = NULL;
|
||||
|
||||
/* Register the endpoint server in DCERPC core */
|
||||
ep_server = initshutdown_get_ep_server();
|
||||
if (ep_server == NULL) {
|
||||
DBG_ERR("Failed to get 'initshutdown' endpoint server\n");
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
service_mode = rpc_service_mode(ep_server->name);
|
||||
if (service_mode != RPC_SERVICE_MODE_EMBEDDED) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
status = dcerpc_register_ep_server(ep_server);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to register '%s' endpoint "
|
||||
"server: %s\n", ep_server->name, nt_errstr(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS dcesrv_init(TALLOC_CTX *mem_ctx,
|
||||
struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx)
|
||||
{
|
||||
TALLOC_CTX *tmp_ctx;
|
||||
bool ok;
|
||||
init_module_fn *mod_init_fns = NULL;
|
||||
NTSTATUS status;
|
||||
|
||||
tmp_ctx = talloc_stackframe();
|
||||
if (tmp_ctx == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
DBG_INFO("Registering DCE/RPC endpoint servers\n");
|
||||
|
||||
status = rpc_setup_winreg(ev_ctx, msg_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = rpc_setup_srvsvc(ev_ctx, msg_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = rpc_setup_lsarpc(ev_ctx, msg_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = rpc_setup_samr(ev_ctx, msg_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = rpc_setup_netlogon(ev_ctx, msg_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = rpc_setup_netdfs(ev_ctx, msg_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
#ifdef DEVELOPER
|
||||
status = rpc_setup_rpcecho(ev_ctx, msg_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
|
||||
status = rpc_setup_dssetup(ev_ctx, msg_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = rpc_setup_wkssvc(ev_ctx, msg_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = rpc_setup_spoolss(ev_ctx, msg_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = rpc_setup_svcctl(ev_ctx, msg_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = rpc_setup_ntsvcs(ev_ctx, msg_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = rpc_setup_eventlog(ev_ctx, msg_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = rpc_setup_initshutdown(ev_ctx, msg_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
DBG_INFO("Initializing DCE/RPC modules\n");
|
||||
|
||||
/* Initialize static subsystems */
|
||||
static_init_rpc(NULL);
|
||||
|
||||
/* Initialize shared modules */
|
||||
mod_init_fns = load_samba_modules(tmp_ctx, "rpc");
|
||||
if ((mod_init_fns == NULL) && (errno != ENOENT)) {
|
||||
/*
|
||||
* ENOENT means the directory doesn't exist which can happen if
|
||||
* all modules are static. So ENOENT is ok, everything else is
|
||||
* not ok.
|
||||
*/
|
||||
DBG_ERR("Loading shared DCE/RPC modules failed [%s]\n",
|
||||
strerror(errno));
|
||||
status = NT_STATUS_UNSUCCESSFUL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ok = run_init_functions(NULL, mod_init_fns);
|
||||
if (!ok) {
|
||||
DBG_ERR("Initializing shared DCE/RPC modules failed\n");
|
||||
status = NT_STATUS_UNSUCCESSFUL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* The RPC module setup function has to register the endpoint server */
|
||||
ok = setup_rpc_modules(ev_ctx, msg_ctx);
|
||||
if (!ok) {
|
||||
DBG_ERR("Shared DCE/RPC modules setup failed\n");
|
||||
status = NT_STATUS_UNSUCCESSFUL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
DBG_INFO("Initializing DCE/RPC registered endpoint servers\n");
|
||||
|
||||
status = dcesrv_init_registered_ep_servers(dce_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to init DCE/RPC endpoint servers: %s\n",
|
||||
nt_errstr(status));
|
||||
goto done;
|
||||
}
|
||||
|
||||
DBG_INFO("Initializing DCE/RPC connection endpoints\n");
|
||||
|
||||
status = dcesrv_init_endpoints(ev_ctx, msg_ctx, dce_ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_ERR("Failed to init DCE/RPC endpoints: %s\n",
|
||||
nt_errstr(status));
|
||||
goto done;
|
||||
}
|
||||
|
||||
status = NT_STATUS_OK;
|
||||
done:
|
||||
talloc_free(tmp_ctx);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* vim: set ts=8 sw=8 noet cindent ft=c.doxygen: */
|
@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
*
|
||||
* SMBD RPC service callbacks
|
||||
*
|
||||
* Copyright (c) 2011 Andreas Schneider <asn@samba.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _RPC_EP_SETUP_H
|
||||
#define _RPC_EP_SETUP_H
|
||||
|
||||
#include "rpc_server/rpc_server.h"
|
||||
|
||||
struct pf_listen_fd;
|
||||
|
||||
NTSTATUS dcesrv_init(TALLOC_CTX *mem_ctx,
|
||||
struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx);
|
||||
|
||||
NTSTATUS dcesrv_setup_endpoint_sockets(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
struct dcesrv_endpoint *e,
|
||||
dcerpc_ncacn_termination_fn term_fn,
|
||||
void *term_data);
|
||||
|
||||
NTSTATUS dcesrv_create_endpoint_sockets(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_endpoint *e,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
size_t *pnum_fds,
|
||||
int **pfds);
|
||||
NTSTATUS dcesrv_create_endpoint_list_pf_listen_fds(
|
||||
struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
struct dcesrv_endpoint *e,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
size_t *pnum_fds,
|
||||
struct pf_listen_fd **pfds);
|
||||
|
||||
NTSTATUS rpc_setup_embedded(struct tevent_context *ev_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
struct dcesrv_context *dce_ctx,
|
||||
const struct dcesrv_interface *iface);
|
||||
|
||||
#endif /* _RPC_EP_SETUP_H */
|
||||
|
||||
/* vim: set ts=8 sw=8 noet cindent ft=c.doxygen: */
|
@ -25,7 +25,6 @@
|
||||
#include "../lib/tsocket/tsocket.h"
|
||||
#include "librpc/rpc/dcesrv_core.h"
|
||||
#include "rpc_server/rpc_sock_helper.h"
|
||||
#include "lib/server_prefork.h"
|
||||
#include "librpc/ndr/ndr_table.h"
|
||||
|
||||
#undef DBGC_CLASS
|
||||
|
@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
* RPC Pipe client / server routines
|
||||
* Almost completely rewritten by (C) Jeremy Allison 2005 - 2010
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* this module apparently provides an implementation of DCE/RPC over a
|
||||
* named pipe (IPC$ connection using SMBtrans). details of DCE/RPC
|
||||
* documentation are available (in on-line form) from the X-Open group.
|
||||
*
|
||||
* this module should provide a level of abstraction between SMB
|
||||
* and DCE/RPC, while minimising the amount of mallocs, unnecessary
|
||||
* data copies, and network traffic.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "rpc_server.h"
|
||||
#include "rpc_server/srv_pipe.h"
|
||||
|
||||
#undef DBGC_CLASS
|
||||
#define DBGC_CLASS DBGC_RPC_SRV
|
||||
|
||||
/**
|
||||
* Is a named pipe known?
|
||||
* @param[in] dce_ctx The rpc server context
|
||||
* @param[in] pipename Just the filename
|
||||
* @param[out] endpoint The DCERPC endpoint serving the pipe name
|
||||
* @result NT error code
|
||||
*/
|
||||
NTSTATUS is_known_pipename(struct dcesrv_context *dce_ctx,
|
||||
const char *pipename,
|
||||
struct dcesrv_endpoint **ep)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
if (strchr(pipename, '/')) {
|
||||
DBG_WARNING("Refusing open on pipe %s\n", pipename);
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (lp_disable_spoolss() && strequal(pipename, "spoolss")) {
|
||||
DBG_DEBUG("refusing spoolss access\n");
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
status = dcesrv_endpoint_by_ncacn_np_name(dce_ctx, pipename, ep);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
status = smb_probe_module("rpc", pipename);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DBG_DEBUG("Unknown pipe '%s'\n", pipename);
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
DBG_DEBUG("'%s' loaded dynamically\n", pipename);
|
||||
|
||||
/*
|
||||
* Scan the list again for the interface id
|
||||
*/
|
||||
status = dcesrv_endpoint_by_ncacn_np_name(dce_ctx, pipename, ep);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
DBG_DEBUG("pipe %s did not register itself!\n", pipename);
|
||||
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
* RPC Pipe client / server routines
|
||||
* Almost completely rewritten by (C) Jeremy Allison 2005 - 2010
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _RPC_SERVER_SRV_PIPE_H_
|
||||
#define _RPC_SERVER_SRV_PIPE_H_
|
||||
|
||||
struct dcesrv_context;
|
||||
struct dcesrv_endpoint;
|
||||
|
||||
/* The following definitions come from rpc_server/srv_pipe.c */
|
||||
|
||||
NTSTATUS is_known_pipename(struct dcesrv_context *dce_ctx,
|
||||
const char *pipename,
|
||||
struct dcesrv_endpoint **ep);
|
||||
|
||||
#endif /* _RPC_SERVER_SRV_PIPE_H_ */
|
@ -378,8 +378,6 @@ bld.SAMBA3_SUBSYSTEM('samba3core',
|
||||
lib/gencache.c
|
||||
lib/util_event.c
|
||||
lib/global_contexts.c
|
||||
lib/server_prefork.c
|
||||
lib/server_prefork_util.c
|
||||
lib/ldap_escape.c
|
||||
lib/system_smbd.c
|
||||
lib/audit.c
|
||||
|
Loading…
x
Reference in New Issue
Block a user