Rework ecore_drm code:

- This rewrite is so we can support passing credentials along with fds.
  - When we receive an 'fd' message from the slave process, we can now
have code which also gets the credentials (code incomplete still).

Signed-off-by: Chris Michael <cp.michael@samsung.com>
This commit is contained in:
Chris Michael 2013-12-20 11:31:22 +00:00
parent 521260b916
commit 661177022a
1 changed files with 220 additions and 197 deletions

View File

@ -3,10 +3,33 @@
#endif
#include "ecore_drm_private.h"
#include <sys/wait.h>
/* #include <fcntl.h> */
/* #include <sys/ioctl.h> */
#if defined(SCM_CREDS) // Bsd
# define CRED_STRUCT cmsgcred
# define CRED_UID cmcred_uid
# define SCM_CREDTYPE SCM_CREDS
#elif defined(SCM_CREDENTIALS) // Linux (3.2.0 ?)
# define CRED_STRUCT ucred
# define CRED_UID uid;
# define CRED_OPT SO_PASSCRED
# define SCM_CREDTYPE SCM_CREDENTIALS
#endif
#define RIGHTS_LEN CMSG_LEN(sizeof(int))
#define CREDS_LEN CMSG_LEN(sizeof(struct CRED_STRUCT))
#define CONTROL_LEN (RIGHTS_LEN + CREDS_LEN)
#define IOVSET(_iov, _addr, _len) \
(_iov)->iov_base = (void *)(_addr); \
(_iov)->iov_len = (_len);
/* local variables */
static int _ecore_drm_init_count = 0;
static int _ecore_drm_socket_fd[2] = { -1, -1 };
static int _ecore_drm_sockets[2] = { -1, -1 };
static struct cmsghdr *cmsgptr = NULL;
/* external variables */
struct udev *udev;
@ -15,151 +38,32 @@ int _ecore_drm_log_dom = -1;
FILE *lg;
#endif
#define IOVSET(_iov, _addr, _len) \
(_iov)->iov_base = (void *)(_addr); \
(_iov)->iov_len = (_len);
static ssize_t
_ecore_drm_socket_send(int opcode, int fd, void *data, size_t bytes)
{
Ecore_Drm_Message dmsg;
struct iovec iov[2];
struct msghdr msg;
struct cmsghdr *cmsg = NULL;
char ctrl[CMSG_SPACE(sizeof(int))];
ssize_t size;
IOVSET(iov + 0, &dmsg, sizeof(dmsg));
IOVSET(iov + 1, data, bytes);
dmsg.opcode = opcode;
dmsg.size = bytes;
memset(&msg, 0, sizeof(struct msghdr));
memset(ctrl, 0, CMSG_SPACE(sizeof(int)));
/* socketpair creates 'connected' sockets so we should not need to
* specify a msg_name here */
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
msg.msg_controllen = CMSG_SPACE(sizeof(int));
msg.msg_control = ctrl;
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
*((int *)CMSG_DATA(cmsg)) = fd;
errno = 0;
size = sendmsg(_ecore_drm_socket_fd[1], &msg, 0);
if (errno != 0)
{
DBG("\tSend Err: %d", errno);
DBG("\tSend Err: %s", strerror(errno));
}
return size;
}
static int
_ecore_drm_socket_receive(int opcode, int fd EINA_UNUSED, void **data, size_t bytes EINA_UNUSED)
{
int ret = -1, rfd;
Ecore_Drm_Message dmsg;
char buff[BUFSIZ];
struct iovec iov[2];
char ctrl[CMSG_SPACE(sizeof(int))];
struct msghdr msg;
struct cmsghdr *cmsg = NULL;
ssize_t size;
IOVSET(iov + 0, &dmsg, sizeof(dmsg));
IOVSET(iov + 1, &buff, sizeof(buff));
memset(&msg, 0, sizeof(msg));
/* socketpair creates 'connected' sockets so we should not need to
* specify a msg_name here */
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
msg.msg_controllen = CMSG_SPACE(sizeof(int));
msg.msg_control = ctrl;
errno = 0;
size = recvmsg(_ecore_drm_socket_fd[0], &msg, 0); // MSG_CMSG_CLOEXEC);
if (errno != 0)
{
DBG("\tRecv %li", size);
DBG("\tRecv Err: %d", errno);
DBG("\tRecv Err: %s", strerror(errno));
}
cmsg = CMSG_FIRSTHDR(&msg);
if ((cmsg) && (cmsg->cmsg_len == CMSG_LEN(sizeof(int))))
{
if (cmsg->cmsg_level != SOL_SOCKET)
{
DBG("Invalid cmsg level");
return -1;
}
if (cmsg->cmsg_type != SCM_RIGHTS)
{
DBG("Invalid cmsg type");
return -1;
}
}
rfd = *((int *)CMSG_DATA(cmsg));
DBG("\tFD: %d", rfd);
DBG("\tDmsg Opcode: %d", dmsg.opcode);
DBG("\tOpcode: %d", opcode);
if (dmsg.opcode != opcode) return -1;
switch (dmsg.opcode)
{
case ECORE_DRM_OP_DEVICE_OPEN:
case ECORE_DRM_OP_TTY_OPEN:
if (rfd >= 0) ret = 1;
if (data) *data = &rfd;
break;
case ECORE_DRM_OP_DEVICE_CLOSE:
case ECORE_DRM_OP_TTY_CLOSE:
ret = -1;
break;
default:
ret = -1;
break;
}
return ret;
}
static Eina_Bool
_ecore_drm_socket_create(void)
_ecore_drm_sockets_create(void)
{
/* test if we have already opened the socketpair */
if (_ecore_drm_socket_fd[0] > -1) return EINA_TRUE;
if (_ecore_drm_sockets[0] > -1) return EINA_TRUE;
/* setup socketpair */
if (socketpair(AF_LOCAL, SOCK_SEQPACKET, 0, _ecore_drm_socket_fd) < 0)
/* create a pair of sequenced sockets (fixed-length)
* NB: when reading from one of these, it is required that we read
* an entire packet with each read() call */
if (socketpair(AF_LOCAL, SOCK_SEQPACKET | SOCK_NONBLOCK,
0, _ecore_drm_sockets) < 0)
{
ERR("Socketpair Failed: %m");
return EINA_FALSE;
}
DBG("Parent Socket: %d", _ecore_drm_socket_fd[0]);
DBG("Child Socket: %d", _ecore_drm_socket_fd[1]);
/* NB: We don't want cloexec for the sockets. That would cause them to be
* closed when we exec the child process but we need them open so that
* we can pass messages */
/* if (fcntl(_ecore_drm_sockets[0], F_SETFD, FD_CLOEXEC) < 0) */
/* { */
/* ERR("Failed to set CLOEXEC: %m"); */
/* return EINA_FALSE; */
/* } */
DBG("Parent Socket: %d", _ecore_drm_sockets[0]);
DBG("Child Socket: %d", _ecore_drm_sockets[1]);
return EINA_TRUE;
}
@ -169,64 +73,184 @@ _ecore_drm_launcher_spawn(void)
{
pid_t pid;
/* TODO: test if spartacus is still alive */
if ((pid = fork()) < 0) return EINA_FALSE;
/* fork */
if ((pid = vfork()) < 0)
CRIT("Failed to fork: %m");
if (pid != 0)
return EINA_TRUE;
else if (pid == 0) /* child process. exec spartacus */
if (pid == 0)
{
char buff[PATH_MAX], env[32];
char env[64], buff[PATH_MAX];
char *args[1] = { NULL };
sigset_t mask;
/* if we are the child, start a rebellion */
DBG("Start Spartacus");
/* read socket for slave is 0 */
snprintf(env, sizeof(env), "%d", _ecore_drm_sockets[0]);
setenv("ECORE_DRM_LAUNCHER_SOCKET_READ", env, 1);
/* drop privileges */
if (setuid(getuid()) < 0)
WRN("Failed to drop privileges");
/* set to read socket for launch process to read from */
snprintf(env, sizeof(env), "%d", _ecore_drm_socket_fd[0]);
setenv("ECORE_DRM_LAUNCHER_WRITE_SOCKET", env, 1);
/* set to read socket for launch process to read from */
snprintf(env, sizeof(env), "%d", _ecore_drm_socket_fd[1]);
setenv("ECORE_DRM_LAUNCHER_READ_SOCKET", env, 1);
sigemptyset(&mask);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGCHLD);
sigaddset(&mask, SIGINT);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
/* write socket for slave is 1 */
snprintf(env, sizeof(env), "%d", _ecore_drm_sockets[1]);
setenv("ECORE_DRM_LAUNCHER_SOCKET_WRITE", env, 1);
/* assemble exec path */
snprintf(buff, sizeof(buff),
"%s/ecore_drm/bin/%s/ecore_drm_launch",
PACKAGE_LIB_DIR, MODULE_ARCH);
/* try to start a rebellion */
/* don't give our signal mask to the child */
sigemptyset(&mask);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGCHLD);
sigaddset(&mask, SIGINT);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
execv(buff, args);
_exit(127);
/* execv does not return unless there is a problem, spew the error */
ERR("Spartacus failed to start a rebellion: %m");
/* NB: We need to use execve here so that capabilities are inherited.
* Also, this should set Our (ecore_drm) effective uid to be the
* owner of the launched process (setuid in this case) */
/* char *ev[3] = { strdup(renv), strdup(wenv), NULL }; */
/* execve(buff, args, ev); */
}
else
{
int status;
/* unsetenv("ECORE_DRM_LAUNCHER_SOCKET"); */
while (waitpid(pid, &status, WNOHANG) < 0)
if (errno != EINTR) break;
return EINA_TRUE;
}
/* close(_ecore_drm_socket_fd[1]); */
return EINA_FALSE;
}
static ssize_t
_ecore_drm_socket_send(int opcode, int fd, void *data, size_t bytes)
{
Ecore_Drm_Message dmsg;
struct iovec iov[2];
struct msghdr msg;
struct cmsghdr *cmsg = NULL;
char buff[CMSG_SPACE(sizeof(int))];
ssize_t size;
/* Simplified version of sending messages. We don't need to send any
* 'credentials' with this as it is just basically an IPC to send over
* our request to the slave process */
/* NB: Hmm, don't think we need to set any socket options here */
IOVSET(iov + 0, &dmsg, sizeof(dmsg));
IOVSET(iov + 1, data, sizeof(data)); //bytes);
dmsg.opcode = opcode;
dmsg.size = bytes;
memset(buff, 0, CMSG_SPACE(sizeof(int)));
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
msg.msg_flags = 0;
msg.msg_controllen = CMSG_LEN(sizeof(int));
msg.msg_control = buff;
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
*((int *)CMSG_DATA(cmsg)) = fd;
/* DBG("Dmsg Size: %d", dmsg.size); */
/* DBG("Message Size: %li", sizeof(msg)); */
/* DBG("Message Len: %d", (int)msg.msg_controllen); */
/* DBG("Control Size: %li", sizeof(cmsg)); */
/* DBG("Control Len: %d", (int)cmsg->cmsg_len); */
errno = 0;
size = sendmsg(_ecore_drm_sockets[1], &msg, MSG_NOSIGNAL);
if (errno != 0)
{
DBG("Error Sending Message: %m");
}
DBG("Sent %d bytes to Socket %d", (int)size, _ecore_drm_sockets[1]);
return size;
}
static int
_ecore_drm_socket_receive(int opcode, int fd, void **data, size_t bytes EINA_UNUSED)
{
int ret = -1, rfd;
Ecore_Drm_Message dmsg;
struct cmsghdr *cmsg;
struct CRED_STRUCT *creds;
struct iovec iov[2];
struct msghdr msg;
char buff[BUFSIZ];
ssize_t size;
#if defined(CRED_OPT)
const int on = 1;
if (setsockopt(fd, SOL_SOCKET, CRED_OPT, &on, sizeof(int)) < 0)
{
ERR("Failed to set socket option: %m");
return -1;
}
#endif
IOVSET(iov + 0, &dmsg, sizeof(dmsg));
IOVSET(iov + 1, &buff, sizeof(buff));
msg.msg_iov = iov;
msg.msg_iovlen = 2;
msg.msg_name = NULL;
msg.msg_namelen = 0;
if ((!cmsgptr) && (!(cmsgptr = malloc(CONTROL_LEN))))
return -1;
msg.msg_control = cmsgptr;
msg.msg_controllen = CONTROL_LEN;
errno = 0;
size = recvmsg(fd, &msg, 0);
if (errno != 0)
{
ERR("Failed to receive message: %m");
return -1;
}
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg))
{
if (cmsg->cmsg_level != SOL_SOCKET)
continue;
switch (cmsg->cmsg_type)
{
case SCM_RIGHTS:
rfd = *((int *)CMSG_DATA(cmsg));
break;
case SCM_CREDTYPE:
creds = (struct CRED_STRUCT *)CMSG_DATA(cmsg);
/* uid = creds->CR_UID; */
break;
}
}
DBG("Received FD: %d", rfd);
return size;
}
void
_ecore_drm_message_send(int opcode, void *data, size_t bytes)
{
_ecore_drm_socket_send(opcode, _ecore_drm_socket_fd[1], data, bytes);
_ecore_drm_socket_send(opcode, _ecore_drm_sockets[1], data, bytes);
}
Eina_Bool
@ -234,7 +258,7 @@ _ecore_drm_message_receive(int opcode, void **data, size_t bytes)
{
int ret;
ret = _ecore_drm_socket_receive(opcode, _ecore_drm_socket_fd[0],
ret = _ecore_drm_socket_receive(opcode, _ecore_drm_sockets[0],
data, bytes);
if (ret < 0) return EINA_FALSE;
@ -264,11 +288,16 @@ ecore_drm_init(void)
/* try to init eina */
if (!eina_init()) return --_ecore_drm_init_count;
if (!ecore_init())
{
eina_shutdown();
return --_ecore_drm_init_count;
}
/* set logging level */
eina_log_level_set(EINA_LOG_LEVEL_DBG);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
/* optionally log output to a file */
#ifdef LOG_TO_FILE
@ -304,26 +333,17 @@ ecore_drm_init(void)
if (!eina_log_domain_level_check(_ecore_drm_log_dom, EINA_LOG_LEVEL_DBG))
eina_log_domain_level_set("ecore_drm", EINA_LOG_LEVEL_DBG);
/* try to create socketpair */
if (!_ecore_drm_socket_create())
{
ERR("Could not create socketpair");
goto sock_err;
}
/* try to create the socketpair */
if (!_ecore_drm_sockets_create())
goto sock_err;
/* create udev connection for later device handling */
if (!(udev = udev_new()))
{
ERR("Could not create udev handle: %m");
goto udev_err;
}
/* try to run SPARTACUS !! */
/* try to run Spartacus */
if (!_ecore_drm_launcher_spawn())
{
ERR("Could not spawn Spartacus !!");
goto spawn_err;
}
goto spawn_err;
/* try to init udev */
if (!(udev = udev_new()))
goto udev_err;
/* return init count */
return _ecore_drm_init_count;
@ -331,9 +351,10 @@ ecore_drm_init(void)
spawn_err:
if (udev) udev_unref(udev);
udev_err:
close(_ecore_drm_socket_fd[0]);
close(_ecore_drm_socket_fd[1]);
close(_ecore_drm_sockets[0]);
close(_ecore_drm_sockets[1]);
sock_err:
ecore_shutdown();
eina_log_domain_unregister(_ecore_drm_log_dom);
_ecore_drm_log_dom = -1;
log_err:
@ -365,8 +386,10 @@ ecore_drm_shutdown(void)
if (udev) udev_unref(udev);
/* close sockets */
close(_ecore_drm_socket_fd[0]);
close(_ecore_drm_socket_fd[1]);
close(_ecore_drm_sockets[0]);
close(_ecore_drm_sockets[1]);
ecore_shutdown();
/* unregsiter log domain */
eina_log_domain_unregister(_ecore_drm_log_dom);