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