Commit | Line | Data |
---|---|---|
7217e0ca ML |
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 |