aboutsummaryrefslogtreecommitdiffstats
path: root/libgamma_linux_drm_partition_initialise.c
blob: 544036554fb6b1ddf2711c919b92c19cfebb86e0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/* 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;
}