Skip to content

Commit f19e18e

Browse files
committed
Better logging and command-line parsing
This emulates Xorg's own command-line parser in case the display manager starts to pass additional options to Xorg.
1 parent 2a81ce2 commit f19e18e

File tree

1 file changed

+171
-50
lines changed

1 file changed

+171
-50
lines changed

shmoverride/shmoverride.c

+171-50
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <stdio.h>
3030
#include <string.h>
3131
#include <errno.h>
32+
#include <limits.h>
3233

3334
#include <dlfcn.h>
3435
#include <sys/types.h>
@@ -82,8 +83,7 @@ static int xc_hnd;
8283
static xengnttab_handle *xgt;
8384
static char __shmid_filename[SHMID_FILENAME_LEN];
8485
static char *shmid_filename = NULL;
85-
static int idfd = -1;
86-
static char display_str[SHMID_DISPLAY_MAXLEN+1] = "";
86+
static int idfd = -1, display = -1;
8787

8888
static uint8_t *mmap_mfns(struct shm_args_hdr *shm_args) {
8989
uint8_t *map;
@@ -221,64 +221,181 @@ ASM_DEF(int, munmap, void *addr, size_t len)
221221
return real_munmap((void *)rounded_addr, len + (addr_int - rounded_addr));
222222
}
223223

224-
int get_display(void)
224+
static const char* const opts_with_args[] = {
225+
"-a",
226+
"-ardelay",
227+
"-arinterval",
228+
"-audit",
229+
"-auth",
230+
"-background",
231+
"-bgamma",
232+
"-cc",
233+
"-class",
234+
"-config",
235+
"-configdir",
236+
"-cookie",
237+
"-deferglyphs",
238+
"-depth",
239+
"-displayID",
240+
"-displayfd",
241+
"-dpi",
242+
"-f",
243+
"-fc",
244+
"-fn",
245+
"-fp",
246+
"-from",
247+
"-gamma",
248+
"-ggamma",
249+
"-indirect",
250+
"-isolateDevice",
251+
"-keyboard",
252+
"-layout",
253+
"-ld",
254+
"-lf",
255+
"-listen",
256+
"-logfile",
257+
"-ls",
258+
"-maxbigreqsize",
259+
"-modulepath",
260+
"-nolisten",
261+
"-p",
262+
"-pointer",
263+
"-port",
264+
"-query",
265+
"-render",
266+
"-rgamma",
267+
"-s",
268+
"-schedInterval",
269+
"-screen",
270+
"-seat",
271+
"-t",
272+
"-to",
273+
"-weight",
274+
"-x",
275+
"-xkbdir",
276+
"-xkbmap",
277+
"c",
278+
};
279+
280+
static int cmp_strings(const void *a, const void *b) { return strcmp(a, b); }
281+
282+
/* FIXME: should be in a utility library */
283+
static inline bool qubes_isdigit(char p) { return p >= '0' && p <= '9'; }
284+
285+
static bool parse_display_name(const char *const ptr, int *display_num)
286+
{
287+
unsigned long l_display = ULONG_MAX;
288+
const char *p = ptr + 1, *period_pointer = NULL;
289+
if (!qubes_isdigit(*p)) {
290+
fprintf(stderr, "Bad display name %s: colon not followed by ASCII digit\n", ptr);
291+
return false;
292+
}
293+
294+
for (p++; *p; p++) {
295+
if (!qubes_isdigit(*p)) {
296+
if (*p != '.') {
297+
fprintf(stderr, "Bad display name %s: invalid character %d\n", ptr, *p);
298+
return false;
299+
}
300+
if (period_pointer) {
301+
fprintf(stderr, "Bad display name %s: more than one period ('.') character\n", ptr);
302+
return false;
303+
}
304+
period_pointer = p;
305+
}
306+
}
307+
308+
if (period_pointer) {
309+
if (p - period_pointer <= 1) {
310+
fprintf(stderr, "Bad display name %s: period at end of name\n", ptr);
311+
return false;
312+
}
313+
314+
if (p - period_pointer > 3) {
315+
fprintf(stderr, "Bad display name %s: more than 2 bytes after period\n", ptr);
316+
return false;
317+
}
318+
}
319+
320+
errno = 0;
321+
l_display = strtoul(ptr + 1, NULL, 10);
322+
if (errno || l_display > INT_MAX) {
323+
fprintf(stderr, "Bad display name %s: exceeds INT_MAX (%d)\n", ptr, INT_MAX);
324+
/* Xorg will correctly reject this later */
325+
return false;
326+
}
327+
328+
/* X happily accepts multiple display names and ignores all but the last */
329+
*display_num = (int)l_display;
330+
return true;
331+
}
332+
333+
static int get_display(void)
225334
{
226-
int fd;
227335
ssize_t res;
228-
char ch;
229-
int in_arg = -1;
336+
int fd, rc = -1, display = 0;
230337

231338
fd = open("/proc/self/cmdline", O_RDONLY | O_NOCTTY | O_CLOEXEC);
232339
if (fd < 0) {
233340
perror("cmdline open");
234-
return -1;
341+
return rc;
342+
}
343+
char *ptr = NULL;
344+
size_t size = 0;
345+
FILE *f = fdopen(fd, "r");
346+
if (!f) {
347+
perror("fdopen()");
348+
close(fd);
349+
return rc;
235350
}
236351

237-
while(1) {
238-
res = read(fd, &ch, 1);
239-
if (res < 0) {
352+
bool skip = true; /* Skip argv[0] (the program name) */
353+
while(true) {
354+
errno = 0;
355+
res = getdelim(&ptr, &size, 0, f);
356+
if (res <= 0) {
357+
if (res == -1 && errno == 0)
358+
break;
240359
perror("cmdline read");
241-
return -1;
360+
goto cleanup;
242361
}
243-
if (res == 0)
244-
break;
245-
246-
if (in_arg == 0 && ch != ':')
247-
in_arg = -1;
248-
if (ch == '\0') {
249-
in_arg = 0;
250-
} else if (in_arg >= 0) {
251-
if (in_arg >= SHMID_DISPLAY_MAXLEN)
252-
break;
253-
if (in_arg > 0 && (ch < '0' || ch > '9')) {
254-
if (in_arg == 1) {
255-
fprintf(stderr, "cmdline DISPLAY parsing failed\n");
256-
return -1;
257-
}
258-
in_arg = -1;
259-
continue;
260-
}
261-
display_str[in_arg++] = ch;
262-
display_str[in_arg] = '\0';
362+
size_t length = (size_t)res;
363+
assert(ptr && ptr[length] == '\0');
364+
365+
/*
366+
* Skip option arguments. Some options take more than one argument,
367+
* but the extra arguments are always optional and display names
368+
* will not be interpreted as arguments to these options. Since
369+
* skip is true at the start of the loop, this also skips argv[0].
370+
*/
371+
if (skip) {
372+
skip = false;
373+
continue;
263374
}
264-
}
265-
close(fd);
266-
267-
if (display_str[0] != ':') {
268-
display_str[0] = ':';
269-
display_str[1] = '0';
270-
display_str[2] = '\0';
271-
} else if (display_str[1] == '\0') {
272-
fprintf(stderr, "cmdline DISPLAY parsing failed\n");
273-
return -1;
274-
}
275375

276-
/* post-processing: drop leading ':' */
277-
res = strlen(display_str);
278-
for (in_arg = 0; in_arg < res; in_arg++)
279-
display_str[in_arg] = display_str[in_arg+1];
376+
if (!strcmp(ptr, "-I"))
377+
break; /* all remaining arguments ignored */
280378

281-
return 0;
379+
/* Check if this is an option that takes a mandatory argument */
380+
if (bsearch(ptr, opts_with_args, sizeof(opts_with_args)/sizeof(opts_with_args[0]),
381+
sizeof(opts_with_args[0]), cmp_strings)) {
382+
skip = true;
383+
continue;
384+
}
385+
386+
if (ptr[0] == ':' && !parse_display_name(ptr, &display)) {
387+
fprintf(stderr, "Bad display name %s\n", ptr);
388+
goto cleanup;
389+
}
390+
}
391+
if (!skip)
392+
rc = display;
393+
else
394+
fputs(stderr, "Missing argument to option, or argv[0] is NULL\n", stderr);
395+
cleanup:
396+
free(ptr);
397+
fclose(f);
398+
return rc;
282399
}
283400

284401
static int assign_off(off_t *off) {
@@ -377,12 +494,16 @@ int __attribute__ ((constructor)) initfunc(void)
377494
goto cleanup; // Allow it to run when not under Xen.
378495
}
379496

380-
if (get_display() < 0)
497+
if ((display = get_display()) < 0)
381498
goto cleanup;
382499

383-
snprintf(__shmid_filename, SHMID_FILENAME_LEN,
384-
SHMID_FILENAME_PREFIX "%s", display_str);
500+
if ((unsigned int)snprintf(__shmid_filename, sizeof __shmid_filename,
501+
SHMID_FILENAME_PREFIX "%d", display) >= sizeof __shmid_filename) {
502+
fputs("snprintf() failed!\n", stderr);
503+
abort();
504+
}
385505
shmid_filename = __shmid_filename;
506+
fprintf(stderr, "shmoverride: running with shm file %s\n", shmid_filename);
386507

387508
/* Try to lock the shm.id file (don't rely on whether it exists, a previous
388509
* process might have crashed).

0 commit comments

Comments
 (0)