monitor.c revision d72958f09ce3718019992b7a117f112e38855b55
dae63dc6ba9bdbaa3dfee38af1b7710055bf5168Vladimir Kotal Service monitor
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco Copyright (C) Simo Sorce 2008
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco This program is free software; you can redistribute it and/or modify
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco it under the terms of the GNU General Public License as published by
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco the Free Software Foundation; either version 3 of the License, or
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco (at your option) any later version.
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco This program is distributed in the hope that it will be useful,
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco but WITHOUT ANY WARRANTY; without even the implied warranty of
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco GNU General Public License for more details.
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco You should have received a copy of the GNU General Public License
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco along with this program. If not, see <http://www.gnu.org/licenses/>.
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco/* Needed for res_init() */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco/* ping time cannot be less then once every few seconds or the
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco * monitor will get crazy hammering children with messages */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco/* terminate the child after this interval by default if it
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco * doesn't shutdown on receiving SIGTERM */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco/* TODO: get the restart related values from config */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco/* maximum allowed number of service restarts if the restarts
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco * were less than MONITOR_RESTART_CNT_INTERVAL_RESET apart, which would
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco * indicate a crash after startup or after every request */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco/* The services are restarted with a delay in case the restart was
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco * hitting a race condition where the DP is not ready yet either.
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco * The MONITOR_MAX_RESTART_DELAY defines the maximum delay between
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco/* name of the monitor server instance */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco#define SSSD_PIDFILE_PATH PID_PATH"/"MONITOR_NAME".pid"
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco/* Special value to leave the Kerberos Replay Cache set to use
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco * the libkrb5 defaults
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco#define KRB5_RCACHE_DIR_DISABLE "__LIBKRB5_DEFAULTS__"
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco/* Warning messages */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco#define CONF_FILE_PERM_ERROR_MSG "Cannot read config file %s. Please check "\
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco "that the file is accessible only by the "\
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco "owner and owned by root.root.\n"
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco /* For running unprivileged services */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Koscostatic int monitor_service_init(struct sbus_connection *conn, void *data);
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Koscostatic int service_send_ping(struct mt_svc *svc);
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Koscostatic int service_signal_reset_offline(struct mt_svc *svc);
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Koscostatic void ping_check(DBusPendingCall *pending, void *data);
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Koscostatic void set_tasks_checker(struct mt_svc *srv);
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Koscostatic int monitor_kill_service (struct mt_svc *svc);
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Koscostatic int get_service_config(struct mt_ctx *ctx, const char *name,
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Koscostatic int get_provider_config(struct mt_ctx *ctx, const char *name,
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco const char *name,
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco const char *name,
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Koscostatic int mark_service_as_started(struct mt_svc *svc);
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Koscostatic int monitor_cleanup(void);
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Koscostatic void network_status_change_cb(void *cb_data)
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco DEBUG(SSSDBG_TRACE_INTERNAL, "A networking status change detected "
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco "signaling providers to reset offline status\n");
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco for (iter = ctx->svc_list; iter; iter = iter->next) {
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco /* Don't signal services, only providers */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco/* dbus_get_monitor_version
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco * Return the monitor version over D-BUS */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Koscostatic int get_monitor_version(struct sbus_request *dbus_req, void *data)
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco/* registers a new client.
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco * if operation is successful also sends back the Monitor version */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Koscostatic int client_registration(struct sbus_request *dbus_req, void *data)
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco mini = talloc_get_type(data, struct mon_init_conn);
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco DEBUG(SSSDBG_FATAL_FAILURE, "Connection holds no valid init data\n");
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco /* First thing, cancel the timeout */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco dbret = dbus_message_get_args(dbus_req->message, &dbus_error,
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco "Failed to parse message, killing connection\n");
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error);
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco /* FIXME: should we just talloc_zfree(conn) ? */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco "Received ID registration: (%s,%d)\n", svc_name, svc_ver);
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco /* search this service in the list */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco "Unable to find peer [%s] in list of services,"
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco /* FIXME: should we just talloc_zfree(conn) ? */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco /* Fill in svc structure with connection data */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco DEBUG(SSSDBG_CRIT_FAILURE, "Failed to mark service [%s]!\n", svc_name);
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco /* reply that all is ok */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco /* init complete, get rid of temp init context */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco struct mt_svc *svc = talloc_get_type(mem, struct mt_svc);
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco /* try to delist service */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco /* Cancel any pending pings */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco /* svc is beeing freed, neutralize the spy */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco talloc_set_destructor((TALLOC_CTX *)svc->conn_spy, NULL);
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco if (svc->type == MT_SVC_SERVICE && svc->svc_started
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco && svc->mt_ctx != NULL && svc->mt_ctx->started_services > 0) {
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco struct svc_spy *spy = talloc_get_type(mem, struct svc_spy);
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco /* svc->conn has been freed, NULL the pointer in svc */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco talloc_set_destructor((TALLOC_CTX *)spy, svc_spy_destructor);
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Koscostatic int mark_service_as_started(struct mt_svc *svc)
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco DEBUG(SSSDBG_FUNC_DATA, "Marking %s as started.\n", svc->name);
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco /* we need to attach a spy to the connection structure so that if some code
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco * frees it we can zero it out in the service structure. Otherwise we may
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco * try to access or even free, freed memory. */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco DEBUG(SSSDBG_FATAL_FAILURE, "Failed to attch spy\n");
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco /* check if all providers are up */
df0f31230faf76eaf3a4eac5eaac78389167caa6Lubos Kosco for (iter = ctx->svc_list; iter; iter = iter->next) {
if (iter) {
goto done;
goto done;
errno = 0;
if (ret != 0) {
done:
return ret;
if (!to) {
return ENOMEM;
return EOK;
{ &mon_srv_iface_meta, 0 },
char *monitor_address;
int ret;
return ret;
return ret;
int ret;
switch (ret) {
case EOK:
case ENXIO:
int ret;
goto done;
goto done;
done:
return ret;
int ret;
if (!reply) {
const char *filename)
int ret;
if(ret != 0) {
return EIO;
return EOK;
int ret;
return EOK;
return EIO;
return ENOMEM;
return ret;
while (dom) {
return EINVAL;
while (other) {
return EOK;
while (dom) {
count++;
return EINVAL;
return EOK;
char ***_services)
int ret;
char **domain_names;
size_t c;
char *conf_path;
char *id_provider;
bool add_pac = false;
return ENOMEM;
&domain_names);
goto done;
domain_names[c]);
goto done;
add_pac = true;
domain_names[c]);
goto done;
done:
return ret;
int ii;
for (i = 0; services[i]; i++) {
return services[i];
return NULL;
char *user_str;
return ret;
return ret;
return EOK;
int ret;
int timeout_seconds;
return ret;
return EINVAL;
return EINVAL;
return ret;
return ret;
return ret;
return ret;
return EOK;
return ret;
return ret;
return EOK;
int ret;
char *path;
if (!svc) {
return ENOMEM;
return ENOMEM;
return ENOMEM;
if (!path) {
return ENOMEM;
return ret;
return ENOMEM;
return ENOMEM;
return ENOMEM;
return ENOMEM;
return ENOMEM;
if (debug_to_file) {
return ENOMEM;
return ENOMEM;
return ret;
return EOK;
const char *name,
int restarts)
int ret;
return ret;
return ret;
int ret;
char *path;
if (!svc) {
return ENOMEM;
return ENOMEM;
return ENOMEM;
if (!path) {
return ENOMEM;
return ret;
return ret;
return ret;
return EIO;
return ENOMEM;
return ENOMEM;
return ENOMEM;
return ENOMEM;
return ENOMEM;
if (debug_to_file) {
return ENOMEM;
return ENOMEM;
return EOK;
const char *name,
int restarts)
int ret;
name);
return ret;
return ENOENT;
return ret;
int signum,
int count,
void *siginfo,
void *private_data)
static int monitor_cleanup(void)
int ret;
errno = 0;
return ret;
return EOK;
int status;
int kret;
bool killed;
killed = false;
errno = 0;
if (kret < 0) {
error = 0;
errno = 0;
killed = true;
} else if (pid != 0) {
error = 0;
killed = true;
if (!killed) {
} while (!killed);
#if HAVE_GETPGRP
error = 0;
errno = 0;
int signum,
int count,
void *siginfo,
void *private_data)
int ret;
if (ret == 0) {
int signum,
int count,
void *siginfo,
void *private_data)
int signum,
int count,
void *siginfo,
void *private_data)
const char *config_file,
if(!ctx) {
return ENOMEM;
goto done;
goto done;
goto done;
goto done;
goto done;
goto done;
goto done;
goto done;
if (ret != 0) {
goto done;
done:
return ret;
const char *file,
bool ignore_missing);
#ifdef HAVE_INOTIFY
if (!te) {
struct rewatch_ctx {
char *name;
if (!tmp_ctx) return;
if (!in_event) {
goto done;
errno = 0;
sizeof(struct inotify_event));
goto done;
if (!name) {
goto done;
errno = 0;
goto done;
if (!cb) {
goto done;
if(!rw_ctx) {
goto done;
goto done;
done:
int err;
if (ret < 0) {
#ifdef HAVE_INOTIFY
return err;
if (fd_args < 0) {
return EINVAL;
if (ret < 0) {
return EINVAL;
file_ctx);
if (!tfd) {
return EIO;
if(!cb) {
return ENOMEM;
return ENOMEM;
return err;
return EOK;
return EINVAL;
const char *file,
bool ignore_missing)
bool use_inotify;
if (ret < 0) {
return EOK;
return err;
true, &use_inotify);
return ret;
if (use_inotify) {
use_inotify = false;
if (!use_inotify) {
return ret;
const char *file,
bool ignore_missing)
if (ret < 0) {
return EOK;
return err;
if (!cb) {
return ENOMEM;
return ENOMEM;
return EIO;
return EOK;
const char *config_file)
char *rcachedir;
int num_providers;
int ret;
int error;
&rcachedir);
return ret;
errno = 0;
if (ret < 0) {
return EIO;
return EIO;
return EIO;
return EIO;
return EIO;
return ret;
/* Watch for changes to the DNS resolv.conf */
monitor_update_resolv,true);
return ret;
if (!tmp_ctx) {
return ENOMEM;
return ret;
return ret;
return ret;
num_providers = 0;
return ret;
if (num_providers > 0) {
return ret;
return EOK;
if (!mini) {
return ENOMEM;
return ENOMEM;
if (!intf) {
return ENOMEM;
int ret;
return ENXIO;
if (!msg) {
return ENOMEM;
return ret;
const char *dbus_error_name;
int type;
if (!svc) {
if (!reply) {
goto done;
switch (type) {
case DBUS_MESSAGE_TYPE_ERROR:
if (!dbus_error_name) {
done:
return ENOMEM;
return EOK;
char **args;
int restart_delay;
int opt;
int opt_daemon = 0;
int opt_interactive = 0;
int opt_version = 0;
int flags = 0;
int ret;
switch(opt) {
if (opt_version) {
return EXIT_SUCCESS;
if (uid != 0) {
if (!tmp_ctx) {
if (opt_interactive) {
if (opt_config_file) {
if (!config_file) {
if (debug_to_file) {
if (ret) {
#ifdef USE_KEYRING
switch (ret) {
case ENOENT:
case EEXIST:
case EOK:
switch (ret) {
case ERR_MISSING_CONF:
case EPERM:
case EACCES: