/*
SSSD
Service monitor
Copyright (C) Simo Sorce 2008
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 "util/util.h"
#include "util/child_common.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/param.h>
#include <time.h>
#include <string.h>
#ifdef HAVE_SYS_INOTIFY_H
#include <sys/inotify.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
/* Needed for res_init() */
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include "popt.h"
#include "tevent.h"
#include "confdb/confdb.h"
#include "confdb/confdb_setup.h"
#include "collection.h"
#include "ini_config.h"
#include "db/sysdb.h"
#include "monitor/monitor.h"
#include "dbus/dbus.h"
#include "sbus/sssd_dbus.h"
#include "monitor/monitor_interfaces.h"
#include "responder/common/responder_sbus.h"
#ifdef USE_KEYRING
#include <keyutils.h>
#endif
/* 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
/* terminate the child after this interval by default if it
* doesn't shutdown on receiving SIGTERM */
#define MONITOR_DEF_FORCE_TIME 60
/* TODO: get the restart related values from config */
#define MONITOR_RESTART_CNT_INTERVAL_RESET 30
/* maximum allowed number of service restarts if the restarts
* were less than MONITOR_RESTART_CNT_INTERVAL_RESET apart, which would
* indicate a crash after startup or after every request */
#define MONITOR_MAX_SVC_RESTARTS 2
/* The services are restarted with a delay in case the restart was
* hitting a race condition where the DP is not ready yet either.
* The MONITOR_MAX_RESTART_DELAY defines the maximum delay between
* restarts.
*/
#define MONITOR_MAX_RESTART_DELAY 4
/* name of the monitor server instance */
#define MONITOR_NAME "sssd"
#define SSSD_PIDFILE_PATH PID_PATH"/"MONITOR_NAME".pid"
/* Special value to leave the Kerberos Replay Cache set to use
* the libkrb5 defaults
*/
#define KRB5_RCACHE_DIR_DISABLE "__LIBKRB5_DEFAULTS__"
int cmdline_debug_level;
int cmdline_debug_timestamps;
int cmdline_debug_microseconds;
struct svc_spy;
enum mt_svc_type {
MT_SVC_SERVICE,
MT_SVC_PROVIDER
};
struct mt_svc {
struct mt_svc *prev;
struct mt_svc *next;
enum mt_svc_type type;
struct sbus_connection *conn;
struct svc_spy *conn_spy;
struct mt_ctx *mt_ctx;
char *provider;
char *command;
char *name;
char *identity;
pid_t pid;
int ping_time;
int kill_time;
bool svc_started;
int restarts;
time_t last_restart;
int failed_pongs;
DBusPendingCall *pending;
int debug_level;
struct tevent_timer *ping_ev;
struct sss_child_ctx *child_ctx;
struct tevent_timer *sigkill_ev;
};
struct config_file_callback {
int wd;
int retries;
monitor_reconf_fn fn;
char *filename;
time_t modified;
struct config_file_callback *next;
struct config_file_callback *prev;
};
struct config_file_ctx {
TALLOC_CTX *parent_ctx;
struct tevent_timer *timer;
bool needs_update;
struct mt_ctx *mt_ctx;
struct config_file_callback *callbacks;
};
struct mt_ctx {
struct tevent_context *ev;
struct confdb_ctx *cdb;
TALLOC_CTX *domain_ctx; /* Memory context for domain list */
struct sss_domain_info *domains;
TALLOC_CTX *service_ctx; /* Memory context for services */
char **services;
int num_services;
int started_services;
struct mt_svc *svc_list;
struct sbus_connection *sbus_srv;
struct config_file_ctx *file_ctx;
int inotify_fd;
int service_id_timeout;
bool check_children;
bool services_started;
struct netlink_ctx *nlctx;
const char *conf_path;
struct sss_sigchild_ctx *sigchld_ctx;
bool is_daemon;
pid_t parent_pid;
};
static int start_service(struct mt_svc *mt_svc);
static int monitor_service_init(struct sbus_connection *conn, void *data);
static int service_send_ping(struct mt_svc *svc);
static int service_signal_reset_offline(struct mt_svc *svc);
static void ping_check(DBusPendingCall *pending, void *data);
static void set_tasks_checker(struct mt_svc *srv);
static int monitor_kill_service (struct mt_svc *svc);
static int get_service_config(struct mt_ctx *ctx, const char *name,
struct mt_svc **svc_cfg);
static int get_provider_config(struct mt_ctx *ctx, const char *name,
struct mt_svc **svc_cfg);
static int add_new_service(struct mt_ctx *ctx,
const char *name,
int restarts);
static int add_new_provider(struct mt_ctx *ctx,
const char *name,
int restarts);
static int mark_service_as_started(struct mt_svc *svc);
static int monitor_cleanup(void);
static void network_status_change_cb(void *cb_data)
{
struct mt_svc *iter;
struct mt_ctx *ctx = (struct mt_ctx *) cb_data;
DEBUG(SSSDBG_TRACE_INTERNAL, ("A networking status change detected "
"signaling providers to reset offline status\n"));
for (iter = ctx->svc_list; iter; iter = iter->next) {
/* Don't signal services, only providers */
if (iter->provider) {
service_signal_reset_offline(iter);
}
}
}
/* dbus_get_monitor_version
* Return the monitor version over D-BUS */
static int get_monitor_version(DBusMessage *message,
struct sbus_connection *conn)
{
dbus_uint16_t version = MONITOR_VERSION;
DBusMessage *reply;
dbus_bool_t ret;
reply = dbus_message_new_method_return(message);
if (!reply) return ENOMEM;
ret = dbus_message_append_args(reply,
DBUS_TYPE_UINT16, &version,
DBUS_TYPE_INVALID);
if (!ret) {
dbus_message_unref(reply);
return EIO;
}
/* send reply back */
sbus_conn_send_reply(conn, reply);
dbus_message_unref(reply);
return EOK;
}
struct mon_init_conn {
struct mt_ctx *ctx;
struct sbus_connection *conn;
struct tevent_timer *timeout;
};
static int add_svc_conn_spy(struct mt_svc *svc);
/* registers a new client.
* if operation is successful also sends back the Monitor version */
static int client_registration(DBusMessage *message,
struct sbus_connection *conn)
{
dbus_uint16_t version = MONITOR_VERSION;
struct mon_init_conn *mini;
struct mt_svc *svc;
void *data;
DBusMessage *reply;
DBusError dbus_error;
dbus_uint16_t svc_ver;
char *svc_name;
dbus_bool_t dbret;
int ret;
data = sbus_conn_get_private_data(conn);
mini = talloc_get_type(data, struct mon_init_conn);
if (!mini) {
DEBUG(0, ("Connection holds no valid init data\n"));
return EINVAL;
}
/* First thing, cancel the timeout */
talloc_zfree(mini->timeout);
dbus_error_init(&dbus_error);
dbret = dbus_message_get_args(message, &dbus_error,
DBUS_TYPE_STRING, &svc_name,
DBUS_TYPE_UINT16, &svc_ver,
DBUS_TYPE_INVALID);
if (!dbret) {
DEBUG(1, ("Failed to parse message, killing connection\n"));
if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error);
sbus_disconnect(conn);
/* FIXME: should we just talloc_zfree(conn) ? */
goto done;
}
DEBUG(4, ("Received ID registration: (%s,%d)\n", svc_name, svc_ver));
/* search this service in the list */
svc = mini->ctx->svc_list;
while (svc) {
ret = strcasecmp(svc->identity, svc_name);
if (ret == 0) {
break;
|