1 Last-Update: 2013-09-19
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.])
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:
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
38 +where <SHA1> is the SHA1 hash of keymap source, so that compiled
39 +keymap of different keymap sources are stored in different files.
41 Unless the X server is modified, sharing this directory between servers on
42 different hosts could cause problems.
45 @@ -30,6 +30,12 @@ THE USE OR PERFORMANCE OF THIS SOFTWARE.
47 #include <xkb-config.h>
49 +#ifdef HAVE_SHA1_IN_LIBGCRYPT /* Use libgcrypt for SHA1 */
51 +#else /* Use OpenSSL's libcrypto */
52 +#warning "xkbcomp caching support disabled"
58 @@ -43,20 +49,9 @@ THE USE OR PERFORMANCE OF THIS SOFTWARE.
59 #define XKBSRV_NEED_FILE_FUNCS
61 #include <X11/extensions/XI.h>
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.
73 -#ifndef XKM_OUTPUT_DIR
74 -#define XKM_OUTPUT_DIR "compiled/"
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.
84 -OutputDirectory(char *outdir, size_t size)
85 +OutputDirectory(char *outdir, size_t size, Bool *is_private_directory)
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;
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;
105 if (strlen("/tmp/") < size) {
106 (void) strcpy(outdir, "/tmp/");
107 + if (is_private_directory)
108 + *is_private_directory = FALSE;
112 +#ifndef SHA_DIGEST_LENGTH
113 +#define SHA_DIGEST_LENGTH 20
117 +Sha1Asc(char sha1Asc[SHA_DIGEST_LENGTH * 2 + 1], const char *input)
120 + unsigned char sha1[SHA_DIGEST_LENGTH];
122 +#ifdef HAVE_SHA1_IN_LIBGCRYPT /* Use libgcrypt for SHA1 */
128 + if (!gcry_check_version(NULL))
130 + gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
131 + gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
135 + err = gcry_md_open(&h, GCRY_MD_SHA1, 0);
138 + gcry_md_write(h, input, strlen(input));
139 + memcpy(sha1, gcry_md_read(h, GCRY_MD_SHA1), 20);
143 + /* convert sha1 to sha1_asc */
144 + for (i = 0; i < SHA_DIGEST_LENGTH; ++i) {
145 + sprintf(sha1Asc + i * 2, "%02X", sha1[i]);
151 +/* call xkbcomp and compile XKB keymap, return xkm file name in
154 XkbDDXCompileKeymapByNames(XkbDescPtr xkb,
155 XkbComponentNamesPtr names,
157 - unsigned need, char *nameRtrn, int nameRtrnLen)
158 + unsigned need, char *nameRtrn, int nameRtrnLen,
159 + Bool *is_private_directory)
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];
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;
178 - const char *xkmfile = "-";
179 + const char *xkbfile = "-";
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");
188 + ErrorF("[xkb] Open xkbKeyMapBuf for writing failed\n");
191 + ret = XkbWriteXKBKeymapForNames(out, names, xkb, want, need);
192 + if (fclose(out) != 0) {
194 + ("[xkb] XkbWriteXKBKeymapForNames error, perhaps xkbKeyMapBuf is too small\n");
198 + if (xkbDebugFlags) {
199 + ErrorF("[xkb] XkbDDXCompileKeymapByNames compiling keymap:\n");
200 + fputs(xkbKeyMapBuf, stderr);
205 + ("[xkb] Generating XKB Keymap failed, giving up compiling keymap\n");
209 + DebugF("[xkb] computing SHA1 of keymap\n");
210 + if (Success == Sha1Asc(sha1Asc, xkbKeyMapBuf)) {
211 + snprintf(xkmfile, sizeof(xkmfile), "server-%s", sha1Asc);
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);
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");
225 + strncpy(nameRtrn, xkmfile, nameRtrnLen);
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);
236 + LogMessage(X_INFO, "XKB: generating xkmfile %s\n", canonicalXkmFileName);
238 + /* continue to call xkbcomp to compile the keymap. to avoid race
239 + condition, we compile it to a tmpfile then rename it to
243 strcpy(tmpname, Win32TempDir());
244 @@ -139,15 +240,21 @@ XkbDDXCompileKeymapByNames(XkbDescPtr xk
248 + if ((tmpXkmFile = tempnam(xkm_output_dir, NULL)) == NULL) {
249 + ErrorF("[xkb] Can't generate temp xkm file name");
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)
268 free(xkbbasedirflag);
269 @@ -158,6 +265,11 @@ XkbDDXCompileKeymapByNames(XkbDescPtr xk
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 */
279 out = Popen(buf, "w");
281 @@ -165,32 +277,43 @@ XkbDDXCompileKeymapByNames(XkbDescPtr xk
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");
296 - XkbWriteXKBKeymapForNames(out, names, xkb, want, need);
298 if (Pclose(out) == 0)
300 if (fclose(out) == 0 && System(buf) >= 0)
303 + /* xkbcomp success */
305 DebugF("[xkb] xkb executes: %s\n", buf);
307 - strlcpy(nameRtrn, keymap, nameRtrnLen);
309 + /* if canonicalXkmFileName already exists now, we simply
310 + overwrite it, this is OK */
311 + ret = rename(tmpXkmFile, canonicalXkmFileName);
313 + ErrorF("[xkb] Can't rename %s to %s, error: %s\n",
314 + tmpXkmFile, canonicalXkmFileName, strerror(errno));
316 + /* in case of error, don't unlink tmpXkmFile, leave i
331 - LogMessage(X_ERROR, "Error compiling keymap (%s)\n", keymap);
332 + LogMessage(X_ERROR, "Error compiling keymap (%s)\n", xkbfile);
334 /* remove the temporary file */
336 @@ -205,8 +328,17 @@ XkbDDXCompileKeymapByNames(XkbDescPtr xk
347 + if (canonicalXkmFileName)
348 + free(canonicalXkmFileName);
356 @@ -217,7 +349,7 @@ XkbDDXOpenConfigFile(char *mapName, char
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] != '/')
364 && (!isalpha(xkm_output_dir[0]) || xkm_output_dir[1] != ':')
365 @@ -256,6 +388,7 @@ XkbDDXLoadKeymapByNames(DeviceIntPtr key
367 char fileName[PATH_MAX];
369 + Bool is_private_directory;
372 if ((keybd == NULL) || (keybd->key == NULL) ||
373 @@ -271,7 +404,8 @@ XkbDDXLoadKeymapByNames(DeviceIntPtr key
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");
383 @@ -293,7 +427,8 @@ XkbDDXLoadKeymapByNames(DeviceIntPtr key
384 (*xkbRtrn)->defined);
387 - (void) unlink(fileName);
388 + if (!is_private_directory)
389 + (void) unlink(fileName);
390 return (need | want) & (~missing);