#include <stdio.h>
#include <stdio_ext.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <memory.h>
#include <alloca.h>
#include <ucontext.h>
#include <errno.h>
#include <syslog.h>
#include <string.h>
#include <strings.h>
#include <door.h>
#include <wait.h>
#include <libintl.h>
#include <locale.h>
#include <sys/param.h>
#include <sys/systeminfo.h>
#include <sys/thread.h>
#include <rpc/xdr.h>
#include <priv.h>
#include <sys/fs_reparse.h>
#include <priv_utils.h>
#include <rpcsvc/daemon_utils.h>
#define REPARSED_CMD_OPTS "v"
#define DOOR_RESULT_BUFSZ (MAXPATHLEN + sizeof (reparsed_door_res_t))
#define SAFETY_BUFFER 8*1024
static char *MyName;
static int verbose = 0;
static int start_reparsed_svcs();
static void daemonize(void);
static void reparsed_door_call_error(int error, int buflen);
static void reparsed_doorfunc(void *cookie, char *argp, size_t arg_size,
door_desc_t *dp, uint_t n_desc);
static void
usage()
{
syslog(LOG_ERR, "Usage: %s", MyName);
syslog(LOG_ERR, "\t[-v]\t\tverbose error messages)");
exit(1);
}
static void
warn_hup(int i)
{
syslog(LOG_ERR, "SIGHUP received: ignored");
(void) signal(SIGHUP, warn_hup);
}
static void
daemonize(void)
{
switch (fork()) {
case -1:
syslog(LOG_ERR, "reparsed: can't fork - errno %d", errno);
exit(2);
case 0:
break;
default:
_exit(0);
}
(void) chdir("/");
(void) close(0);
(void) close(1);
(void) close(2);
(void) open("/dev/null", O_RDONLY);
(void) open("/dev/null", O_WRONLY);
(void) dup(1);
(void) setsid();
}
int
main(int argc, char *argv[])
{
pid_t pid;
int c, error;
struct rlimit rlset;
char *defval;
MyName = argv[0];
if (geteuid() != 0) {
syslog(LOG_ERR, "%s must be run as root", MyName);
exit(1);
}
while ((c = getopt(argc, argv, REPARSED_CMD_OPTS)) != EOF) {
switch (c) {
case 'v':
verbose++;
break;
default:
usage();
}
}
daemonize();
openlog(MyName, LOG_PID | LOG_NDELAY, LOG_DAEMON);
(void) _create_daemon_lock(REPARSED, DAEMON_UID, DAEMON_GID);
(void) enable_extended_FILE_stdio(-1, -1);
switch (_enter_daemon_lock(REPARSED)) {
case 0:
break;
case -1:
syslog(LOG_ERR, "Error locking for %s", REPARSED);
exit(2);
default:
exit(0);
}
(void) signal(SIGHUP, warn_hup);
if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, 0, 0,
(char *)NULL) == -1) {
syslog(LOG_ERR, "should be run with sufficient privileges");
exit(3);
}
__fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION,
PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, (char *)NULL);
return (start_reparsed_svcs());
}
__NORETURN static void
reparsed_door_call_error(int error, int buflen)
{
reparsed_door_res_t rpd_res;
memset(&rpd_res, 0, sizeof (reparsed_door_res_t));
rpd_res.res_status = error;
rpd_res.res_len = buflen;
(void) door_return((char *)&rpd_res,
sizeof (reparsed_door_res_t), NULL, 0);
(void) door_return(NULL, 0, NULL, 0);
abort();
}
static void
reparsed_doorfunc(void *cookie, char *argp, size_t arg_size,
door_desc_t *dp, uint_t n_desc)
{
int err;
size_t bufsz;
char *svc_type, *svc_data;
char *cp, *buf, *sbuf, res_buf[DOOR_RESULT_BUFSZ];
reparsed_door_res_t *resp;
if ((argp == NULL) || (arg_size == 0)) {
reparsed_door_call_error(EINVAL, 0);
}
if (verbose)
syslog(LOG_NOTICE, "reparsed_door: [%s, %d]", argp, arg_size);
if ((svc_type = strdup(argp)) == NULL) {
reparsed_door_call_error(ENOMEM, 0);
}
if ((cp = strchr(svc_type, ':')) == NULL) {
free(svc_type);
reparsed_door_call_error(EINVAL, 0);
}
*cp++ = '\0';
svc_data = cp;
resp = (reparsed_door_res_t *)res_buf;
buf = resp->res_data;
bufsz = sizeof (res_buf) - sizeof (reparsed_door_res_t);
err = reparse_deref(svc_type, svc_data, buf, &bufsz);
if (verbose)
syslog(LOG_NOTICE,
"reparsed_deref(svc_type: %s, data: %s, size: %d) -> %d",
svc_type, svc_data, bufsz, err);
switch (err) {
case 0:
break;
case EOVERFLOW:
sbuf = alloca(bufsz + sizeof (reparsed_door_res_t));
if (sbuf == NULL || stack_inbounds(buf) == 0 ||
stack_inbounds(buf + sizeof (reparsed_door_res_t) +
SAFETY_BUFFER - 1) == 0) {
free(svc_type);
reparsed_door_call_error(ENOMEM, 0);
}
resp = (reparsed_door_res_t *)sbuf;
if ((err = reparse_deref(svc_type, svc_data, resp->res_data,
&bufsz)) == 0)
break;
default:
free(svc_type);
reparsed_door_call_error(err, 0);
}
free(svc_type);
if (verbose)
syslog(LOG_NOTICE, "reparsed_door_return <buf=%s> size=%d",
buf, bufsz);
resp->res_status = 0;
resp->res_len = bufsz;
(void) door_return((char *)resp, bufsz + sizeof (reparsed_door_res_t),
NULL, 0);
(void) door_return(NULL, 0, NULL, 0);
}
static int
start_reparsed_svcs()
{
int doorfd;
int dfd;
if ((doorfd = door_create(reparsed_doorfunc, NULL,
DOOR_REFUSE_DESC|DOOR_NO_CANCEL)) == -1) {
syslog(LOG_ERR, "Unable to create door");
return (1);
}
if ((dfd = open(REPARSED_DOOR, O_RDWR|O_CREAT|O_TRUNC,
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
syslog(LOG_ERR, "unable to open %s", REPARSED_DOOR);
(void) close(doorfd);
return (1);
}
(void) fdetach(REPARSED_DOOR);
if (fattach(doorfd, REPARSED_DOOR) == -1) {
syslog(LOG_ERR, "Unable to fattach door %s", REPARSED_DOOR);
(void) close(doorfd);
(void) close(dfd);
return (1);
}
(void) close(dfd);
while (1)
(void) pause();
}