Imported Debian patch 2:1.15.1-0ubuntu2.6
[deb_xorg-server.git] / debian / patches / 190_cache-xkbcomp_output_for_fast_start_up.patch
CommitLineData
7217e0ca
ML
1Last-Update: 2013-09-19
2
3Index: xorg-server-1.14.2.901/configure.ac
4===================================================================
5--- xorg-server-1.14.2.901.orig/configure.ac 2013-09-19 11:43:53.948797077 -0400
6+++ xorg-server-1.14.2.901/configure.ac 2013-09-19 11:43:53.944797077 -0400
7@@ -517,9 +517,9 @@
8 AC_ARG_WITH(xkb-path, AS_HELP_STRING([--with-xkb-path=PATH], [Path to XKB base dir (default: ${datadir}/X11/xkb)]),
9 [ XKBPATH="$withval" ],
10 [ XKBPATH="${datadir}/X11/xkb" ])
11-AC_ARG_WITH(xkb-output, AS_HELP_STRING([--with-xkb-output=PATH], [Path to XKB output dir (default: ${datadir}/X11/xkb/compiled)]),
12+AC_ARG_WITH(xkb-output, AS_HELP_STRING([--with-xkb-output=PATH], [Path to XKB output dir (default: ${localstatedir}/cache/xkb)]),
13 [ XKBOUTPUT="$withval" ],
14- [ XKBOUTPUT="compiled" ])
15+ [ XKBOUTPUT="${localstatedir}/cache/xkb" ])
16 AC_ARG_WITH(default-xkb-rules, AS_HELP_STRING([--with-default-xkb-rules=RULES],
17 [Keyboard ruleset (default: base/evdev)]),
18 [ XKB_DFLT_RULES="$withval" ],
19@@ -1225,7 +1225,7 @@
20 dnl Make sure XKM_OUTPUT_DIR is an absolute path
21 XKBOUTPUT_FIRSTCHAR=`echo $XKBOUTPUT | cut -b 1`
22 if [[ x$XKBOUTPUT_FIRSTCHAR != x/ -a x$XKBOUTPUT_FIRSTCHAR != 'x$' ]] ; then
23- XKBOUTPUT="$XKB_BASE_DIRECTORY/$XKBOUTPUT"
24+ AC_MSG_ERROR([xkb-output must be an absolute path.])
25 fi
26
27 dnl XKM_OUTPUT_DIR (used in code) must end in / or file names get hosed
28Index: xorg-server-1.14.2.901/xkb/README.compiled
29===================================================================
30--- xorg-server-1.14.2.901.orig/xkb/README.compiled 2013-09-19 11:43:53.948797077 -0400
31+++ xorg-server-1.14.2.901/xkb/README.compiled 2013-09-19 11:43:53.944797077 -0400
32@@ -4,10 +4,10 @@
33 or some other tool might destroy or replace the files in this directory,
34 so it is not a safe place to store compiled keymaps for long periods of
35 time. The default keymap for any server is usually stored in:
36- X<num>-default.xkm
37-where <num> is the display number of the server in question, which makes
38-it possible for several servers *on the same host* to share the same
39-directory.
40+ server-<SHA1>.xkm
41+
42+where <SHA1> is the SHA1 hash of keymap source, so that compiled
43+keymap of different keymap sources are stored in different files.
44
45 Unless the X server is modified, sharing this directory between servers on
46 different hosts could cause problems.
47Index: xorg-server-1.14.2.901/xkb/ddxLoad.c
48===================================================================
49--- xorg-server-1.14.2.901.orig/xkb/ddxLoad.c 2013-09-19 11:43:53.948797077 -0400
50+++ xorg-server-1.14.2.901/xkb/ddxLoad.c 2013-09-19 11:51:04.744800715 -0400
51@@ -30,6 +30,12 @@
52
53 #include <xkb-config.h>
54
55+#ifdef HAVE_SHA1_IN_LIBGCRYPT /* Use libgcrypt for SHA1 */
56+#include <gcrypt.h>
57+#else /* Use OpenSSL's libcrypto */
58+#warning "xkbcomp caching support disabled"
59+#endif
60+
61 #include <stdio.h>
62 #include <ctype.h>
63 #include <X11/X.h>
64@@ -43,20 +49,9 @@
65 #define XKBSRV_NEED_FILE_FUNCS
66 #include <xkbsrv.h>
67 #include <X11/extensions/XI.h>
68+#include <errno.h>
69 #include "xkb.h"
70
71- /*
72- * If XKM_OUTPUT_DIR specifies a path without a leading slash, it is
73- * relative to the top-level XKB configuration directory.
74- * Making the server write to a subdirectory of that directory
75- * requires some work in the general case (install procedure
76- * has to create links to /var or somesuch on many machines),
77- * so we just compile into /usr/tmp for now.
78- */
79-#ifndef XKM_OUTPUT_DIR
80-#define XKM_OUTPUT_DIR "compiled/"
81-#endif
82-
83 #define PRE_ERROR_MSG "\"The XKEYBOARD keymap compiler (xkbcomp) reports:\""
84 #define ERROR_PREFIX "\"> \""
85 #define POST_ERROR_MSG1 "\"Errors from xkbcomp are not fatal to the X server\""
86@@ -69,35 +64,87 @@
87 #endif
88
89 static void
90-OutputDirectory(char *outdir, size_t size)
91+OutputDirectory(char *outdir, size_t size, Bool *is_private_directory)
92 {
93 #ifndef WIN32
94 /* Can we write an xkm and then open it too? */
95 if (access(XKM_OUTPUT_DIR, W_OK | X_OK) == 0 &&
96 (strlen(XKM_OUTPUT_DIR) < size)) {
97 (void) strcpy(outdir, XKM_OUTPUT_DIR);
98+ if (is_private_directory)
99+ *is_private_directory = TRUE;
100 }
101 else
102 #else
103 if (strlen(Win32TempDir()) + 1 < size) {
104 (void) strcpy(outdir, Win32TempDir());
105 (void) strcat(outdir, "\\");
106+ if (is_private_directory)
107+ *is_private_directory = FALSE;
108 }
109 else
110 #endif
111 if (strlen("/tmp/") < size) {
112 (void) strcpy(outdir, "/tmp/");
113+ if (is_private_directory)
114+ *is_private_directory = FALSE;
115 }
116 }
117
118+#ifndef SHA_DIGEST_LENGTH
119+#define SHA_DIGEST_LENGTH 20
120+#endif
121+
122+static Bool
123+Sha1Asc(char sha1Asc[SHA_DIGEST_LENGTH * 2 + 1], const char *input)
124+{
125+ int i;
126+ unsigned char sha1[SHA_DIGEST_LENGTH];
127+
128+#ifdef HAVE_SHA1_IN_LIBGCRYPT /* Use libgcrypt for SHA1 */
129+ static int init;
130+ gcry_md_hd_t h;
131+ gcry_error_t err;
132+
133+ if (!init) {
134+ if (!gcry_check_version(NULL))
135+ return BadAlloc;
136+ gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
137+ gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
138+ init = 1;
139+ }
140+
141+ err = gcry_md_open(&h, GCRY_MD_SHA1, 0);
142+ if (err)
143+ return BadAlloc;
144+ gcry_md_write(h, input, strlen(input));
145+ memcpy(sha1, gcry_md_read(h, GCRY_MD_SHA1), 20);
146+ gcry_md_close(h);
147+#endif
148+
149+ /* convert sha1 to sha1_asc */
150+ for (i = 0; i < SHA_DIGEST_LENGTH; ++i) {
151+ sprintf(sha1Asc + i * 2, "%02X", sha1[i]);
152+ }
153+
154+ return Success;
155+}
156+
157+/* call xkbcomp and compile XKB keymap, return xkm file name in
158+ nameRtrn */
159 static Bool
160 XkbDDXCompileKeymapByNames(XkbDescPtr xkb,
161 XkbComponentNamesPtr names,
162 unsigned want,
163- unsigned need, char *nameRtrn, int nameRtrnLen)
164+ unsigned need, char *nameRtrn, int nameRtrnLen,
165+ Bool *is_private_directory)
166 {
167 FILE *out;
168- char *buf = NULL, keymap[PATH_MAX], xkm_output_dir[PATH_MAX];
169+ char *buf = NULL, xkmfile[PATH_MAX], xkm_output_dir[PATH_MAX];
170+ char *tmpXkmFile = NULL;
171+ char *canonicalXkmFileName = NULL;
172+ char sha1Asc[SHA_DIGEST_LENGTH * 2 + 1], xkbKeyMapBuf[100 * 1024];
173+ int ret, result;
174
175 const char *emptystring = "";
176 char *xkbbasedirflag = NULL;
177@@ -108,14 +155,68 @@
178 /* WIN32 has no popen. The input must be stored in a file which is
179 used as input for xkbcomp. xkbcomp does not read from stdin. */
180 char tmpname[PATH_MAX];
181- const char *xkmfile = tmpname;
182+ const char *xkbfile = tmpname;
183 #else
184- const char *xkmfile = "-";
185+ const char *xkbfile = "-";
186 #endif
187
188- snprintf(keymap, sizeof(keymap), "server-%s", display);
189+ /* Write keymap source (xkbfile) to memory buffer `xkbKeyMapBuf',
190+ of which SHA1 is generated and used as result xkm file name */
191+ memset(xkbKeyMapBuf, 0, sizeof(xkbKeyMapBuf));
192+ out = fmemopen(xkbKeyMapBuf, sizeof(xkbKeyMapBuf), "w");
193+ if (NULL == out) {
194+ ErrorF("[xkb] Open xkbKeyMapBuf for writing failed\n");
195+ return FALSE;
196+ }
197+ ret = XkbWriteXKBKeymapForNames(out, names, xkb, want, need);
198+ if (fclose(out) != 0) {
199+ ErrorF
200+ ("[xkb] XkbWriteXKBKeymapForNames error, perhaps xkbKeyMapBuf is too small\n");
201+ return FALSE;
202+ }
203+#ifdef DEBUG
204+ if (xkbDebugFlags) {
205+ ErrorF("[xkb] XkbDDXCompileKeymapByNames compiling keymap:\n");
206+ fputs(xkbKeyMapBuf, stderr);
207+ }
208+#endif
209+ if (!ret) {
210+ ErrorF
211+ ("[xkb] Generating XKB Keymap failed, giving up compiling keymap\n");
212+ return FALSE;
213+ }
214+
215+ DebugF("[xkb] computing SHA1 of keymap\n");
216+ if (Success == Sha1Asc(sha1Asc, xkbKeyMapBuf)) {
217+ snprintf(xkmfile, sizeof(xkmfile), "server-%s", sha1Asc);
218+ }
219+ else {
220+ ErrorF("[xkb] Computing SHA1 of keymap failed, "
221+ "using display name instead as xkm file name\n");
222+ snprintf(xkmfile, sizeof(xkmfile), "server-%s", display);
223+ }
224+
225+ OutputDirectory(xkm_output_dir, sizeof(xkm_output_dir), is_private_directory);
226+ /* set nameRtrn, fail if it's too small */
227+ if ((strlen(xkmfile) + 1 > nameRtrnLen) && nameRtrn) {
228+ ErrorF("[xkb] nameRtrn too small to hold xkmfile name\n");
229+ return FALSE;
230+ }
231+ strncpy(nameRtrn, xkmfile, nameRtrnLen);
232
233- OutputDirectory(xkm_output_dir, sizeof(xkm_output_dir));
234+ /* if the xkm file already exists, reuse it */
235+ canonicalXkmFileName = Xprintf("%s%s.xkm", xkm_output_dir, xkmfile);
236+ if ((*is_private_directory) && (access(canonicalXkmFileName, R_OK) == 0)) {
237+ /* yes, we can reuse the old xkm file */
238+ LogMessage(X_INFO, "XKB: reuse xkmfile %s\n", canonicalXkmFileName);
239+ result = TRUE;
240+ goto _ret;
241+ }
242+ LogMessage(X_INFO, "XKB: generating xkmfile %s\n", canonicalXkmFileName);
243+
244+ /* continue to call xkbcomp to compile the keymap. to avoid race
245+ condition, we compile it to a tmpfile then rename it to
246+ xkmfile */
247
248 #ifdef WIN32
249 strcpy(tmpname, Win32TempDir());
250@@ -139,15 +240,21 @@
251 }
252 }
253
254+ if ((tmpXkmFile = tempnam(xkm_output_dir, NULL)) == NULL) {
255+ ErrorF("[xkb] Can't generate temp xkm file name");
256+ result = FALSE;
257+ goto _ret;
258+ }
259+
260 if (asprintf(&buf,
261 "\"%s%sxkbcomp\" -w %d %s -xkm \"%s\" "
262- "-em1 %s -emp %s -eml %s \"%s%s.xkm\"",
263+ "-em1 %s -emp %s -eml %s \"%s\"",
264 xkbbindir, xkbbindirsep,
265 ((xkbDebugFlags < 2) ? 1 :
266 ((xkbDebugFlags > 10) ? 10 : (int) xkbDebugFlags)),
267- xkbbasedirflag ? xkbbasedirflag : "", xkmfile,
268+ xkbbasedirflag ? xkbbasedirflag : "", xkbfile,
269 PRE_ERROR_MSG, ERROR_PREFIX, POST_ERROR_MSG1,
270- xkm_output_dir, keymap) == -1)
271+ tmpXkmFile) == -1)
272 buf = NULL;
273
274 free(xkbbasedirflag);
275@@ -158,6 +265,11 @@
276 return FALSE;
277 }
278
279+ /* there's a potential race condition between calling tempnam()
280+ and invoking xkbcomp to write the result file (potential temp
281+ file name conflicts), but since xkbcomp is a standalone
282+ program, we have to live with this */
283+
284 #ifndef WIN32
285 out = Popen(buf, "w");
286 #else
287@@ -165,32 +277,43 @@
288 #endif
289
290 if (out != NULL) {
291-#ifdef DEBUG
292- if (xkbDebugFlags) {
293- ErrorF("[xkb] XkbDDXCompileKeymapByNames compiling keymap:\n");
294- XkbWriteXKBKeymapForNames(stderr, names, xkb, want, need);
295+ /* write XKBKeyMapBuf to xkbcomp */
296+ if (EOF == fputs(xkbKeyMapBuf, out)) {
297+ ErrorF("[xkb] Sending keymap to xkbcomp failed\n");
298+ result = FALSE;
299+ goto _ret;
300 }
301-#endif
302- XkbWriteXKBKeymapForNames(out, names, xkb, want, need);
303 #ifndef WIN32
304 if (Pclose(out) == 0)
305 #else
306 if (fclose(out) == 0 && System(buf) >= 0)
307 #endif
308 {
309+ /* xkbcomp success */
310 if (xkbDebugFlags)
311 DebugF("[xkb] xkb executes: %s\n", buf);
312- if (nameRtrn) {
313- strlcpy(nameRtrn, keymap, nameRtrnLen);
314+
315+ /* if canonicalXkmFileName already exists now, we simply
316+ overwrite it, this is OK */
317+ ret = rename(tmpXkmFile, canonicalXkmFileName);
318+ if (0 != ret) {
319+ ErrorF("[xkb] Can't rename %s to %s, error: %s\n",
320+ tmpXkmFile, canonicalXkmFileName, strerror(errno));
321+
322+ /* in case of error, don't unlink tmpXkmFile, leave i
323+ for debugging */
324+
325+ result = FALSE;
326+ goto _ret;
327 }
328- free(buf);
329 #ifdef WIN32
330 unlink(tmpname);
331 #endif
332- return TRUE;
333+ result = TRUE;
334+ goto _ret;
335 }
336 else
337- LogMessage(X_ERROR, "Error compiling keymap (%s)\n", keymap);
338+ LogMessage(X_ERROR, "Error compiling keymap (%s)\n", xkbfile);
339 #ifdef WIN32
340 /* remove the temporary file */
341 unlink(tmpname);
342@@ -205,8 +328,17 @@
343 }
344 if (nameRtrn)
345 nameRtrn[0] = '\0';
346- free(buf);
347- return FALSE;
348+ result = FALSE;
349+
350+ _ret:
351+ if (tmpXkmFile)
352+ free(tmpXkmFile);
353+ if (canonicalXkmFileName)
354+ free(canonicalXkmFileName);
355+ if (buf)
356+ free(buf);
357+
358+ return result;
359 }
360
361 static FILE *
362@@ -217,7 +349,7 @@
363
364 buf[0] = '\0';
365 if (mapName != NULL) {
366- OutputDirectory(xkm_output_dir, sizeof(xkm_output_dir));
367+ OutputDirectory(xkm_output_dir, sizeof(xkm_output_dir), NULL);
368 if ((XkbBaseDirectory != NULL) && (xkm_output_dir[0] != '/')
369 #ifdef WIN32
370 && (!isalpha(xkm_output_dir[0]) || xkm_output_dir[1] != ':')
371@@ -256,6 +388,7 @@
372 FILE *file;
373 char fileName[PATH_MAX];
374 unsigned missing;
375+ Bool is_private_directory;
376
377 *xkbRtrn = NULL;
378 if ((keybd == NULL) || (keybd->key == NULL) ||
379@@ -271,7 +404,8 @@
380 return 0;
381 }
382 else if (!XkbDDXCompileKeymapByNames(xkb, names, want, need,
383- nameRtrn, nameRtrnLen)) {
384+ nameRtrn, nameRtrnLen,
385+ &is_private_directory)) {
386 LogMessage(X_ERROR, "XKB: Couldn't compile keymap\n");
387 return 0;
388 }
389@@ -293,7 +427,8 @@
390 (*xkbRtrn)->defined);
391 }
392 fclose(file);
393- (void) unlink(fileName);
394+ if (!is_private_directory)
395+ (void) unlink(fileName);
396 return (need | want) & (~missing);
397 }
398