monitor.c revision f52c3c6a93f673ba422f5eee1788e2f5b70b3a6a
/*
SSSD
Service monitor
Copyright (C) Simo Sorce 2008
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/>.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include "popt.h"
#include "tevent.h"
#include "monitor.h"
#include "sbus/sssd_dbus.h"
#include "sbus_interfaces.h"
/* ping time cannot be less then once every few seconds or the
* monitor will get crazy hammering children with messages */
#define MONITOR_DEF_PING_TIME 10
struct mt_conn {
struct sbus_conn_ctx *conn_ctx;
};
struct mt_svc {
char *command;
char *name;
int ping_time;
int restarts;
};
struct mt_ctx {
struct event_context *ev;
struct confdb_ctx *cdb;
char **services;
struct sbus_srv_ctx *sbus_srv;
int service_id_timeout;
};
/* dbus_get_monitor_version
* Return the monitor version over D-BUS */
void *data,
DBusMessage **r)
{
const char *version = MONITOR_VERSION;
if (!ret) {
return EIO;
}
*r = reply;
return EOK;
}
struct sbus_method monitor_methods[] = {
};
/* monitor_dbus_init
* Set up the monitor service as a D-BUS Server */
{
struct sbus_method_ctx *sd_ctx;
struct sbus_srv_ctx *sbus_srv;
char *sbus_address;
char *default_monitor_address;
int ret;
if (!default_monitor_address) {
return ENOMEM;
}
return ret;
}
if (!sd_ctx) {
return ENOMEM;
}
/* Set up globally-available D-BUS methods */
return ENOMEM;
}
return ENOMEM;
}
return ret;
}
struct timed_event *te,
{
bool process_alive = true;
int ret;
switch (ret) {
case EOK:
/* all fine */
break;
case ECHILD:
process_alive = false;
break;
default:
/* TODO: should we tear down it ? */
break;
}
if (process_alive) {
switch (ret) {
case EOK:
/* all fine */
break;
case ENXIO:
break;
default:
/* TODO: should we tear it down ? */
break;
}
/* too long since we last heard of this process */
DEBUG(0,("Sending signal to child (%s:%d) failed! "
"Ignore and pretend child is dead.\n",
}
process_alive = false;
}
}
}
if (!process_alive) {
if (svc->last_restart != 0) {
/* it was long ago reset restart threshold */
}
}
/* restart the process */
return;
}
return;
}
return;
}
/* all fine, set up the task checker again */
}
{
DEBUG(0, ("failed to add event, monitor offline for [%s]!\n",
/* FIXME: shutdown ? */
}
}
{
int ret;
return ret;
}
DEBUG(0, ("No services configured!\n"));
return EINVAL;
}
return EOK;
}
struct event_context *event_ctx,
struct confdb_ctx *cdb)
{
char **doms;
char *path;
int ret, i;
if (!ctx) {
DEBUG(0, ("fatal error initializing monitor!\n"));
return ENOMEM;
}
return ret;
/* Initialize D-BUS Server
* The monitor will act as a D-BUS server for all
* SSSD processes */
return ret;
}
/* start all services */
if (!svc) {
return ENOMEM;
}
if (!path) {
return ENOMEM;
}
continue;
}
continue;
}
/* Add this service to the queue to be started once the monitor
* enters its mainloop.
*/
continue;
}
}
/* now start the data providers */
return ret;
}
for (i = 0; doms[i]; i++) {
if (!svc) {
return ENOMEM;
}
if (!path) {
return ENOMEM;
}
continue;
}
continue;
}
/* if no command is present do not run the domain */
/* the LOCAL domain does not need a backend at the moment */
DEBUG(0, ("Missing command to run provider\n"));
}
continue;
}
continue;
}
}
return EOK;
}
static int mt_conn_destructor(void *ptr)
{
/* now clear up so that the rest of the code will know there
* is no connection attached to the service anymore */
return 0;
}
/*
* dbus_service_init
* This function should initiate a query to the newly connected
* service to discover the service's identity (invoke the getIdentity
* method on the new client). The reply callback for this request
* should set the connection destructor appropriately.
*/
{
/* hang off this memory to the connection so that when the connection
* is freed we can call a destructor to clear up the structure and
* have a way to know we need to restart the service */
if (!mt_conn) {
DEBUG(0,("Out of memory?!\n"));
return ENOMEM;
}
/* at this stage we still do not know what service is this
* we will know only after we get its identity, so we make
* up a temporary fake service and complete the operation
* when we receive the reply */
if (!svc) {
return ENOMEM;
}
/*
* Set up identity request
* This should be a well-known path and method
* for all services
*/
DEBUG(0,("Out of memory?!\n"));
return ENOMEM;
}
if (!dbret) {
/*
* Critical Failure
* We can't communicate on this connection
* We'll drop it using the default destructor.
*/
DEBUG(0, ("D-BUS send failed.\n"));
return EIO;
}
/* Set up the reply handler */
return EOK;
}
{
struct sbus_conn_ctx *conn_ctx;
char *svc_name;
int type;
if (!reply) {
/* reply should never be null. This function shouldn't be called
* until reply is valid or timeout has occurred. If reply is NULL
* here, something is seriously wrong and we should bail out.
*/
DEBUG(0, ("Serious error. A reply callback was called but no reply was received and no timeout occurred\n"));
/* Destroy this connection */
goto done;
}
switch (type) {
if (!ret) {
goto done;
}
/* search this service in the list */
while (svc) {
if (ret == 0) {
break;
}
}
if (!svc) {
DEBUG(0,("Unable to find peer in list of services, killing connection!\n"));
goto done;
}
/* transfer all from the fake service and get rid of it */
/* Set up the destructor for this service */
break;
case DBUS_MESSAGE_TYPE_ERROR:
DEBUG(0,("getIdentity returned an error [%s], closing connection.\n",
/* Falling through to default intentionally*/
default:
/*
* Timeout or other error occurred or something
* unexpected happened.
* It doesn't matter which, because either way we
* know that this connection isn't trustworthy.
* We'll destroy it now.
*/
return;
}
done:
}
/* service_send_ping
* this function send a dbus ping to a service.
* It returns EOK if all is fine or ENXIO if the connection is
* not available (either not yet set up or teared down).
* Returns e generic error in other cases.
*/
{
return ENXIO;
}
/*
* Set up identity request
* This should be a well-known path and method
* for all services
*/
if (!msg) {
DEBUG(0,("Out of memory?!\n"));
return ENOMEM;
}
if (!dbret) {
/*
* Critical Failure
* We can't communicate on this connection
* We'll drop it using the default destructor.
*/
DEBUG(0, ("D-BUS send failed.\n"));
return EIO;
}
/* Set up the reply handler */
return EOK;
}
{
struct sbus_conn_ctx *conn_ctx;
const char *dbus_error_name;
int type;
if (!reply) {
/* reply should never be null. This function shouldn't be called
* until reply is valid or timeout has occurred. If reply is NULL
* here, something is seriously wrong and we should bail out.
*/
DEBUG(0, ("A reply callback was called but no reply was received"
" and no timeout occurred\n"));
/* Destroy this connection */
goto done;
}
switch (type) {
/* ok peer replied,
* set the reply timestamp into the service structure */
break;
case DBUS_MESSAGE_TYPE_ERROR:
/* timeouts are handled in the main service check function */
break;
DEBUG(0,("A service PING returned an error [%s], closing connection.\n",
/* Falling through to default intentionally*/
default:
/*
* Timeout or other error occurred or something
* unexpected happened.
* It doesn't matter which, because either way we
* know that this connection isn't trustworthy.
* We'll destroy it now.
*/
}
done:
}
/* service_check_alive
* This function checks if the service child is still alive
*/
{
int status;
if (pid == 0) {
return EOK;
}
/* TODO: what do we do now ? */
return EINVAL;
}
/* TODO: check configuration to see if it was removed
* from the list of process to run */
}
return ECHILD;
}
{
int i;
if (args) {
}
}
/* parse a string into arguments.
* arguments are separated by a space
* '\' is an escape character and can be used only to escape
* itself or the white space.
*/
static char **parse_args(const char *str)
{
const char *p;
char **ret, **r;
char *tmp;
int num;
int i, e;
num = 0;
e = 0;
i = 0;
p = str;
while (*p) {
switch (*p) {
case '\\':
if (e) {
tmp[i] = '\\';
i++;
e = 0;
} else {
e = 1;
}
break;
case ' ':
if (e) {
tmp[i] = ' ';
i++;
e = 0;
} else {
tmp[i] = '\0';
i++;
}
break;
default:
if (e) {
tmp[i] = '\\';
i++;
e = 0;
}
tmp[i] = *p;
i++;
break;
}
p++;
/* check if this was the last char */
if (*p == '\0') {
if (e) {
tmp[i] = '\\';
i++;
e = 0;
}
tmp[i] = '\0';
i++;
}
/* check next char and skip multiple spaces */
continue;
}
if (!r) goto fail;
ret = r;
num++;
i = 0;
}
return ret;
fail:
return NULL;
}
struct timed_event *te,
{
struct timed_event *te;
/* Add a timed event to start up the service.
* We have to do this in order to avoid a race
* condition where the service being started forks
* and attempts to connect to the SBUS before
* the monitor is serving it.
*/
return ENOMEM;
}
return EOK;
}
struct timed_event *te,
{
char **args;
return;
}
return;
}
/* Parent */
return;
}
/* child */
/* If we are here, exec() has failed
* Print errno and abort quickly */
/* We have to call _exit() instead of exit() here
* because a bug in D-BUS will cause the server to
* close its socket at exit() */
_exit(1);
}
{
int opt;
int opt_daemon = 0;
int opt_interactive = 0;
int flags = 0;
struct main_context *main_ctx;
int ret;
struct poptOption long_options[] = {
"Become a daemon (default)", NULL }, \
"Run interactive (not a daemon)", NULL}, \
{ NULL }
};
switch(opt) {
default:
return 1;
}
}
if (opt_daemon && opt_interactive) {
return 1;
}
/* we want a pid file check */
flags |= FLAGS_PID_FILE;
/* set up things like debug , signals, daemonization, etc... */
/* loop on main */
return 0;
}