ODROID-U3 xorg-server debian package fork :
[deb_xorg-server.git] / debian / patches / 190_cache-xkbcomp_output_for_fast_start_up.patch
1 Last-Update: 2013-09-19
2
3 --- a/configure.ac
4 +++ b/configure.ac
5 @@ -514,9 +514,9 @@ AC_MSG_RESULT([$FONTPATH])
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" ],
17 @@ -1415,7 +1415,7 @@ AC_DEFINE_DIR(XKB_BIN_DIRECTORY, XKB_BIN
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
26 --- a/xkb/README.compiled
27 +++ b/xkb/README.compiled
28 @@ -4,10 +4,10 @@ current keymap and/or any scratch keymap
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.
43 --- a/xkb/ddxLoad.c
44 +++ b/xkb/ddxLoad.c
45 @@ -30,6 +30,12 @@ THE USE OR PERFORMANCE OF THIS SOFTWARE.
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>
58 @@ -43,20 +49,9 @@ THE USE OR PERFORMANCE OF THIS SOFTWARE.
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\""
80 @@ -69,35 +64,87 @@ THE USE OR PERFORMANCE OF THIS SOFTWARE.
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;
171 @@ -108,14 +155,68 @@ XkbDDXCompileKeymapByNames(XkbDescPtr xk
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());
244 @@ -139,15 +240,21 @@ XkbDDXCompileKeymapByNames(XkbDescPtr xk
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);
269 @@ -158,6 +265,11 @@ XkbDDXCompileKeymapByNames(XkbDescPtr xk
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
281 @@ -165,32 +277,43 @@ XkbDDXCompileKeymapByNames(XkbDescPtr xk
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);
336 @@ -205,8 +328,17 @@ XkbDDXCompileKeymapByNames(XkbDescPtr xk
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 *
356 @@ -217,7 +349,7 @@ XkbDDXOpenConfigFile(char *mapName, char
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] != ':')
365 @@ -256,6 +388,7 @@ XkbDDXLoadKeymapByNames(DeviceIntPtr key
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) ||
373 @@ -271,7 +404,8 @@ XkbDDXLoadKeymapByNames(DeviceIntPtr key
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 }
383 @@ -293,7 +427,8 @@ XkbDDXLoadKeymapByNames(DeviceIntPtr key
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