#include "sm.h"
#ifdef HAVE_IDN
#include <stringprep.h>
#endif
static sig_atomic_t sm_shutdown = 0;
sig_atomic_t sm_lost_router = 0;
static sig_atomic_t sm_logrotate = 0;
static void _sm_signal(int signum)
{
sm_shutdown = 1;
sm_lost_router = 0;
}
static void _sm_signal_hup(int signum)
{
sm_logrotate = 1;
}
static void _sm_pidfile(sm_t sm) {
char *pidfile;
FILE *f;
pid_t pid;
pidfile = config_get_one(sm->config, "pidfile", 0);
if(pidfile == NULL)
return;
pid = getpid();
if((f = fopen(pidfile, "w+")) == NULL) {
log_write(sm->log, LOG_ERR, "couldn't open %s for writing: %s", pidfile, strerror(errno));
return;
}
if(fprintf(f, "%d", pid) < 0) {
log_write(sm->log, LOG_ERR, "couldn't write to %s: %s", pidfile, strerror(errno));
return;
}
fclose(f);
log_write(sm->log, LOG_INFO, "process id is %d, written to %s", pid, pidfile);
}
static void _sm_config_expand(sm_t sm)
{
char *str;
sm->id = config_get_one(sm->config, "id", 0);
if(sm->id == NULL)
sm->id = "localhost";
sm->router_ip = config_get_one(sm->config, "router.ip", 0);
if(sm->router_ip == NULL)
sm->router_ip = "127.0.0.1";
sm->router_port = j_atoi(config_get_one(sm->config, "router.port", 0), 5347);
sm->router_user = config_get_one(sm->config, "router.user", 0);
if(sm->router_user == NULL)
sm->router_user = "jabberd";
sm->router_pass = config_get_one(sm->config, "router.pass", 0);
if(sm->router_pass == NULL)
sm->router_pass = "secret";
sm->router_pemfile = config_get_one(sm->config, "router.pemfile", 0);
sm->retry_init = j_atoi(config_get_one(sm->config, "router.retry.init", 0), 3);
sm->retry_lost = j_atoi(config_get_one(sm->config, "router.retry.lost", 0), 3);
if((sm->retry_sleep = j_atoi(config_get_one(sm->config, "router.retry.sleep", 0), 2)) < 1)
sm->retry_sleep = 1;
sm->log_type = log_STDOUT;
if(config_get(sm->config, "log") != NULL) {
if((str = config_get_attr(sm->config, "log", 0, "type")) != NULL) {
if(strcmp(str, "file") == 0)
sm->log_type = log_FILE;
else if(strcmp(str, "syslog") == 0)
sm->log_type = log_SYSLOG;
}
}
if(sm->log_type == log_SYSLOG) {
sm->log_facility = config_get_one(sm->config, "log.facility", 0);
sm->log_ident = config_get_one(sm->config, "log.ident", 0);
if(sm->log_ident == NULL)
sm->log_ident = "jabberd/sm";
} else if(sm->log_type == log_FILE)
sm->log_ident = config_get_one(sm->config, "log.file", 0);
}
static int _sm_router_connect(sm_t sm) {
log_write(sm->log, LOG_NOTICE, "attempting connection to router at %s, port=%d", sm->router_ip, sm->router_port);
sm->fd = mio_connect(sm->mio, sm->router_port, sm->router_ip, sm_mio_callback, (void *) sm);
if(sm->fd < 0) {
if(errno == ECONNREFUSED)
sm_lost_router = 1;
log_write(sm->log, LOG_NOTICE, "connection attempt to router failed: %s (%d)", strerror(errno), errno);
return 1;
}
sm->router = sx_new(sm->sx_env, sm->fd, sm_sx_callback, (void *) sm);
sx_client_init(sm->router, 0, NULL, NULL, NULL, "1.0");
return 0;
}
int main(int argc, char **argv) {
sm_t sm;
char *config_file;
int optchar;
sess_t sess;
char id[1024];
struct passwd *p;
int newgid, newuid;
#ifdef POOL_DEBUG
time_t pool_time = 0;
#endif
#ifdef JABBER_USER
p = getpwnam(JABBER_USER);
if (p == NULL) {
printf("Error: could not find user %s\n", JABBER_USER);
return 1;
}
newuid = p->pw_uid;
newgid = p->pw_gid;
memset(p, 0, sizeof(struct passwd));
if (initgroups(JABBER_USER, newgid)) {
printf("cannot initialize groups for user %s: %s\n", JABBER_USER, strerror(errno));
return 1;
}
if (setgid(newgid)) {
printf("cannot setgid: %s\n", strerror(errno));
return 1;
}
if (seteuid(newuid)) {
printf("cannot seteuid: %s\n", strerror(errno));
return 1;
}
#else
printf("No user is defined for setuid/setgid, continuing\n");
#endif
#ifdef HAVE_UMASK
umask((mode_t) 0027);
#endif
srand(time(NULL));
#ifdef HAVE_WINSOCK2_H
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return 0;
}
}
#endif
jabber_signal(SIGINT, _sm_signal);
jabber_signal(SIGTERM, _sm_signal);
#ifdef SIGHUP
jabber_signal(SIGHUP, _sm_signal_hup);
#endif
#ifdef SIGPIPE
jabber_signal(SIGPIPE, SIG_IGN);
#endif
sm = (sm_t) malloc(sizeof(struct sm_st));
memset(sm, 0, sizeof(struct sm_st));
sm->config = config_new();
config_file = CONFIG_DIR "/sm.xml";
while((optchar = getopt(argc, argv, "Dc:h?")) >= 0)
{
switch(optchar)
{
case 'c':
config_file = optarg;
break;
case 'D':
#ifdef DEBUG
set_debug_flag(1);
#else
printf("WARN: Debugging not enabled. Ignoring -D.\n");
#endif
break;
case 'h': case '?': default:
fputs(
"sm - jabberd session manager (" VERSION ")\n"
"Usage: sm <options>\n"
"Options are:\n"
" -c <config> config file to use [default: " CONFIG_DIR "/sm.xml]\n"
#ifdef DEBUG
" -D Show debug output\n"
#endif
,
stdout);
config_free(sm->config);
free(sm);
return 1;
}
}
if(config_load(sm->config, config_file) != 0)
{
fputs("sm: couldn't load config, aborting\n", stderr);
config_free(sm->config);
free(sm);
return 2;
}
_sm_config_expand(sm);
sm->log = log_new(sm->log_type, sm->log_ident, sm->log_facility);
log_write(sm->log, LOG_NOTICE, "starting up");
strncpy(id, sm->id, 1024);
id[sizeof(id)-1] = '\0';
#ifdef HAVE_IDN
if (stringprep_nameprep(id, 1024) != 0) {
log_write(sm->log, LOG_ERR, "cannot stringprep id %s, aborting", sm->id);
exit(1);
}
#endif
sm->id = id;
log_write(sm->log, LOG_NOTICE, "id: %s", sm->id);
_sm_pidfile(sm);
sm_signature(sm, "jabberd sm " VERSION);
sm->pc = prep_cache_new();
sm->st = storage_new(sm);
if (sm->st == NULL) {
log_write(sm->log, LOG_ERR, "failed to initialise one or more storage drivers, aborting");
exit(1);
}
sm->xmlns = xhash_new(101);
xhash_put(sm->xmlns, uri_AUTH, (void *) ns_AUTH);
xhash_put(sm->xmlns, uri_REGISTER, (void *) ns_REGISTER);
xhash_put(sm->xmlns, uri_ROSTER, (void *) ns_ROSTER);
xhash_put(sm->xmlns, uri_AGENTS, (void *) ns_AGENTS);
xhash_put(sm->xmlns, uri_DELAY, (void *) ns_DELAY);
xhash_put(sm->xmlns, uri_VERSION, (void *) ns_VERSION);
xhash_put(sm->xmlns, uri_TIME, (void *) ns_TIME);
xhash_put(sm->xmlns, uri_VCARD, (void *) ns_VCARD);
xhash_put(sm->xmlns, uri_PRIVATE, (void *) ns_PRIVATE);
xhash_put(sm->xmlns, uri_BROWSE, (void *) ns_BROWSE);
xhash_put(sm->xmlns, uri_EVENT, (void *) ns_EVENT);
xhash_put(sm->xmlns, uri_GATEWAY, (void *) ns_GATEWAY);
xhash_put(sm->xmlns, uri_LAST, (void *) ns_LAST);
xhash_put(sm->xmlns, uri_EXPIRE, (void *) ns_EXPIRE);
xhash_put(sm->xmlns, uri_PRIVACY, (void *) ns_PRIVACY);
xhash_put(sm->xmlns, uri_SEARCH, (void *) ns_SEARCH);
xhash_put(sm->xmlns, uri_DISCO, (void *) ns_DISCO);
xhash_put(sm->xmlns, uri_DISCO_ITEMS, (void *) ns_DISCO_ITEMS);
xhash_put(sm->xmlns, uri_DISCO_INFO, (void *) ns_DISCO_INFO);
xhash_put(sm->xmlns, uri_VACATION, (void *) ns_VACATION);
sm->features = xhash_new(101);
sm->acls = aci_load(sm);
feature_register(sm, "iq");
sm->mm = mm_new(sm);
log_write(sm->log, LOG_NOTICE, "version: %s", sm->signature);
sm->sessions = xhash_new(401);
sm->users = xhash_new(401);
sm->sx_env = sx_env_new();
#ifdef HAVE_SSL
#ifdef JABBER_USER
if (seteuid(0)) {
log_write(sm->log, LOG_ERR, "cannot seteuid to root: %s", strerror(errno));
return 1;
}
#else
log_write(sm->log, LOG_NOTICE, "No user is defined for setuid/setgid, continuing");
#endif // JABBER_USER
if(sm->router_pemfile != NULL) {
sm->sx_ssl = sx_env_plugin(sm->sx_env, sx_ssl_init, sm->router_pemfile, NULL);
if(sm->sx_ssl == NULL) {
log_write(sm->log, LOG_ERR, "failed to load SSL pemfile, SSL disabled");
sm->router_pemfile = NULL;
}
}
#endif // HAVE_SSL
#ifdef JABBER_USER
if (setuid(newuid)) {
log_write(sm->log, LOG_ERR, "cannot setuid(%d): %s", newuid, strerror(errno));
return 1;
}
#else
log_write(sm->log, LOG_NOTICE, "No user is defined for setuid/setgid, continuing");
#endif // JABBER_USER
sm->sx_sasl = sx_env_plugin(sm->sx_env, sx_sasl_init, "xmpp", SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT, NULL, NULL, 0);
if(sm->sx_sasl == NULL) {
log_write(sm->log, LOG_ERR, "failed to initialise SASL context, aborting");
exit(1);
}
sm->mio = mio_new(1024);
sm->retry_left = sm->retry_init;
_sm_router_connect(sm);
while(!sm_shutdown) {
mio_run(sm->mio, 5);
if(sm_logrotate) {
log_write(sm->log, LOG_NOTICE, "reopening log ...");
log_free(sm->log);
sm->log = log_new(sm->log_type, sm->log_ident, sm->log_facility);
log_write(sm->log, LOG_NOTICE, "log started");
sm_logrotate = 0;
}
if(sm_lost_router) {
if(sm->retry_left < 0) {
log_write(sm->log, LOG_NOTICE, "attempting reconnect");
sleep(sm->retry_sleep);
sm_lost_router = 0;
_sm_router_connect(sm);
}
else if(sm->retry_left == 0) {
sm_shutdown = 1;
}
else {
log_write(sm->log, LOG_NOTICE, "attempting reconnect (%d left)", sm->retry_left);
sm->retry_left--;
sleep(sm->retry_sleep);
sm_lost_router = 0;
_sm_router_connect(sm);
}
}
#ifdef POOL_DEBUG
if(time(NULL) > pool_time + 60) {
pool_stat(1);
pool_time = time(NULL);
}
#endif
}
log_write(sm->log, LOG_NOTICE, "shutting down");
if(xhash_iter_first(sm->sessions))
do {
xhash_iter_get(sm->sessions, NULL, (void *) &sess);
sm_c2s_action(sess, "ended", NULL);
sess_end(sess);
} while(xhash_count(sm->sessions) > 0);
xhash_free(sm->sessions);
mio_free(sm->mio);
aci_unload(sm->acls);
xhash_free(sm->acls);
xhash_free(sm->features);
xhash_free(sm->xmlns);
xhash_free(sm->users);
mm_free(sm->mm);
storage_free(sm->st);
sx_free(sm->router);
sx_env_free(sm->sx_env);
prep_cache_free(sm->pc);
log_free(sm->log);
config_free(sm->config);
free(sm);
#ifdef POOL_DEBUG
pool_stat(1);
#endif
#ifdef HAVE_WINSOCK2_H
WSACleanup();
#endif
return 0;
}