#include <assert.h>
#include <door.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ucred.h>
#include "repcache_protocol.h"
#include "configd.h"
#define INVALID_RESULT ((uint32_t)-1U)
static int main_door_fd = -1;
static void
main_switcher(void *cookie, char *argp, size_t arg_size, door_desc_t *desc,
uint_t n_desc)
{
repository_door_request_t *request;
repository_door_response_t reply;
door_desc_t reply_desc;
thread_info_t *ti = thread_self();
int send_desc = 0;
int fd;
thread_newstate(ti, TI_MAIN_DOOR_CALL);
ti->ti_main_door_request = (void *)argp;
assert(cookie == REPOSITORY_DOOR_COOKIE);
reply.rdr_status = INVALID_RESULT;
if (argp == DOOR_UNREF_DATA) {
backend_fini();
exit(CONFIGD_EXIT_LOST_MAIN_DOOR);
}
assert(n_desc == 0);
if (arg_size < offsetofend(repository_door_request_t, rdr_version)) {
reply.rdr_status = REPOSITORY_DOOR_FAIL_BAD_REQUEST;
goto fail;
}
request = (repository_door_request_t *)argp;
ti->ti_main_door_request = request;
if (request->rdr_version != REPOSITORY_DOOR_VERSION) {
reply.rdr_status = REPOSITORY_DOOR_FAIL_VERSION_MISMATCH;
goto fail;
}
if (arg_size < offsetofend(repository_door_request_t, rdr_request)) {
reply.rdr_status = REPOSITORY_DOOR_FAIL_BAD_REQUEST;
goto fail;
}
if (door_ucred(&ti->ti_ucred) != 0) {
reply.rdr_status = REPOSITORY_DOOR_FAIL_PERMISSION_DENIED;
goto fail;
}
switch (request->rdr_request) {
case REPOSITORY_DOOR_REQUEST_CONNECT:
fd = -1;
reply.rdr_status = create_connection(ti->ti_ucred, request,
arg_size, &fd);
if (reply.rdr_status != REPOSITORY_DOOR_SUCCESS) {
assert(fd == -1);
goto fail;
}
assert(fd != -1);
reply_desc.d_attributes = DOOR_DESCRIPTOR | DOOR_RELEASE;
reply_desc.d_data.d_desc.d_descriptor = fd;
send_desc = 1;
break;
default:
reply.rdr_status = REPOSITORY_DOOR_FAIL_BAD_REQUEST;
goto fail;
}
fail:
assert(reply.rdr_status != INVALID_RESULT);
thread_newstate(ti, TI_DOOR_RETURN);
ti->ti_main_door_request = NULL;
(void) door_return((char *)&reply, sizeof (reply),
&reply_desc, (send_desc)? 1:0);
(void) door_return(NULL, 0, NULL, 0);
}
int
setup_main_door(const char *doorpath)
{
mode_t oldmask;
int fd;
int door_flags = DOOR_UNREF | DOOR_REFUSE_DESC;
#ifdef DOOR_NO_CANCEL
door_flags |= DOOR_NO_CANCEL;
#endif
if ((main_door_fd = door_create(main_switcher, REPOSITORY_DOOR_COOKIE,
door_flags)) < 0) {
perror("door_create");
return (0);
}
#ifdef DOOR_PARAM_DATA_MIN
if (door_setparam(main_door_fd, DOOR_PARAM_DATA_MIN,
offsetofend(repository_door_request_t, rdr_request)) == -1 ||
door_setparam(main_door_fd, DOOR_PARAM_DATA_MAX,
sizeof (repository_door_request_t)) == -1) {
perror("door_setparam");
return (0);
}
#endif
oldmask = umask(000);
fd = open(doorpath, O_RDWR | O_CREAT | O_EXCL, 0644);
(void) umask(oldmask);
if (fd >= 0)
(void) close(fd);
if (fattach(main_door_fd, doorpath) < 0) {
if ((errno != EBUSY) ||
(fdetach(doorpath) < 0) ||
(fattach(main_door_fd, doorpath) < 0)) {
perror("fattach");
(void) door_revoke(main_door_fd);
main_door_fd = -1;
return (0);
}
}
return (1);
}