Imported Debian patch 2:1.15.1-0ubuntu2.6
[deb_xorg-server.git] / debian / patches / 190_cache-xkbcomp_output_for_fast_start_up.patch
1 Last-Update: 2013-09-19
2
3 Index: 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
28 Index: 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.
47 Index: 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