xserver.cpp 18 KB
Newer Older
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
/*******************************************************************************
 *
 * X testing environment - Google Test helper class to communicate with the
 * server
 *
 * Copyright (C) 2012 Red Hat, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 ******************************************************************************/

#include "xorg/gtest/xorg-gtest-xserver.h"
30
#include "defines.h"
31

Peter Hutterer's avatar
Peter Hutterer committed
32
#include <sys/prctl.h>
33
34
35
36
37
38
39
40
41
42
43
44
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include <algorithm>
#include <cerrno>
#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <stdexcept>
#include <vector>
45
#include <map>
46
#include <fstream>
47

48
#include <X11/Xlib.h>
49
#include <X11/Xlibint.h>
50
51
52
53
54

// undef macros with frequently-used names which are defined in Xlibint.h
#undef min
#undef max

55
56
57
#include <X11/extensions/XInput2.h>

struct xorg::testing::XServer::Private {
58
59
60
61
62
  Private()
      : display_number(DEFAULT_DISPLAY),
        path_to_server(DEFAULT_XORG_SERVER) {
  }

63
64
  unsigned int display_number;
  std::string display_string;
65
66
  std::string path_to_server;
  std::map<std::string, std::string> options;
67
  std::string version;
68
69
70
71
72
73
74
};

xorg::testing::XServer::XServer() : d_(new Private) {
  d_->display_number = DEFAULT_DISPLAY;
  SetDisplayNumber(d_->display_number);
}

75
76
77
78
79
80
xorg::testing::XServer::~XServer() {
  if (Pid() > 0)
    if (!Terminate(3000))
      Kill(300);
}

81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
void xorg::testing::XServer::SetDisplayNumber(unsigned int display_number) {
    d_->display_number = display_number;

    std::stringstream s;
    s << ":" << display_number;
    d_->display_string = s.str();
}

unsigned int xorg::testing::XServer::GetDisplayNumber(void) {
  return d_->display_number;
}

const std::string& xorg::testing::XServer::GetDisplayString(void) {
  return d_->display_string;
}

97
98
99
100
void xorg::testing::XServer::SetServerPath(const std::string &path_to_server) {
  d_->path_to_server = path_to_server;
}

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
bool xorg::testing::XServer::WaitForEvent(::Display *display, time_t timeout)
{
    fd_set fds;
    FD_ZERO(&fds);

    int display_fd = ConnectionNumber(display);

    XSync(display, False);

    if (XPending(display))
        return true;
    else {
        FD_SET(display_fd, &fds);

        struct timeval timeval = {
            static_cast<time_t>(timeout / 1000),
117
            static_cast<time_t>(timeout % 1000) * 1000,
118
119
120
121
        };

        int ret;
        if (timeout)
122
            ret = select(display_fd + 1, &fds, nullptr, nullptr, &timeval);
123
        else
124
            ret = select(display_fd + 1, &fds, nullptr, nullptr, nullptr);
125
126
127
128
129
130
131
132
133
134
135
136
137
138

        if (ret < 0)
            throw std::runtime_error("Failed to select on X fd");

        if (ret == 0)
            return false;

        return XPending(display);
    }
}

bool xorg::testing::XServer::WaitForEventOfType(::Display *display, int type, int extension,
                                                int evtype, time_t timeout)
{
139
    while (WaitForEvent(display, timeout)) {
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
        XEvent event;
        if (!XPeekEvent(display, &event))
            throw std::runtime_error("Failed to peek X event");

        if (event.type != type) {
            if (XNextEvent(display, &event) != Success)
                throw std::runtime_error("Failed to remove X event");
            continue;
        }

        if (event.type != GenericEvent || extension == -1)
            return true;

        XGenericEvent *generic_event = reinterpret_cast<XGenericEvent*>(&event);

        if (generic_event->extension != extension) {
            if (XNextEvent(display, &event) != Success)
                throw std::runtime_error("Failed to remove X event");
            continue;
        }

        if (evtype == -1 || generic_event->evtype == evtype)
            return true;

        if (XNextEvent(display, &event) != Success)
            throw std::runtime_error("Failed to remove X event");
    }

    return false;
}

171
172
173
174
175
176
177
178
179
static XIEventMask* set_hierarchy_mask(::Display *display,
                                       int *nmasks_out,
                                       bool *was_set,
                                       bool *was_created)
{
    XIEventMask *masks;
    int nmasks;
    bool mask_toggled = false;
    bool new_mask_created = false;
180
    XIEventMask *all_devices_mask = nullptr;
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218

    masks = XIGetSelectedEvents(display, DefaultRootWindow(display), &nmasks);

    /* masks is in a quirky data format (one chunk of memory). Change into a
       format easier to manipulate. */

    /* extra one, in case we have zero masks or no XIAllDevices mask */
    XIEventMask *new_masks = new XIEventMask[nmasks + 1];
    for (int i = 0; i < nmasks; i++) {
      XIEventMask *m = &new_masks[i];
      *m = masks[i];

      if (masks[i].deviceid == XIAllDevices) {
        all_devices_mask = m;
        if (masks[i].mask_len < XIMaskLen(XI_HierarchyChanged)) {
          m->mask_len = XIMaskLen(XI_HierarchyChanged);
          mask_toggled = true;
        } else
          mask_toggled = !XIMaskIsSet(m->mask, XI_HierarchyChanged);
      }

      m->mask = new unsigned char[m->mask_len]();
      memcpy(m->mask, masks[i].mask, masks[i].mask_len);

      if (mask_toggled && m->deviceid == XIAllDevices)
        XISetMask(m->mask, XI_HierarchyChanged);
    }

    if (!all_devices_mask) {
      all_devices_mask = &new_masks[nmasks++];
      all_devices_mask->deviceid = XIAllDevices;
      all_devices_mask->mask_len = XIMaskLen(XI_HierarchyChanged);
      all_devices_mask->mask = new unsigned char[all_devices_mask->mask_len]();
      XISetMask(all_devices_mask->mask, XI_HierarchyChanged);
      new_mask_created = true;
    }

    XFree(masks);
219
    masks = nullptr;
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253

    if (new_mask_created || mask_toggled) {
      XISelectEvents(display, DefaultRootWindow(display), new_masks, nmasks);
      XFlush(display);
    }

    *was_set = mask_toggled;
    *was_created = new_mask_created;
    *nmasks_out = nmasks;

    return new_masks;
}

static void unset_hierarchy_mask(::Display *display,
                                 XIEventMask *masks, int nmasks,
                                 bool was_set, bool was_created)
{
    if (was_set || was_created) {
      if (was_set) {
        for (int i = 0; i < nmasks; i++) {
          if (masks[i].deviceid == XIAllDevices)
            XIClearMask(masks[i].mask, XI_HierarchyChanged);
        }
      } else if (was_created)
        masks[nmasks - 1].mask_len = 0;
      XISelectEvents(display, DefaultRootWindow(display), masks, nmasks);
      XFlush(display);
    }

    for (int i = 0; i < nmasks; i++)
      delete[] masks[i].mask;
    delete[] masks;
}

254
255
256
257
258
259
bool xorg::testing::XServer::WaitForDevice(::Display *display, const std::string &name,
                                           time_t timeout)
{
    int opcode;
    int event_start;
    int error_start;
260
    bool device_found = false;
261
262
263
264
265

    if (!XQueryExtension(display, "XInputExtension", &opcode, &event_start,
                         &error_start))
        throw std::runtime_error("Failed to query XInput extension");

266
267
268
269
270
    XIEventMask *masks;
    int nmasks;
    bool mask_set, mask_created;
    masks = set_hierarchy_mask(display, &nmasks, &mask_set, &mask_created);

271
272
273
274
275
276
277
278
279
    XIDeviceInfo *info;
    int ndevices;

    info = XIQueryDevice(display, XIAllDevices, &ndevices);
    for (int i = 0; !device_found && i < ndevices; i++) {
      device_found = (name.compare(info[i].name) == 0);
    }
    XIFreeDeviceInfo(info);

280
281
    while (!device_found &&
           WaitForEventOfType(display, GenericEvent, opcode,
282
                              XI_HierarchyChanged, timeout)) {
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
        XEvent event;
        if (XNextEvent(display, &event) != Success)
            throw std::runtime_error("Failed to get X event");

        XGenericEventCookie *xcookie =
            reinterpret_cast<XGenericEventCookie*>(&event.xcookie);
        if (!XGetEventData(display, xcookie))
            throw std::runtime_error("Failed to get X event data");

        XIHierarchyEvent *hierarchy_event =
            reinterpret_cast<XIHierarchyEvent*>(xcookie->data);

        if (!(hierarchy_event->flags & XIDeviceEnabled)) {
            XFreeEventData(display, xcookie);
            continue;
        }

300
        device_found = false;
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
        for (int i = 0; i < hierarchy_event->num_info; i++) {
            if (!(hierarchy_event->info[i].flags & XIDeviceEnabled))
                continue;

            int num_devices;
            XIDeviceInfo *device_info =
                XIQueryDevice(display, hierarchy_event->info[i].deviceid,
                              &num_devices);
            if (num_devices != 1 || !device_info)
                throw std::runtime_error("Failed to query device");

            if (name.compare(device_info[0].name) == 0) {
                device_found = true;
                break;
            }
        }

        XFreeEventData(display, xcookie);

        if (device_found)
321
          break;
322
323
    }

324
325
326
    unset_hierarchy_mask(display, masks, nmasks, mask_set, mask_created);

    return device_found;
327
}
328

329
330
331
void xorg::testing::XServer::WaitForConnections(void) {
}

332
333
334
335
336
337
338
339
340
341
342
void xorg::testing::XServer::TestStartup(void) {
  Display* test_display = XOpenDisplay(GetDisplayString().c_str());
  if (test_display) {
    XCloseDisplay(test_display);
    std::string message;
    message += "A server is already running on ";
    message += GetDisplayString();
    message += ".";
    throw std::runtime_error(message);
  }

343
344
  std::string log = d_->options["-logfile"];

345
346
  /* The Xorg server won't start unless the log file and the old log file are
   * writable. */
347
348
349
350
351
  bool logfile_was_present;
  std::ifstream file_test;
  file_test.open(log.c_str());
  logfile_was_present = file_test.good();

352
  std::ofstream log_test;
353
  log_test.open(log.c_str(), std::ofstream::out);
354
355
  log_test.close();
  if (log_test.fail()) {
356
    throw std::runtime_error("X.org server log file " + log + " is not writable.");
357
358
  } else if (!logfile_was_present)
    unlink(log.c_str());
359

360
361
  std::string old_log_file = log + ".old";

362
363
364
  file_test.open(old_log_file.c_str());
  logfile_was_present = file_test.good();

365
366
367
  log_test.open(old_log_file.c_str(), std::ofstream::out);
  log_test.close();
  if (log_test.fail()) {
368
    throw std::runtime_error("X.org old server log file " + old_log_file + " is not writable.");
369
370
  } else if (!logfile_was_present)
    unlink(old_log_file.c_str());
371
372
}

373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
const std::string& xorg::testing::XServer::GetVersion(void) {
  if (Pid() == -1 || !d_->version.empty())
    return d_->version;

  std::ifstream logfile;
  logfile.open(d_->options["-logfile"].c_str());

  std::string prefix = "X.Org X Server ";

  if (logfile.is_open()) {
    std::string line;
    while (getline(logfile, line)) {
      size_t start = line.find(prefix);
      if (start == line.npos)
        continue;

      line = line.substr(prefix.size());
      /* RCs have the human-readable version after the version */
      size_t end = line.find(" ");
      if (end == line.npos)
        end = line.size();

      d_->version = line.substr(0, end);
      break;
    }
  }

  return d_->version;
}

403
static int _x_error_handler(XORG_GTEST_UNUSED Display *dpy, XErrorEvent *err)
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
{
  std::stringstream error;
  switch(err->error_code) {
    case BadRequest: error << "BadRequest"; break;
    case BadValue: error << "BadValue"; break;
    case BadWindow: error << "BadWindow"; break;
    case BadPixmap: error << "BadPixmap"; break;
    case BadAtom: error << "BadAtom"; break;
    case BadCursor: error << "BadCursor"; break;
    case BadFont: error << "BadFont"; break;
    case BadMatch: error << "BadMatch"; break;
    case BadDrawable: error << "BadDrawable"; break;
    case BadAccess: error << "BadAccess"; break;
    case BadAlloc: error << "BadAlloc"; break;
    case BadColor: error << "BadColor"; break;
    case BadGC: error << "BadGC"; break;
    case BadIDChoice: error << "BadIDChoice"; break;
    case BadName: error << "BadName"; break;
    case BadLength: error << "BadLength"; break;
    case BadImplementation: error << "BadImplementation"; break;
    default:
425
      error << "code: " << err->error_code;
426
427
428
      break;
  }

429
430
431
432
433
  ADD_FAILURE() << "XError received: " << error.str() << ", request "
                << static_cast<int>(err->request_code) << "(" << static_cast<int>(err->minor_code)
                << "), detail: " << err->resourceid
                << "\nThis error handler is likely to be triggered "
                   "more than once.\nCheck the first error for the real error";
434
435
436
437
  return 0;
}


438
static int _x_io_error_handler(Display *dpy) _X_NORETURN;
439
static int _x_io_error_handler(XORG_GTEST_UNUSED Display *dpy)
440
441
442
443
444
445
{
  throw xorg::testing::XIOError("Connection to X Server lost. Possible server crash.");
}

void xorg::testing::XServer::RegisterXIOErrorHandler()
{
446
  XIOErrorHandler old_handler, def_handler;
447

448
  old_handler = XSetIOErrorHandler(nullptr);
449
450
451
452
453
  def_handler = XSetIOErrorHandler(_x_io_error_handler);

  if (old_handler != def_handler &&
      old_handler != _XDefaultIOError &&
      old_handler != _x_io_error_handler)
454
455
456
    XSetIOErrorHandler(old_handler);
}

457
458
void xorg::testing::XServer::RegisterXErrorHandler()
{
459
460
  XErrorHandler old_handler, def_handler;

461
  old_handler = XSetErrorHandler(nullptr);
462
  def_handler = XSetErrorHandler(_x_error_handler);
463

464
465
466
  if (old_handler != def_handler &&
      old_handler != _XDefaultError &&
      old_handler != _x_error_handler)
467
468
469
    XSetErrorHandler(old_handler);
}

470
void xorg::testing::XServer::Start(const std::string &program) {
471
472
  TestStartup();

473
474
  std::vector<std::string> args;
  std::map<std::string, std::string>::iterator it;
475
476
477
  std::string err_msg;

  sigset_t sig_mask;
478
  struct timespec sig_timeout = {3, 0}; /* 3 sec + 0 nsec */
479
480
481
482

  /* add SIGUSR1 to the signal mask */
  sigemptyset(&sig_mask);
  sigaddset(&sig_mask, SIGUSR1);
483
  sigaddset(&sig_mask, SIGCHLD);
484
  if (sigprocmask(SIG_BLOCK, &sig_mask, nullptr)) {
485
486
487
488
    err_msg.append("Failed to set signal mask: ");
    err_msg.append(std::strerror(errno));
    throw std::runtime_error(err_msg);
  }
489

490
491
  pid_t pid = Fork();
  if (pid == 0) {
492
493
494
495
496
#ifdef __linux
    if (getenv("XORG_GTEST_XSERVER_KEEPALIVE"))
      prctl(PR_SET_PDEATHSIG, 0);
#endif

497
498
499
500
501
502
503
504
505
    /* set SIGUSR1 handler to SIG_IGN, XServer tests for this and will
     * send SIGUSR1 when ready */
    sighandler_t old_handler;
    old_handler = signal(SIGUSR1, SIG_IGN);
    if (old_handler == SIG_ERR) {
      err_msg.append("Failed to set signal handler: ");
      err_msg.append(std::strerror(errno));
      throw std::runtime_error(err_msg);
    }
506

507
508
509
510
    /* unblock for the child process so the server receives SIGUSR1, needed
       for VT switching */
    sigemptyset(&sig_mask);
    sigaddset(&sig_mask, SIGUSR1);
511
    if (sigprocmask(SIG_UNBLOCK, &sig_mask, nullptr)) {
512
513
514
515
516
517
      err_msg.append("Failed to unblock signal mask: ");
      err_msg.append(std::strerror(errno));
      throw std::runtime_error(err_msg);
    }

    args.push_back(std::string(GetDisplayString()));
518

519
520
521
522
    for (it = d_->options.begin(); it != d_->options.end(); it++) {
      args.push_back(it->first);
      if (!it->second.empty())
        args.push_back(it->second);
523
    }
524
525
526
527
528
529
530
531
532
533
534
535

    Process::Start(program.empty() ? d_->path_to_server : program, args);
    /* noreturn */

  }

  /* parent */
  char *sleepwait = getenv("XORG_GTEST_XSERVER_SIGSTOP");
  if (sleepwait)
    raise(SIGSTOP);

  /* wait for SIGUSR1 from XServer */
536
  int recv_sig = sigtimedwait(&sig_mask, nullptr, &sig_timeout);
537
538
539
540
541
542
  if (recv_sig == SIGCHLD) {
    GetState();
  } else if (recv_sig != SIGUSR1 && errno != EAGAIN) {
    err_msg.append("Error while waiting for XServer startup: ");
    err_msg.append(std::strerror(errno));
    throw std::runtime_error(err_msg);
543
  }
544
545
546

  sigemptyset(&sig_mask);
  sigaddset(&sig_mask, SIGCHLD);
547
  sigaddset(&sig_mask, SIGUSR1);
548
  sigprocmask(SIG_UNBLOCK, &sig_mask, nullptr);
549

550
551
552
553
  /* Ignore SIGUSR1, it's triggered on server regeneration. Tests that need
   * to handle SIGUSR1 will have to install their own signal handler anyways */
  signal(SIGUSR1 ,SIG_IGN);

554
  RegisterXIOErrorHandler();
555
  RegisterXErrorHandler();
556
557
}

558
bool xorg::testing::XServer::Terminate(unsigned int timeout) {
559
560
561
  if (getenv("XORG_GTEST_XSERVER_KEEPALIVE"))
    return true;

562
  if (!Process::Terminate(timeout)) {
563
    std::cerr << "Warning: Failed to terminate Xorg server: "
564
565
566
567
568
569
570
              << std::strerror(errno) << "\n";
    return false;
  } else
    return true;
}

bool xorg::testing::XServer::Kill(unsigned int timeout) {
571
572
573
  if (getenv("XORG_GTEST_XSERVER_KEEPALIVE"))
    return true;

574
  if (!Process::Kill(timeout)) {
575
    std::cerr << "Warning: Failed to kill Xorg server: "
576
577
578
579
580
581
              << std::strerror(errno) << "\n";
    return false;
  } else
    return true;
}

Peter Hutterer's avatar
Peter Hutterer committed
582
583
584
585
586
587
void xorg::testing::XServer::RemoveLogFile(bool force) {
  enum Process::State state = GetState();
  if (force || state == Process::TERMINATED || state == Process::FINISHED_SUCCESS)
    unlink(d_->options["-logfile"].c_str());
}

588
589
590
void xorg::testing::XServer::SetOption(const std::string &key, const std::string &value) {
  d_->options[key] = value;
}
591

Peter Hutterer's avatar
Peter Hutterer committed
592
593
594
595
void xorg::testing::XServer::RemoveOption(const std::string &option) {
  d_->options.erase(option);
}

596
597
598
599
600
601
602
const std::string& xorg::testing::XServer::GetLogFilePath() {
  return d_->options["-logfile"];
}

const std::string& xorg::testing::XServer::GetConfigPath() {
  return d_->options["-config"];
}