forked from enlightenment/efl
430 lines
10 KiB
C
430 lines
10 KiB
C
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include "ecore_drm_private.h"
|
|
#include <sys/wait.h>
|
|
#include <sys/socket.h>
|
|
#include <signal.h>
|
|
|
|
#define RIGHTS_LEN CMSG_LEN(sizeof(int))
|
|
|
|
#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_sockets[2] = { -1, -1 };
|
|
static struct cmsghdr *cmsgptr = NULL;
|
|
|
|
/* external variables */
|
|
struct udev *udev;
|
|
int _ecore_drm_log_dom = -1;
|
|
#ifdef LOG_TO_FILE
|
|
FILE *lg;
|
|
#endif
|
|
|
|
static Eina_Bool
|
|
_ecore_drm_sockets_create(void)
|
|
{
|
|
if (_ecore_drm_sockets[0] > -1) return EINA_TRUE;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_ecore_drm_launcher_spawn(void)
|
|
{
|
|
pid_t pid;
|
|
|
|
if ((pid = fork()) < 0) return EINA_FALSE;
|
|
|
|
if (pid == 0)
|
|
{
|
|
char renv[64], wenv[64], buff[PATH_MAX];
|
|
char *args[1] = { NULL };
|
|
sigset_t mask;
|
|
|
|
/* read socket for slave is 1 */
|
|
snprintf(renv, sizeof(renv), "ECORE_DRM_LAUNCHER_SOCKET_READ=%d",
|
|
_ecore_drm_sockets[1]);
|
|
|
|
/* write socket for slave is 0 */
|
|
snprintf(wenv, sizeof(wenv), "ECORE_DRM_LAUNCHER_SOCKET_WRITE=%d",
|
|
_ecore_drm_sockets[0]);
|
|
|
|
/* assemble exec path */
|
|
snprintf(buff, sizeof(buff),
|
|
"%s/ecore_drm/bin/%s/ecore_drm_launch",
|
|
PACKAGE_LIB_DIR, MODULE_ARCH);
|
|
|
|
/* don't give our signal mask to the child */
|
|
sigemptyset(&mask);
|
|
sigaddset(&mask, SIGTERM);
|
|
sigaddset(&mask, SIGCHLD);
|
|
sigaddset(&mask, SIGINT);
|
|
sigaddset(&mask, SIGTTIN);
|
|
sigaddset(&mask, SIGTTOU);
|
|
sigprocmask(SIG_UNBLOCK, &mask, NULL);
|
|
|
|
/* 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;
|
|
|
|
while (waitpid(pid, &status, WNOHANG) < 0)
|
|
if (errno != EINTR) break;
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
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;
|
|
ssize_t size;
|
|
int *iptr;
|
|
|
|
/* 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 */
|
|
|
|
memset(&dmsg, 0, sizeof(dmsg));
|
|
|
|
IOVSET(iov + 0, &dmsg, sizeof(dmsg));
|
|
IOVSET(iov + 1, &data, bytes);
|
|
|
|
dmsg.opcode = opcode;
|
|
dmsg.size = bytes;
|
|
|
|
msg.msg_name = NULL;
|
|
msg.msg_namelen = 0;
|
|
msg.msg_iov = iov;
|
|
msg.msg_iovlen = 2;
|
|
msg.msg_flags = 0;
|
|
|
|
if ((!cmsgptr) && (!(cmsgptr = malloc(RIGHTS_LEN))))
|
|
return -1;
|
|
|
|
cmsgptr->cmsg_level = SOL_SOCKET;
|
|
cmsgptr->cmsg_type = SCM_RIGHTS;
|
|
cmsgptr->cmsg_len = RIGHTS_LEN;
|
|
|
|
msg.msg_control = cmsgptr;
|
|
msg.msg_controllen = RIGHTS_LEN;
|
|
|
|
iptr = (int *)(CMSG_DATA(cmsgptr));
|
|
if (fd > -1) *iptr = fd;
|
|
else *iptr = _ecore_drm_sockets[1];
|
|
|
|
errno = 0;
|
|
size = sendmsg(_ecore_drm_sockets[1], &msg, MSG_EOR);
|
|
if (errno != 0)
|
|
{
|
|
DBG("Error Sending Message: %m");
|
|
}
|
|
|
|
/* DBG("Sent %li bytes to Socket %d", size, _ecore_drm_sockets[1]); */
|
|
|
|
return size;
|
|
}
|
|
|
|
static int
|
|
_ecore_drm_socket_receive(int opcode EINA_UNUSED, int *fd, void **data, size_t bytes)
|
|
{
|
|
int ret = ECORE_DRM_OP_FAILURE;
|
|
Ecore_Drm_Message dmsg;
|
|
struct cmsghdr *cmsg;
|
|
struct iovec iov[2];
|
|
struct msghdr msg;
|
|
char buff[CMSG_SPACE(sizeof(fd))];
|
|
int *iptr;
|
|
/* ssize_t size; */
|
|
|
|
memset(&dmsg, 0, sizeof(dmsg));
|
|
memset(&buff, 0, sizeof(buff));
|
|
|
|
IOVSET(iov + 0, &dmsg, sizeof(dmsg));
|
|
IOVSET(iov + 1, &buff, sizeof(buff));
|
|
|
|
msg.msg_name = NULL;
|
|
msg.msg_namelen = 0;
|
|
msg.msg_iov = iov;
|
|
msg.msg_iovlen = 2;
|
|
msg.msg_flags = 0;
|
|
|
|
if ((!cmsgptr) && (!(cmsgptr = malloc(RIGHTS_LEN))))
|
|
return -1;
|
|
|
|
msg.msg_control = cmsgptr;
|
|
msg.msg_controllen = RIGHTS_LEN;
|
|
|
|
errno = 0;
|
|
recvmsg(_ecore_drm_sockets[0], &msg, 0);
|
|
if (errno != 0)
|
|
{
|
|
ERR("Failed to receive message: %m");
|
|
return -1;
|
|
}
|
|
|
|
/* DBG("Received %li bytes from %d", size, _ecore_drm_sockets[0]); */
|
|
|
|
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:
|
|
iptr = (int *)(CMSG_DATA(cmsg));
|
|
if (fd) *fd = *iptr;
|
|
switch (dmsg.opcode)
|
|
{
|
|
case ECORE_DRM_OP_DEVICE_OPEN:
|
|
case ECORE_DRM_OP_DEVICE_CLOSE:
|
|
case ECORE_DRM_OP_TTY_OPEN:
|
|
case ECORE_DRM_OP_DEVICE_MASTER_DROP:
|
|
case ECORE_DRM_OP_DEVICE_MASTER_SET:
|
|
if ((fd) && (*fd >= 0)) ret = ECORE_DRM_OP_SUCCESS;
|
|
if (data) memcpy(*data, buff, bytes);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
_ecore_drm_message_send(int opcode, int fd, void *data, size_t bytes)
|
|
{
|
|
_ecore_drm_socket_send(opcode, fd, data, bytes);
|
|
}
|
|
|
|
Eina_Bool
|
|
_ecore_drm_message_receive(int opcode, int *fd, void **data, size_t bytes)
|
|
{
|
|
int ret = ECORE_DRM_OP_FAILURE;
|
|
|
|
ret = _ecore_drm_socket_receive(opcode, fd, data, bytes);
|
|
if (ret != ECORE_DRM_OP_SUCCESS) return EINA_FALSE;
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
/**
|
|
* @defgroup Ecore_Drm_Init_Group Drm Library Init and Shutdown Functions
|
|
*
|
|
* Functions that start and shutdown the Ecore_Drm Library.
|
|
*/
|
|
|
|
/**
|
|
* Initialize the Ecore_Drm library
|
|
*
|
|
* @return The number of times the library has been initialized without
|
|
* being shut down. 0 is returned if an error occurs.
|
|
*
|
|
* @ingroup Ecore_Drm_Init_Group
|
|
*/
|
|
EAPI int
|
|
ecore_drm_init(void)
|
|
{
|
|
/* if we have already initialized, return the count */
|
|
if (++_ecore_drm_init_count != 1) return _ecore_drm_init_count;
|
|
|
|
/* try to init eina */
|
|
if (!eina_init()) return --_ecore_drm_init_count;
|
|
|
|
/* try to init ecore */
|
|
if (!ecore_init())
|
|
{
|
|
eina_shutdown();
|
|
return --_ecore_drm_init_count;
|
|
}
|
|
|
|
/* try to init ecore_event */
|
|
if (!ecore_event_init())
|
|
{
|
|
ecore_shutdown();
|
|
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
|
|
int log_fd;
|
|
char log_path[PATH_MAX];
|
|
mode_t um;
|
|
|
|
/* assemble logging file path */
|
|
strcpy(log_path, "/tmp/ecore_drm_XXXXXX");
|
|
|
|
/* create temporary logging file */
|
|
um = umask(S_IRWXG | S_IRWXO);
|
|
log_fd = mkstemp(log_path);
|
|
umask(um);
|
|
|
|
/* try to open logging file */
|
|
if (!(lg = fdopen(log_fd, "w")))
|
|
goto log_err;
|
|
|
|
eina_log_print_cb_set(eina_log_print_cb_file, lg);
|
|
#endif
|
|
|
|
/* try to create logging domain */
|
|
_ecore_drm_log_dom =
|
|
eina_log_domain_register("ecore_drm", ECORE_DRM_DEFAULT_LOG_COLOR);
|
|
if (!_ecore_drm_log_dom)
|
|
{
|
|
EINA_LOG_ERR("Could not create log domain for Ecore_Drm");
|
|
goto log_err;
|
|
}
|
|
|
|
/* set default logging level for this domain */
|
|
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 init udev */
|
|
if (!(udev = udev_new()))
|
|
goto udev_err;
|
|
|
|
/* try to create the socketpair */
|
|
if (!_ecore_drm_sockets_create())
|
|
goto sock_err;
|
|
|
|
/* try to run Spartacus */
|
|
if (!_ecore_drm_launcher_spawn())
|
|
goto spawn_err;
|
|
|
|
/* return init count */
|
|
return _ecore_drm_init_count;
|
|
|
|
spawn_err:
|
|
close(_ecore_drm_sockets[0]);
|
|
close(_ecore_drm_sockets[1]);
|
|
sock_err:
|
|
if (udev) udev_unref(udev);
|
|
udev_err:
|
|
ecore_shutdown();
|
|
eina_log_domain_unregister(_ecore_drm_log_dom);
|
|
_ecore_drm_log_dom = -1;
|
|
log_err:
|
|
#ifdef LOG_TO_FILE
|
|
if (lg) fclose(lg);
|
|
#endif
|
|
|
|
/* shutdown eina */
|
|
eina_shutdown();
|
|
|
|
return --_ecore_drm_init_count;
|
|
}
|
|
|
|
/**
|
|
* Shutdown the Ecore_Drm library.
|
|
*
|
|
* @return The number of times the library has been initialized without
|
|
* being shutdown. 0 is returned if an error occurs.
|
|
*
|
|
* @ingroup Ecore_Drm_Init_Group
|
|
*/
|
|
EAPI int
|
|
ecore_drm_shutdown(void)
|
|
{
|
|
/* if we are still in use, decrement init count and get out */
|
|
if (--_ecore_drm_init_count != 0) return _ecore_drm_init_count;
|
|
|
|
/* close udev handle */
|
|
if (udev) udev_unref(udev);
|
|
|
|
/* close sockets */
|
|
close(_ecore_drm_sockets[0]);
|
|
close(_ecore_drm_sockets[1]);
|
|
|
|
/* shutdown ecore_event */
|
|
ecore_event_shutdown();
|
|
|
|
/* shutdown ecore */
|
|
ecore_shutdown();
|
|
|
|
/* unregsiter log domain */
|
|
eina_log_domain_unregister(_ecore_drm_log_dom);
|
|
_ecore_drm_log_dom = -1;
|
|
|
|
#ifdef LOG_TO_FILE
|
|
if (lg) fclose(lg);
|
|
#endif
|
|
|
|
/* shutdown eina */
|
|
eina_shutdown();
|
|
|
|
/* return init count */
|
|
return _ecore_drm_init_count;
|
|
}
|
|
|
|
EAPI void *
|
|
ecore_drm_gbm_get(Ecore_Drm_Device *dev)
|
|
{
|
|
if (!dev) return NULL;
|
|
|
|
#ifdef HAVE_GBM
|
|
return dev->gbm;
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
|
|
EAPI unsigned int
|
|
ecore_drm_gbm_format_get(Ecore_Drm_Device *dev)
|
|
{
|
|
if (!dev) return 0;
|
|
return dev->format;
|
|
}
|