monitor.c revision fa93cd0f0fc75a6d635079e67788f8a9fe183c3c
/*
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/>.
*/
#include "util/child_common.h"
#include <time.h>
#include <string.h>
#ifdef HAVE_SYS_INOTIFY_H
#endif
#include <unistd.h>
#include <fcntl.h>
#include <popt.h>
#include <tevent.h>
/* Needed for res_init() */
#include <resolv.h>
#include "confdb/confdb_setup.h"
#include "sbus/sssd_dbus.h"
#include "monitor/monitor_interfaces.h"
#include "responder/common/responder_sbus.h"
#ifdef USE_KEYRING
#include <keyutils.h>
#endif
/* 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"
/* Special value to leave the Kerberos Replay Cache set to use
* the libkrb5 defaults
*/
#define KRB5_RCACHE_DIR_DISABLE "__LIBKRB5_DEFAULTS__"
/* Warning messages */
#define CONF_FILE_PERM_ERROR_MSG "Cannot read config file %s. Please check "\
"that the file is accessible only by the "\
"owner and owned by root.root.\n"
int cmdline_debug_level;
struct svc_spy;
enum mt_svc_type {
};
struct mt_svc {
enum mt_svc_type type;
struct sbus_connection *conn;
char *provider;
char *command;
char *name;
char *identity;
int kill_time;
bool svc_started;
int restarts;
int debug_level;
struct sss_child_ctx *child_ctx;
};
struct config_file_callback {
int wd;
int retries;
char *filename;
struct config_file_callback *next;
struct config_file_callback *prev;
};
struct config_file_ctx {
struct tevent_timer *timer;
bool needs_update;
struct config_file_callback *callbacks;
};
struct mt_ctx {
struct tevent_context *ev;
struct confdb_ctx *cdb;
struct sss_domain_info *domains;
char **services;
int num_services;
int started_services;
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;
/* For running unprivileged services */
};
const char *name,
int restarts);
const char *name,
int restarts);
static int monitor_cleanup(void);
static void network_status_change_cb(void *cb_data)
{
"signaling providers to reset offline status\n");
/* Don't signal services, only providers */
}
}
}
/* dbus_get_monitor_version
* Return the monitor version over D-BUS */
{
}
struct mon_init_conn {
struct sbus_connection *conn;
struct tevent_timer *timeout;
};
/* registers a new client.
* if operation is successful also sends back the Monitor version */
{
struct mon_init_conn *mini;
char *svc_name;
int ret;
if (!mini) {
return EINVAL;
}
/* First thing, cancel the timeout */
if (!dbret) {
"Failed to parse message, killing connection\n");
/* FIXME: should we just talloc_zfree(conn) ? */
goto done;
}
/* search this service in the list */
while (svc) {
if (ret == 0) {
break;
}
}
if (!svc) {
"Unable to find peer [%s] in list of services,"
" killing connection!\n", svc_name);
/* FIXME: should we just talloc_zfree(conn) ? */
goto done;
}
/* Fill in svc structure with connection data */
if (ret) {
goto done;
}
/* reply that all is ok */
done:
/* init complete, get rid of temp init context */
return EOK;
}
struct svc_spy {
};
static int svc_destructor(void *mem)
{
if (!svc) {
/* ?!?!? */
return 0;
}
/* try to delist service */
}
/* Cancel any pending calls */
}
/* svc is being freed, neutralize the spy */
}
}
return 0;
}
static int svc_spy_destructor(void *mem)
{
if (!spy) {
/* ?!?!? */
return 0;
}
/* svc->conn has been freed, NULL the pointer in svc */
return 0;
}
{
return EOK;
}
{
if (WIFEXITED(wait_status)) {
"Child [%d] exited with code [%d]\n",
} else if (WIFSIGNALED(wait_status)) {
"Child [%d] terminated with signal [%d]\n",
} else {
/* Forcibly kill this child, just in case */
/* Let us get caught by another
* call to the SIGCHLD handler
*/
}
}
{
int ret;
int i;
svc->svc_started = true;
/* we need to attach a spy to the connection structure so that if some code
* frees it we can zero it out in the service structure. Otherwise we may
* try to access or even free, freed memory. */
if (ret) {
goto done;
}
if (!ctx->services_started) {
/* check if all providers are up */
break;
}
}
if (iter) {
/* there are still unstarted providers */
goto done;
}
ctx->services_started = true;
/* then start all services */
}
}
ctx->started_services++;
}
/* Initialization is complete, terminate parent process if in daemon
* mode. Make sure we send the signal to the right process */
/* the parent process was already terminated */
ctx->parent_pid);
goto done;
}
"terminating parent process\n");
errno = 0;
if (ret != 0) {
}
}
}
done:
return ret;
}
struct tevent_timer *te,
{
int i;
if (!ctx->services_started) {
"forcing services startup!\n");
ctx->services_started = true;
/* then start all services */
}
}
}
{
struct tevent_timer *to;
/* 5 seconds should be plenty */
if (!to) {
return ENOMEM;
}
return EOK;
}
struct mon_srv_iface monitor_methods = {
{ &mon_srv_iface_meta, 0 },
};
/* monitor_dbus_init
* Set up the monitor service as a D-BUS Server */
{
char *monitor_address;
int ret;
return ret;
}
/* If a service is running as unprivileged user, we need to make sure this
* user can access the monitor sbus server. root is still king, so we don't
* lose any access.
*/
return ret;
}
{
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.
*/
"A reply callback was called but no reply was received"
" and no timeout occurred\n");
/* Destroy this connection */
return;
}
/* TODO: Handle cases where the call has timed out or returned
* with an error.
*/
}
const char *filename)
{
int ret;
if(ret != 0) {
return EIO;
}
/* Signal all services to reload their DNS configuration */
}
return EOK;
}
{
int ret;
/* The local provider requires no signaling */
return EOK;
}
/* Avoid a race condition where we are trying to
* order a service to reload that hasn't started
* yet.
*/
return EIO;
}
"Out of memory trying to allocate memory to invoke: %s\n",
return ENOMEM;
}
return ret;
}
{
}
{
}
{
}
{
}
{
}
{
}
{
}
{
while (dom) {
return EINVAL;
}
while (other) {
"Domains '%s' and '%s' overlap in range %u - %u\n",
}
}
}
return EOK;
}
{
while (dom) {
count++;
}
if (count > 1) {
break;
}
}
if (count > 1) {
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;
}
for (c = 0; domain_names[c] != NULL; c++) {
domain_names[c]);
goto done;
}
if (id_provider == NULL) {
"domain [%s], trying next domain.\n", domain_names[c]);
continue;
}
add_pac = true;
}
} else {
"domain [%s], trying next domain.\n",
domain_names[c]);
}
}
if (BUILD_WITH_PAC_RESPONDER && add_pac &&
goto done;
}
}
done:
return ret;
}
static char *check_services(char **services)
{
const char * const *known_services = get_known_services();
int i;
int ii;
/* Check if services we are about to start are in the list if known */
for (i = 0; services[i]; i++) {
break;
}
}
return services[i];
}
}
return NULL;
}
{
char *user_str;
return ret;
}
return ret;
}
return EOK;
}
{
int ret;
int timeout_seconds;
int i;
10, &timeout_seconds);
return ret;
}
return EINVAL;
}
"services. Some functionality might "
"be missing\n");
}
return EINVAL;
}
ctx->started_services = 0;
ctx->num_services = 0;
ctx->num_services++;
}
return ret;
}
return ret;
}
return ret;
}
return ret;
}
return EOK;
}
/* This is a temporary function that returns false if the service
* being started was only tested when running as root.
*/
static bool svc_supported_as_nonroot(const char *svc_name)
{
return true;
}
return false;
}
{
int ret;
char *path;
if (!svc) {
return ENOMEM;
}
return ENOMEM;
}
return ENOMEM;
}
if (!path) {
return ENOMEM;
}
return ret;
}
}
);
return ENOMEM;
}
return ENOMEM;
}
if (cmdline_debug_level != SSSDBG_UNRESOLVED) {
);
return ENOMEM;
}
}
);
return ENOMEM;
}
}
);
return ENOMEM;
}
}
if (debug_to_file) {
);
return ENOMEM;
}
);
return ENOMEM;
}
}
}
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;
}
"Failed to find ID provider from [%s] configuration\n", name);
return ret;
}
"Failed to find command from [%s] configuration\n", name);
return ret;
}
/* if no provider is present do not run the domain */
return EIO;
}
/* if there are no custom commands, build a default one */
);
return ENOMEM;
}
return ENOMEM;
}
if (cmdline_debug_level != SSSDBG_UNRESOLVED) {
);
return ENOMEM;
}
}
);
return ENOMEM;
}
}
);
return ENOMEM;
}
}
if (debug_to_file) {
);
return ENOMEM;
}
);
return ENOMEM;
}
}
}
return EOK;
}
const char *name,
int restarts)
{
int ret;
"Could not get provider configuration for [%s]\n",
name);
return ret;
}
/* The LOCAL provider requires no back-end currently
* We'll add it to the service list, but we don't need
* to poll it.
*/
svc->svc_started = true;
return ENOENT;
}
}
return ret;
}
struct tevent_signal *se,
int signum,
int count,
void *siginfo,
void *private_data)
{
/* Send D-Bus message to other services to rotate their logs.
* NSS service receives also message to clear memory caches. */
}
}
}
}
static int monitor_cleanup(void)
{
int ret;
errno = 0;
if (ret == -1) {
return ret;
}
return EOK;
}
{
int status;
int kret;
bool killed;
/* Kill all of our known children manually */
/* The local provider has no PID */
continue;
}
killed = false;
do {
errno = 0;
if (kret < 0) {
}
error = 0;
do {
errno = 0;
if (pid == -1) {
/* An error occurred while waiting */
killed = true;
"[%d][%s] while waiting for [%s]\n",
/* Forcibly kill this child */
break;
}
} else if (pid != 0) {
error = 0;
} else if (WIFSIGNALED(status)) {
} else {
/* Forcibly kill this child */
}
killed = true;
}
if (!killed) {
/* Sleep 10ms and try again */
usleep(10000);
}
} while (!killed);
}
#if HAVE_GETPGRP
/* Kill any remaining children in our process group, just in case
* we have any leftover children we don't expect. For example, if
* a krb5_child or ldap_child is running at the same moment.
*/
error = 0;
do {
errno = 0;
if (pid == -1) {
}
}
#endif
}
struct tevent_signal *se,
int signum,
int count,
void *siginfo,
void *private_data)
{
monitor_quit(mt_ctx, 0);
}
{
int ret;
if (ret == 0) {
}
}
}
struct tevent_signal *se,
int signum,
int count,
void *siginfo,
void *private_data)
{
"Signaling providers to go offline immediately.\n");
/* Signal all providers to immediately go offline */
/* Don't signal services, only providers */
}
}
}
struct tevent_signal *se,
int signum,
int count,
void *siginfo,
void *private_data)
{
"Signaling providers to reset offline immediately.\n");
}
}
}
}
static int monitor_ctx_destructor(void *mem)
{
/* zero out references in svcs so that they don't try
* to access the monitor context on process shutdown */
}
return 0;
}
/*
* This function should not be static otherwise gcc does some special kind of
* optimisations which should not happen according to code: chown (unlink)
* failed (return -1) but errno was zero.
* As a result of this * warning is printed ‘monitor’ may be used
* uninitialized in this function. Instead of checking errno for 0
* it's better to disable optimisation(in-lining) of this function.
*/
const char *config_file,
const char *config_dir,
{
if(!ctx) {
return ENOMEM;
}
goto done;
}
goto done;
}
/* Validate the configuration in the database */
/* Read in the monitor's configuration */
goto done;
}
/* Allow configuration database to be accessible
* when SSSD runs as nonroot */
if (ret != 0) {
"chown failed for [%s]: [%d][%s].\n",
goto done;
}
done:
}
return ret;
}
const char *file,
bool ignore_missing);
#ifdef HAVE_INOTIFY
struct tevent_timer *te,
{
struct config_file_ctx *file_ctx;
if (file_ctx->needs_update) {
/* Skip updating. It's already queued for update.
*/
return;
}
/* We will queue the file for update in one second.
* This way, if there is a script writing to the file
* repeatedly, we won't be attempting to update multiple
* times.
*/
if (!te) {
"Unable to queue config file update! Exiting.\n");
return;
}
}
struct rewatch_ctx {
struct config_file_callback *cb;
struct config_file_ctx *file_ctx;
};
struct tevent_timer *te,
struct tevent_timer *te,
{
struct inotify_event *in_event;
char *name;
struct config_file_ctx *file_ctx;
struct config_file_callback *cb;
struct rewatch_ctx *rw_ctx;
if (!tmp_ctx) return;
if (!in_event) {
goto done;
}
errno = 0;
sizeof(struct inotify_event));
if (len == -1) {
"Critical error reading inotify file descriptor [%d]: %s\n",
goto done;
}
/* Read in the name, even though we don't use it,
* so that read ptr is in the right place
*/
if (!name) {
goto done;
}
errno = 0;
if (len == -1) {
"Critical error reading inotify file descriptor [%d]: %s\n",
goto done;
}
}
break;
}
}
if (!cb) {
goto done;
}
/* Some text editors will move a new file on top of the
* existing one instead of modifying it. In this case,
* the kernel will send us an IN_IGNORE signal.
* We will try to open a new watch descriptor on the
* new file.
*/
struct tevent_timer *tev;
if(!rw_ctx) {
"Could not restore inotify watch. Quitting!\n");
goto done;
}
"Could not restore inotify watch. Quitting!\n");
}
goto done;
}
/* Tell the monitor to signal the children */
file_ctx->needs_update = 0;
done:
}
struct tevent_timer *te,
{
int err;
struct config_file_callback *cb;
struct rewatch_ctx *rw_ctx;
struct config_file_ctx *file_ctx;
/* Retry six times at five-second intervals before giving up */
"Could not restore inotify watch. Switching to polling!\n");
/* A new callback was created in monitor_config_file_fallback()*/
return;
}
"Could not add inotify watch for file [%s]. Error [%d:%s]\n",
"Could not restore inotify watch. Quitting!\n");
}
return;
}
/* Tell the monitor to signal the children */
file_ctx->needs_update = 0;
}
#endif /* HAVE_INOTIFY */
struct tevent_timer *te,
{
struct config_file_ctx *file_ctx;
struct config_file_callback *cb;
if (ret < 0) {
"Could not stat file [%s]. Error [%d:%s]\n",
/* TODO: If the config file is missing, should we shut down? */
return;
}
/* Parse the configuration file and signal the children */
/* Note: this will fire if the modification time changes into the past
* as well as the future.
*/
/* Tell the monitor to signal the children */
}
}
"Error: Config file no longer monitored for changes!\n");
}
}
{
#ifdef HAVE_INOTIFY
struct config_file_callback *cb;
/* Monitoring the file descriptor should be global */
/* Set up inotify to monitor the config file for changes */
"Could not initialize inotify, error [%d:%s]\n",
return err;
}
if (fd_args < 0) {
/* Could not set nonblocking */
return EINVAL;
}
fd_args |= O_NONBLOCK;
if (ret < 0) {
/* Could not set nonblocking */
return EINVAL;
}
/* Add the inotify file descriptor to the TEvent context */
file_ctx);
if (!tfd) {
return EIO;
}
}
if(!cb) {
return ENOMEM;
}
return ENOMEM;
}
"Could not add inotify watch for file [%s]. Error [%d:%s]\n",
return err;
}
return EOK;
#else
return EINVAL;
#endif /* HAVE_INOTIFY */
}
const char *file,
bool ignore_missing)
{
bool use_inotify;
if (ret < 0) {
"file [%s] is missing. Will not update online status "
"based on watching the file\n", file);
return EOK;
} else {
"Could not stat file [%s]. Error [%d:%s]\n",
return err;
}
}
}
true, &use_inotify);
return ret;
}
if (use_inotify) {
use_inotify = false;
}
}
if (!use_inotify) {
/* Could not monitor file with inotify, fall back to polling */
}
return ret;
}
const char *file,
bool ignore_missing)
{
if (ret < 0) {
"file [%s] is missing. Will not update online status "
"based on watching the file\n", file);
return EOK;
} else {
"Could not stat file [%s]. Error [%d:%s]\n",
return err;
}
}
if (!cb) {
return ENOMEM;
}
return ENOMEM;
}
return EIO;
}
}
return EOK;
}
#define MISSING_RESOLV_CONF_POLL_TIME 10
struct tevent_timer *te,
{
int ret;
monitor_update_resolv, false);
"tevent_add_timer failed. resolv.conf will be ignored.\n");
}
} else {
"Monitor_config_file failed. resolv.conf will be ignored.\n");
}
}
const char *config_file,
bool opt_netlinkoff)
{
struct tevent_signal *tes;
struct tevent_timer *te;
struct sss_domain_info *dom;
char *rcachedir;
int num_providers;
int ret;
int error;
struct sysdb_upgrade_ctx db_up_ctx;
/* Set up the environment variable for the Kerberos Replay Cache */
&rcachedir);
return ret;
}
{
errno = 0;
if (ret < 0) {
"Unable to set KRB5RCACHEDIR: %s."
"Will attempt to use libkrb5 defaults\n",
}
}
/* Set up an event handler for a SIGHUP */
monitor_hup, ctx);
return EIO;
}
/* Set up an event handler for a SIGINT */
BlockSignals(false, SIGINT);
return EIO;
}
/* Set up an event handler for a SIGTERM */
return EIO;
}
/* Handle SIGUSR1 (tell all providers to go offline) */
BlockSignals(false, SIGUSR1);
return EIO;
}
/* Handle SIGUSR2 (tell all providers to go reset offline) */
BlockSignals(false, SIGUSR2);
return EIO;
}
/* Set up the SIGCHLD handler */
#if 0
/* Watch for changes to the confdb config file */
true);
return ret;
}
#endif
/* Watch for changes to the DNS resolv.conf */
monitor_update_resolv, false);
}
return ret;
}
/* Avoid a startup race condition between process.
* We need to handle DB upgrades or DB creation only
* in one process before all other start.
*/
if (!tmp_ctx) {
return ENOMEM;
}
return ret;
}
/* Initialize D-BUS Server
* The monitor will act as a D-BUS server for all
* SSSD processes */
return ret;
}
if (opt_netlinkoff == false) {
"Cannot set up listening for network notifications\n");
return ret;
}
}
/* start providers */
num_providers = 0;
return ret;
}
}
}
if (num_providers > 0) {
/* now set the services stratup timeout *
* (responders will be started automatically when all
* providers are up and running or when the tomeout
* expires) */
return ret;
}
} else {
int i;
ctx->services_started = true;
/* No providers start services immediately
* Normally this means only LOCAL is configured */
}
}
return EOK;
}
struct tevent_timer *te,
{
struct mon_init_conn *mini;
}
/*
* monitor_service_init
* Set up a timeout function and temporary connection structure.
* If the client does not identify before the timeout kicks in,
* the client is forcibly disconnected.
*/
{
struct mon_init_conn *mini;
if (!mini) {
return ENOMEM;
}
/* Allow access from the SSSD user */
/* 10 seconds should be plenty */
return ENOMEM;
}
MON_SRV_PATH, mini);
}
struct tevent_timer *te,
{
struct tevent_timer *te;
tv = tevent_timeval_current();
/* 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 tevent_timer *te,
{
char **args;
return;
}
"Could not fork child to start service [%s]. "
return;
}
/* Parent */
/* Handle process exit */
"Could not register sigchld handler.\n");
/* Should we exit here? For now, we'll hope this
* child never dies, because we can't restart it.
*/
}
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);
}
struct tevent_timer *te,
{
return;
}
} else {
/* Invalid type? */
}
/* Free the old service (which will also remove it
* from the child list)
*/
}
{
/* Check the number of restart tries and relaunch the service */
return;
}
{
int restart_delay;
struct tevent_timer *te;
/* Handle the actual checks for how many times to restart this
* service before giving up.
*/
}
/* Restart the service */
"Exiting the SSSD. Could not restart critical service [%s].",
/* exit the SSSD with an error, shutting down all
* services and domains.
* We do this because if one of the responders is down
* and can't come back up, this is the only way to
* guarantee admin intervention.
*/
return;
}
/* restarts are schedule after 0, 2, 4 seconds */
if (restart_delay > MONITOR_MAX_RESTART_DELAY) {
}
/* Nothing much we can do */
"Failed to allocate timed event: mt_svc_restart.\n");
return;
}
}
{
int opt;
int opt_daemon = 0;
int opt_interactive = 0;
int opt_genconf = 0;
int opt_version = 0;
int opt_netlinkoff = 0;
char *opt_config_file = NULL;
char *config_file = NULL;
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}, \
_("Disable netlink interface"), NULL}, \
_("Specify a non-default config file"), NULL}, \
_("Refresh the configuration database, then exit"), \
NULL}, \
_("Print version number and exit"), NULL }, \
};
/* Set debug level to invalid value so we can deside if -d 0 was used. */
switch(opt) {
default:
return 1;
}
}
if (opt_version) {
return EXIT_SUCCESS;
}
/* If the level or timestamps was passed at the command-line, we want
* to save it and pass it to the children later.
*/
if (opt_daemon && opt_interactive) {
return 1;
}
return 1;
}
opt_daemon = 1;
}
if (uid != 0) {
return 8;
}
if (!tmp_ctx) {
return 7;
}
if (opt_interactive) {
debug_to_stderr = 1;
}
if (opt_genconf) {
flags |= FLAGS_GEN_CONF;
debug_to_stderr = 1;
}
if (opt_config_file) {
} else {
}
if (!config_file) {
return 6;
}
/* we want a pid file check */
flags |= FLAGS_PID_FILE;
/* Open before server_setup() does to have logging
* during configuration checking */
if (debug_to_file) {
ret = open_debug_file();
if (ret) {
return 7;
}
}
#ifdef USE_KEYRING
/* Do this before all the forks, it sets the session key ring so all
* keys are private to the daemon and cannot be read by any other process
* tree */
/* make a new session */
if (ret == -1) {
"Could not create private keyring session. "
"If you store password there they may be easily accessible "
}
if (ret == -1) {
"Could not set permissions on private keyring. "
"If you store password there they may be easily accessible "
}
#endif
/* Warn if nscd seems to be running */
switch (ret) {
case ENOENT:
"NSCD socket was detected. NSCD caching capabilities "
"may conflict with SSSD for users and groups. It is "
"recommended not to run NSCD in parallel with SSSD, "
"unless NSCD is configured not to cache the passwd, "
"group, netgroup and services nsswitch maps.");
break;
case EEXIST:
"NSCD socket was detected and seems to be configured "
"to cache some of the databases controlled by "
"SSSD [passwd,group,netgroup,services]. It is "
"recommended not to run NSCD in parallel with SSSD, "
"unless NSCD is configured not to cache these.");
break;
case EOK:
"seems to be configured not to interfere with "
"SSSD's caching capabilities\n");
}
}
/* Parse config file, fail if cannot be done */
&monitor);
switch (ret) {
case ERR_MISSING_CONF:
"Configuration file: %s does not exist.\n", config_file);
"Configuration file: %s does not exist.\n", config_file);
break;
case EPERM:
case EACCES:
break;
default:
"SSSD couldn't load the configuration database.\n");
"SSSD couldn't load the configuration database [%d]: %s.\n",
break;
}
return 4;
}
/* at this point we are done generating the config file, we may exit
* if that's all we were asked to do */
if (opt_genconf) return 0;
/* set up things like debug , signals, daemonization, etc... */
/* loop on main */
ret = monitor_cleanup();
return 0;
}