aboutsummaryrefslogblamecommitdiffstats
path: root/libgamma_linux_drm_partition_initialise.c
blob: 544036554fb6b1ddf2711c919b92c19cfebb86e0 (plain) (tree)
1
2
                                                         
                             































                                                                           
                                                                
                                                                





                                                                                         

                                                                                           


                                                     
                                            
                                                                    








                                                               
                                          




                                                                    
                                                                       



                                                              

                                                                         










                                                                                                
           












                                                                                

                                                                                                    

                   
                                                     









                                                        
                                     







                                                    
                                                                                         































                                                                          
/* See LICENSE file for copyright and license details. */
#define IN_LIBGAMMA_LINUX_DRM
#include "common.h"


/**
 * Figure out why `open` failed for a graphics card
 * 
 * @param   pathname  The pathname of the error card
 * @return            The error code to report
 */
static int
figure_out_card_open_error(const char *pathname)
{
	char buf[1024]; /* My output of `sysconf(_SC_GETGR_R_SIZE_MAX)`. */
	gid_t supplemental_groups[NGROUPS_MAX];
	struct group *group, _grp;
	struct stat attr;
	int i, n;


	/* Check which the device exists */
	if (errno == ENXIO || errno == ENODEV)
		return LIBGAMMA_NO_SUCH_PARTITION;


	/* If we did not get access permission, figure out why */

	if (errno != EACCES) {
		/* If we could not figure out what went
		 * wrong, just return the error we got */
		return LIBGAMMA_ERRNO_SET;
	}

	/* TODO Can this be simplified? Is this even correct? */
#define TEST(R, W) ((attr.st_mode & ((R) | (W))) == ((R) | (W)))

	/* Get permission requirement for the file */
	if (stat(pathname, &attr))
		return errno == EACCES ? LIBGAMMA_NO_SUCH_PARTITION : LIBGAMMA_ERRNO_SET;

	/* Test owner's, group's and others' permissions */
	if ((attr.st_uid == geteuid() && TEST(S_IRUSR, S_IWUSR)) ||
	    (attr.st_gid == getegid() && TEST(S_IRGRP, S_IWGRP)) || TEST(S_IROTH, S_IWOTH))
		return LIBGAMMA_DEVICE_ACCESS_FAILED;

	/* The group should be "video", but perhaps
	 * it is "root" to restrict users */
	if (!attr.st_gid /* root group */ || TEST(S_IRGRP, S_IWGRP))
		return LIBGAMMA_DEVICE_RESTRICTED;


	/* Get the user's supplemental group membership list */
	n = getgroups(NGROUPS_MAX, supplemental_groups);
	if (n < 0)
		return LIBGAMMA_ERRNO_SET;

	/* Test whether any of the supplemental
	 * group should be satisfactory */
	for (i = 0; i < n; i++)
		if (supplemental_groups[i] == attr.st_gid)
			break;

	/* If one of the supplemental groups should be satisfactory,
	 * then we do not know anything more than that access failed */
	if (i != n)
		return LIBGAMMA_DEVICE_ACCESS_FAILED;

	/* Otherwise, try to get the name of the group that is
	 * required and report the missing group membership */ 
	errno = getgrgid_r(attr.st_gid, &_grp, buf, sizeof(buf), &group);
	if (errno == ERANGE) {
		/* The lenght of the group's name is absurdly long, degrade to thread-unsafe. */
		errno = 0;
		group = getgrgid(attr.st_gid);
	} else if (errno) {
		return LIBGAMMA_ERRNO_SET;
	}

	libgamma_group_gid_set(attr.st_gid);
	libgamma_group_name_set(group ? group->gr_name : NULL);
	return LIBGAMMA_DEVICE_REQUIRE_GROUP;
#undef TEST
}


/**
 * Initialise an allocated partition state
 * 
 * @param   this       The partition state to initialise
 * @param   site       The site state for the site that the partition belongs to
 * @param   partition  The the index of the partition within the site
 * @return             Zero on success, otherwise (negative) the value of an
 *                     error identifier provided by this library
 */
int
libgamma_linux_drm_partition_initialise(struct libgamma_partition_state *restrict this,
                                        struct libgamma_site_state *restrict site, size_t partition)
{
	int rc = 0;
	struct libgamma_drm_card_data *restrict data;
	char pathname[PATH_MAX];

	(void) site;

	/* Check for partition index overflow */
	if (partition > INT_MAX)
		return LIBGAMMA_NO_SUCH_PARTITION;

	/* Allocate and initialise graphics card data */
	this->data = NULL;
	data = malloc(sizeof(*data));
	if (!data)
		return LIBGAMMA_ERRNO_SET;
	data->fd = -1;
	data->res = NULL;
	data->encoders = NULL;
	data->connectors = NULL;
  
	/* Get the pathname for the graphics card */
	snprintf(pathname, sizeof(pathname), DRM_DEV_NAME, DRM_DIR_NAME, (int)partition);

	/* Acquire access to the graphics card */
	data->fd = open(pathname, O_RDWR | O_CLOEXEC);
	if (data->fd < 0) {
		rc = figure_out_card_open_error(pathname);
		goto fail_data;
	}

	/* Acquire mode resources */
	data->res = drmModeGetResources(data->fd);
	if (!data->res) {
		rc = LIBGAMMA_ACQUIRING_MODE_RESOURCES_FAILED;
		goto fail_fd;
	}

	/* Get the number of CRTC:s that are available in the partition */
	if (data->res->count_crtcs < 0) {
		rc = LIBGAMMA_NEGATIVE_CRTC_COUNT;
		goto fail_res;
	}
	this->crtcs_available = (size_t)data->res->count_crtcs;
	this->data = data;
	return 0;

fail_res:
	drmModeFreeResources(data->res);
fail_fd:
	close(data->fd);
fail_data:
	free(data);
	return rc;
}