/* This file is part of Mailfromd.
Copyright (C) 2006, 2007, 2008 Sergey Poznyakoff
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, 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 . */
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include /* FIXME */
#include
#include
#include
#include
#include
#include
#include "inttostr.h"
#include "libmf.h"
int smap_reload;
unsigned int smap_timeout;
int smap_transcript;
struct mf_privs smap_privs;
char *syslog_tag;
char *negative_reply;
char *positive_reply;
char *
expand_reply_text (const char *arg, const char *key, struct mu_auth_data *auth)
{
int rc;
mu_vartab_t vtab;
char *reply = NULL;
char buf[INT_BUFSIZE_BOUND (uintmax_t)];
if (!arg)
return NULL;
mu_vartab_create (&vtab);
mu_vartab_define (vtab, "key", key, 0);
mu_vartab_define (vtab, MU_AUTH_NAME, auth ? auth->name : "", 0);
mu_vartab_define (vtab, MU_AUTH_PASSWD, auth ? auth->passwd : "", 0);
mu_vartab_define (vtab, MU_AUTH_UID,
auth ? umaxtostr (auth->uid, buf) : "-1", 0);
mu_vartab_define (vtab, MU_AUTH_GID,
auth ? umaxtostr (auth->gid, buf) : "-1", 0);
mu_vartab_define (vtab, MU_AUTH_GECOS,
auth ? auth->gecos : "", 0);
mu_vartab_define (vtab, MU_AUTH_DIR, auth ? auth->dir : "", 0);
mu_vartab_define (vtab, MU_AUTH_SHELL, auth ? auth->shell : "", 0);
mu_vartab_define (vtab, MU_AUTH_MAILBOX, auth ? auth->mailbox : "", 0);
mu_vartab_define (vtab, MU_AUTH_QUOTA,
auth ? umaxtostr (auth->quota, buf) : "NONE", 0);
rc = mu_vartab_expand (vtab, arg, &reply);
if (rc)
mu_error (_("cannot expand string `%s': %s"), arg, mu_strerror (rc));
mu_vartab_destroy (&vtab);
return reply;
}
int
read_delim (FILE *fp, int delim, char *buf, size_t bufsize)
{
int c;
int len = 0;
alarm (smap_timeout);
bufsize--;
while (len < bufsize && ((c = fgetc (fp))) != delim)
{
if (c == EOF)
{
if (len)
{
if (smap_transcript && len)
mu_diag_output (MU_DIAG_INFO, "recv: %.*s:", len, buf);
mu_error (_("unexpected end of file on input"));
exit (1);
}
return 0;
}
buf[len++] = c;
}
alarm (0);
buf[len] = 0;
if (smap_transcript)
mu_diag_output (MU_DIAG_INFO, "recv: %s:", buf);
return len;
}
void
smap_reply (FILE *fp, const char *code, const char *result)
{
size_t len = strlen (code);
if (result)
len += 1 + strlen (result);
if (result)
{
if (smap_transcript)
mu_diag_output (MU_DIAG_INFO, "send: %lu:%s %s,",
(unsigned long) len, code, result);
fprintf (fp, "%lu:%s %s,", (unsigned long) len, code, result);
}
else
{
if (smap_transcript)
mu_diag_output (MU_DIAG_INFO, "send: %lu:%s,",
(unsigned long) len, code);
fprintf (fp, "%lu:%s,", (unsigned long) len, code);
}
}
int
smap_loop (FILE *in, FILE *out)
{
char buf[80];
size_t len;
char *key, *p;
struct mu_auth_data *auth;
mf_epriv_setup (NULL);
mf_priv_setup (&smap_privs);
setvbuf (in, NULL, _IONBF, 0);
setvbuf (out, NULL, _IONBF, 0);
/* Read input: */
while (read_delim (in, ':', buf, sizeof buf))
{
char *reply_txt = NULL;
len = strtoul (buf, &p, 10);
if (*p != 0)
{
mu_error (_("protocol error: expected packet length, but found %s"),
buf);
exit (1);
}
if (len > sizeof buf - 1)
{
mu_error (_("protocol error: packet length too big"));
exit (1);
}
if (fread (buf, 1, len, in) != len)
{
mu_error (_("protocol error: short read"));
exit (1);
}
buf[len] = 0;
if (smap_transcript)
mu_diag_output (MU_DIAG_INFO, "recv: %s,", buf);
if (getc (in) != ',')
{
mu_error (_("protocol error: missing terminating comma"));
exit (1);
}
buf[len] = 0;
key = strchr (buf, ' ');
if (!key)
{
mu_error (_("protocol error: missing map name"));
exit (1);
}
key++;
len = strlen (key);
if (key[len-1] == '*')
key[len-1] = 0;
auth = mu_get_auth_by_name (key);
/* Reply: */
if (!auth)
{
reply_txt = expand_reply_text (negative_reply, key, NULL);
smap_reply (out, "NOTFOUND", reply_txt);
}
else
{
reply_txt = expand_reply_text (positive_reply, key, auth);
smap_reply (out, "OK", reply_txt);
mu_auth_data_free (auth);
}
free (reply_txt);
}
/* Cleanup and exit */
fclose (in);
fclose (out);
return 0;
}
int
smap_connection (int fd, struct sockaddr *sa, int salen, void *data,
mu_ip_server_t srv, time_t timeout, int transcript)
{
smap_timeout = timeout;
smap_transcript = transcript;
smap_loop (fdopen (fd, "r"), fdopen (fd, "w"));
return 0;
}
/* Command line and configuration */
const char *program_version = "smap (" PACKAGE_STRING ")";
static char doc[] = N_("General-purpose remote map for MeTA1");
static char args_doc[] = "";
#define OPTION_GECOS 256
#define OPTION_POSITIVE_REPLY 257
#define OPTION_NEGATIVE_REPLY 258
#define OPTION_NEGATIVE_OK 259
#define OPTION_FOREGROUND 260
static struct argp_option options[] =
{
#define GRP 0
{ "foreground", OPTION_FOREGROUND, 0, 0, N_("Remain in foreground."), GRP+1},
{ "inetd", 'i', 0, 0, N_("Run in inetd mode"), GRP+1},
{ "daemon", 'd', N_("NUMBER"), OPTION_ARG_OPTIONAL,
N_("Runs in daemon mode with a maximum of NUMBER children"), GRP+1 },
#undef GRP
#define GRP 10
{ "user", 'U', "NAME", 0,
N_("Run as user NAME"), GRP+1 },
{ "gecos", OPTION_GECOS, NULL, 0,
N_("Same as '--positive-reply=${gecos}'"), GRP+1 },
/* FIXME-MU: */
{ "log-tag", 'L', N_("STRING"), 0,
N_("Set syslog tag"), GRP+1 },
{ "positive-reply", OPTION_POSITIVE_REPLY, N_("STRING"), 0,
N_("Set positive reply text"), GRP+1 },
{ "negative-reply", OPTION_NEGATIVE_REPLY, N_("STRING"), 0,
N_("Set negative reply text"), GRP+1 },
#undef GRP
{ NULL }
};
static error_t parse_opt (int key, char *arg, struct argp_state *state);
static struct argp argp = {
options,
parse_opt,
args_doc,
doc,
NULL,
NULL, NULL
};
static const char *argp_capa[] = {
"auth",
"common",
"debug",
"license",
"logging",
NULL
};
static error_t
parse_opt (int key, char *arg, struct argp_state *state)
{
static struct mu_argp_node_list lst;
switch (key)
{
case 'd':
mu_argp_node_list_new (&lst, "mode", "daemon");
if (arg)
mu_argp_node_list_new (&lst, "max-children", arg);
break;
case 'i':
mu_argp_node_list_new (&lst, "mode", "inetd");
break;
case OPTION_FOREGROUND:
mu_argp_node_list_new (&lst, "foreground", "yes");
break;
case OPTION_NEGATIVE_REPLY:
negative_reply = arg;
break;
case OPTION_POSITIVE_REPLY:
positive_reply = arg;
break;
case 'L':
syslog_tag = arg;
break;
case 'U':
mu_argp_node_list_new (&lst, "user", arg);
break;
case OPTION_GECOS:
positive_reply = "${gecos}";
break;
default:
return ARGP_ERR_UNKNOWN;
case ARGP_KEY_INIT:
mu_argp_node_list_init (&lst);
break;
case ARGP_KEY_FINI:
mu_argp_node_list_finish (&lst, NULL, NULL);
break;
case ARGP_KEY_ERROR:
exit (EX_USAGE);
}
return 0;
}
static struct mu_cfg_param cfg_param[] = {
{ "user", mu_cfg_string, &smap_privs.user, 0, NULL,
N_("Run with this user privileges.") },
{ "group", MU_CFG_LIST_OF(mu_cfg_string), &smap_privs.groups, 0, NULL,
N_("Retain supplementary group.") },
{ "allgroups", mu_cfg_bool, &smap_privs.allgroups, 0, NULL,
N_("Retain all supplementary groups of which user is a member.") },
{ "positive-reply", mu_cfg_string, &positive_reply, 0, NULL,
N_("Set positive reply text.") },
{ "negative-reply", mu_cfg_string, &negative_reply, 0, NULL,
N_("Set negative reply text.") },
{ ".server", mu_cfg_section, NULL, 0, NULL,
N_("Server configuration.") },
{ NULL }
};
void
logging_setup ()
{
mu_debug_t debug;
if (syslog_tag)
mu_log_tag = syslog_tag;
openlog (MU_LOG_TAG (), LOG_PID, mu_log_facility);
mu_diag_get_debug (&debug);
mu_debug_set_print (debug, mu_diag_syslog_printer, NULL);
mu_debug_default_printer = mu_debug_syslog_printer;
}
static RETSIGTYPE
smap_hup (int sig)
{
mu_m_server_stop (1);
smap_reload = 1;
}
static void
version (FILE *stream, struct argp_state *state)
{
mailfromd_version ("smap", stream);
}
int
main (int argc, char **argv)
{
int status;
mu_m_server_t server;
mf_init_nls ();
MU_AUTH_REGISTER_ALL_MODULES ();
mu_acl_cfg_init ();
mu_m_server_cfg_init ();
mu_m_server_create (&server, program_version);
mu_m_server_set_conn (server, smap_connection);
mu_m_server_set_mode (server, MODE_INTERACTIVE);
mu_m_server_set_max_children (server, 20);
/* FIXME: mu_m_server_set_pidfile (); */
/* FIXME: mu_m_server_set_default_port (server, ??); */
mu_m_server_set_timeout (server, 600);
mu_argp_init (program_version, "<" PACKAGE_BUGREPORT ">");
argp_program_version_hook = version;
if (mu_app_init (&argp, argp_capa, cfg_param, argc, argv, 0, NULL, server))
exit (1);
logging_setup ();
if (mu_m_server_mode (server) == MODE_DAEMON)
{
if (argv[0][0] != '/')
mu_diag_output (MU_DIAG_NOTICE,
_("Program name is not absolute; reloading will not "
"be possible"));
else
{
sigset_t set;
mu_m_server_get_sigset (server, &set);
sigdelset (&set, SIGHUP);
mu_m_server_set_sigset (server, &set);
signal (SIGHUP, smap_hup);
}
mf_epriv_setup (&smap_privs);
mu_m_server_begin (server);
status = mu_m_server_run (server);
mu_m_server_end (server);
mu_m_server_destroy (&server);
mf_epriv_setup (NULL);
if (smap_reload)
{
mu_diag_output (MU_DIAG_NOTICE, _("Restarting"));
execvp (argv[0], argv);
}
}
else
status = smap_loop (stdin, stdout);
exit (status == 0 ? 0 : EX_SOFTWARE);
}
void
xalloc_die ()
{
mu_error ("not enough memory");
abort ();
}
/*
Local Variables:
c-file-style: "gnu"
End:
*/
/* EOF */