1 /*
2  * Copyright © 2017 Keith Packard
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that copyright
7  * notice and this permission notice appear in supporting documentation, and
8  * that the name of the copyright holders not be used in advertising or
9  * publicity pertaining to distribution of the software without specific,
10  * written prior permission.  The copyright holders make no representations
11  * about the suitability of this software for any purpose.  It is provided "as
12  * is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20  * OF THIS SOFTWARE.
21  */
22 
23 #include "randrstr.h"
24 #include "swaprep.h"
25 #include <unistd.h>
26 
27 RESTYPE RRLeaseType;
28 
29 /*
30  * Notify of some lease change
31  */
32 void
RRDeliverLeaseEvent(ClientPtr client,WindowPtr window)33 RRDeliverLeaseEvent(ClientPtr client, WindowPtr window)
34 {
35     ScreenPtr screen = window->drawable.pScreen;
36     rrScrPrivPtr scr_priv = rrGetScrPriv(screen);
37     RRLeasePtr lease;
38 
39     UpdateCurrentTimeIf();
40     xorg_list_for_each_entry(lease, &scr_priv->leases, list) {
41         if (lease->id != None && (lease->state == RRLeaseCreating ||
42                                   lease->state == RRLeaseTerminating))
43         {
44             xRRLeaseNotifyEvent le = (xRRLeaseNotifyEvent) {
45                 .type = RRNotify + RREventBase,
46                 .subCode = RRNotify_Lease,
47                 .timestamp = currentTime.milliseconds,
48                 .window = window->drawable.id,
49                 .lease = lease->id,
50                 .created = lease->state == RRLeaseCreating,
51             };
52             WriteEventsToClient(client, 1, (xEvent *) &le);
53         }
54     }
55 }
56 
57 /*
58  * Change the state of a lease and let anyone watching leases know
59  */
60 static void
RRLeaseChangeState(RRLeasePtr lease,RRLeaseState old,RRLeaseState new)61 RRLeaseChangeState(RRLeasePtr lease, RRLeaseState old, RRLeaseState new)
62 {
63     ScreenPtr screen = lease->screen;
64     rrScrPrivPtr scr_priv = rrGetScrPriv(screen);
65 
66     lease->state = old;
67     scr_priv->leasesChanged = TRUE;
68     RRSetChanged(lease->screen);
69     RRTellChanged(lease->screen);
70     scr_priv->leasesChanged = FALSE;
71     lease->state = new;
72 }
73 
74 /*
75  * Allocate and initialize a lease
76  */
77 static RRLeasePtr
RRLeaseAlloc(ScreenPtr screen,RRLease lid,int numCrtcs,int numOutputs)78 RRLeaseAlloc(ScreenPtr screen, RRLease lid, int numCrtcs, int numOutputs)
79 {
80     RRLeasePtr lease;
81     lease = calloc(1,
82                    sizeof(RRLeaseRec) +
83                    numCrtcs * sizeof (RRCrtcPtr) +
84                    numOutputs * sizeof(RROutputPtr));
85     if (!lease)
86         return NULL;
87     lease->screen = screen;
88     xorg_list_init(&lease->list);
89     lease->id = lid;
90     lease->state = RRLeaseCreating;
91     lease->numCrtcs = numCrtcs;
92     lease->numOutputs = numOutputs;
93     lease->crtcs = (RRCrtcPtr *) (lease + 1);
94     lease->outputs = (RROutputPtr *) (lease->crtcs + numCrtcs);
95     return lease;
96 }
97 
98 /*
99  * Check if a crtc is leased
100  */
101 Bool
RRCrtcIsLeased(RRCrtcPtr crtc)102 RRCrtcIsLeased(RRCrtcPtr crtc)
103 {
104     ScreenPtr screen = crtc->pScreen;
105     rrScrPrivPtr scr_priv = rrGetScrPriv(screen);
106     RRLeasePtr lease;
107     int c;
108 
109     xorg_list_for_each_entry(lease, &scr_priv->leases, list) {
110         for (c = 0; c < lease->numCrtcs; c++)
111             if (lease->crtcs[c] == crtc)
112                 return TRUE;
113     }
114     return FALSE;
115 }
116 
117 /*
118  * Check if an output is leased
119  */
120 Bool
RROutputIsLeased(RROutputPtr output)121 RROutputIsLeased(RROutputPtr output)
122 {
123     ScreenPtr screen = output->pScreen;
124     rrScrPrivPtr scr_priv = rrGetScrPriv(screen);
125     RRLeasePtr lease;
126     int o;
127 
128     xorg_list_for_each_entry(lease, &scr_priv->leases, list) {
129         for (o = 0; o < lease->numOutputs; o++)
130             if (lease->outputs[o] == output)
131                 return TRUE;
132     }
133     return FALSE;
134 }
135 
136 /*
137  * A lease has been terminated.
138  * The driver is responsible for noticing and
139  * calling this function when that happens
140  */
141 
142 void
RRLeaseTerminated(RRLeasePtr lease)143 RRLeaseTerminated(RRLeasePtr lease)
144 {
145     /* Notify clients with events, but only if this isn't during lease creation */
146     if (lease->state == RRLeaseRunning)
147         RRLeaseChangeState(lease, RRLeaseTerminating, RRLeaseTerminating);
148 
149     if (lease->id != None)
150         FreeResource(lease->id, RT_NONE);
151 
152     xorg_list_del(&lease->list);
153 }
154 
155 /*
156  * A lease is completely shut down and is
157  * ready to be deallocated
158  */
159 
160 void
RRLeaseFree(RRLeasePtr lease)161 RRLeaseFree(RRLeasePtr lease)
162 {
163     free(lease);
164 }
165 
166 /*
167  * Ask the driver to terminate a lease. The
168  * driver will call RRLeaseTerminated when that has
169  * finished, which may be some time after this function returns
170  * if the driver operation is asynchronous
171  */
172 void
RRTerminateLease(RRLeasePtr lease)173 RRTerminateLease(RRLeasePtr lease)
174 {
175     ScreenPtr screen = lease->screen;
176     rrScrPrivPtr scr_priv = rrGetScrPriv(screen);
177 
178     scr_priv->rrTerminateLease(screen, lease);
179 }
180 
181 /*
182  * Destroy a lease resource ID. All this
183  * does is note that the lease no longer has an ID, and
184  * so doesn't appear over the protocol anymore.
185  */
186 static int
RRLeaseDestroyResource(void * value,XID pid)187 RRLeaseDestroyResource(void *value, XID pid)
188 {
189     RRLeasePtr lease = value;
190 
191     lease->id = None;
192     return 1;
193 }
194 
195 /*
196  * Create the lease resource type during server initialization
197  */
198 Bool
RRLeaseInit(void)199 RRLeaseInit(void)
200 {
201     RRLeaseType = CreateNewResourceType(RRLeaseDestroyResource, "LEASE");
202     if (!RRLeaseType)
203         return FALSE;
204     return TRUE;
205 }
206 
207 int
ProcRRCreateLease(ClientPtr client)208 ProcRRCreateLease(ClientPtr client)
209 {
210     REQUEST(xRRCreateLeaseReq);
211     xRRCreateLeaseReply rep;
212     WindowPtr window;
213     ScreenPtr screen;
214     rrScrPrivPtr scr_priv;
215     RRLeasePtr lease;
216     RRCrtc *crtcIds;
217     RROutput *outputIds;
218     int fd;
219     int rc;
220     unsigned long len;
221     int c, o;
222 
223     REQUEST_AT_LEAST_SIZE(xRRCreateLeaseReq);
224 
225     LEGAL_NEW_RESOURCE(stuff->lid, client);
226 
227     rc = dixLookupWindow(&window, stuff->window, client, DixGetAttrAccess);
228     if (rc != Success)
229         return rc;
230 
231     len = client->req_len - bytes_to_int32(sizeof(xRRCreateLeaseReq));
232 
233     if (len != stuff->nCrtcs + stuff->nOutputs)
234         return BadLength;
235 
236     screen = window->drawable.pScreen;
237     scr_priv = rrGetScrPriv(screen);
238 
239     if (!scr_priv)
240         return BadMatch;
241 
242     if (!scr_priv->rrCreateLease)
243         return BadMatch;
244 
245     /* Allocate a structure to hold all of the lease information */
246 
247     lease = RRLeaseAlloc(screen, stuff->lid, stuff->nCrtcs, stuff->nOutputs);
248     if (!lease)
249         return BadAlloc;
250 
251     /* Look up all of the crtcs */
252     crtcIds = (RRCrtc *) (stuff + 1);
253     for (c = 0; c < stuff->nCrtcs; c++) {
254         RRCrtcPtr crtc;
255 
256 	rc = dixLookupResourceByType((void **)&crtc, crtcIds[c],
257                                      RRCrtcType, client, DixSetAttrAccess);
258 
259         if (rc != Success) {
260             client->errorValue = crtcIds[c];
261             goto bail_lease;
262         }
263 
264         if (RRCrtcIsLeased(crtc)) {
265             client->errorValue = crtcIds[c];
266             rc = BadAccess;
267             goto bail_lease;
268         }
269 
270         lease->crtcs[c] = crtc;
271     }
272 
273     /* Look up all of the outputs */
274     outputIds = (RROutput *) (crtcIds + stuff->nCrtcs);
275     for (o = 0; o < stuff->nOutputs; o++) {
276         RROutputPtr output;
277 
278 	rc = dixLookupResourceByType((void **)&output, outputIds[o],
279                                      RROutputType, client, DixSetAttrAccess);
280         if (rc != Success) {
281             client->errorValue = outputIds[o];
282             goto bail_lease;
283         }
284 
285         if (RROutputIsLeased(output)) {
286             client->errorValue = outputIds[o];
287             rc = BadAccess;
288             goto bail_lease;
289         }
290 
291         lease->outputs[o] = output;
292     }
293 
294     rc = scr_priv->rrCreateLease(screen, lease, &fd);
295     if (rc != Success)
296         goto bail_lease;
297 
298     xorg_list_add(&scr_priv->leases, &lease->list);
299 
300     if (!AddResource(stuff->lid, RRLeaseType, lease)) {
301         close(fd);
302         return BadAlloc;
303     }
304 
305     if (WriteFdToClient(client, fd, TRUE) < 0) {
306         RRTerminateLease(lease);
307         close(fd);
308         return BadAlloc;
309     }
310 
311     RRLeaseChangeState(lease, RRLeaseCreating, RRLeaseRunning);
312 
313     rep = (xRRCreateLeaseReply) {
314         .type = X_Reply,
315         .nfd = 1,
316         .sequenceNumber = client->sequence,
317         .length = 0,
318     };
319 
320     if (client->swapped) {
321         swaps(&rep.sequenceNumber);
322         swapl(&rep.length);
323     }
324 
325     WriteToClient(client, sizeof (rep), &rep);
326 
327     return Success;
328 
329 bail_lease:
330     free(lease);
331     return rc;
332 }
333 
334 int
ProcRRFreeLease(ClientPtr client)335 ProcRRFreeLease(ClientPtr client)
336 {
337     REQUEST(xRRFreeLeaseReq);
338     RRLeasePtr lease;
339 
340     REQUEST_SIZE_MATCH(xRRFreeLeaseReq);
341 
342     VERIFY_RR_LEASE(stuff->lid, lease, DixDestroyAccess);
343 
344     if (stuff->terminate)
345         RRTerminateLease(lease);
346     else
347         /* Get rid of the resource database entry */
348         FreeResource(stuff->lid, RT_NONE);
349 
350     return Success;
351 }
352