Is there a race condition if two servers start at once (two vt's or
multiseat, etc.) and the first server has started writing the .xkm
but not finished when the second one finds it and tries to read?
(Perhaps it should write to a temporary name and then rename() when
 writing is complete.)    We had problems with that with the old
code before we switched to exclusively using server-<display>.xkm
naming.

I'd also rather see the Sha1 helper function moved to a file under
the os dir and shared by both render/glyph.c & this code instead
of duplicating the code here and giving people two places to make
the same updates when adding new sha1 library implementations.

        -alan-

Yan Li wrote:
> xkbcomp outputs will be cached in files with hashed keymap as
> names. This saves boot time for around 1s on commodity netbooks.
> 
> Signed-off-by: Yan Li <yan.i...@intel.com>
> ---
>  configure.ac        |    6 +-
>  xkb/README.compiled |    8 ++--
>  xkb/ddxLoad.c       |  141 +++++++++++++++++++++++++++++++++++++++-----------
>  3 files changed, 117 insertions(+), 38 deletions(-)
> 
> diff --git a/configure.ac b/configure.ac
> index f2718b8..5d8a6e5 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -476,9 +476,9 @@ AC_ARG_WITH(default-font-path, 
> AS_HELP_STRING([--with-default-font-path=PATH], [
>  AC_ARG_WITH(xkb-path,         AS_HELP_STRING([--with-xkb-path=PATH], [Path 
> to XKB base dir (default: ${datadir}/X11/xkb)]),
>                               [ XKBPATH="$withval" ],
>                               [ XKBPATH="${datadir}/X11/xkb" ])
> -AC_ARG_WITH(xkb-output,       AS_HELP_STRING([--with-xkb-output=PATH], [Path 
> to XKB output dir (default: ${datadir}/X11/xkb/compiled)]),
> +AC_ARG_WITH(xkb-output,       AS_HELP_STRING([--with-xkb-output=PATH], [Path 
> to XKB output dir (default: ${localstatedir}/cache/xkb)]),
>                               [ XKBOUTPUT="$withval" ],
> -                             [ XKBOUTPUT="compiled" ])
> +                             [ XKBOUTPUT="${localstatedir}/cache/xkb" ])
>  AC_ARG_WITH(serverconfig-path, 
> AS_HELP_STRING([--with-serverconfig-path=PATH],
>                                  [Directory where ancillary server config 
> files are installed (default: ${libdir}/xorg)]),
>                               [ SERVERCONFIG="$withval" ],
> @@ -1753,7 +1753,7 @@ AC_DEFINE_DIR(XKB_BIN_DIRECTORY, bindir, [Path to XKB 
> bin dir])
>  XKBOUTPUT_FIRSTCHAR=`echo $XKBOUTPUT | cut -b 1`
>  
>  if [[ x$XKBOUTPUT_FIRSTCHAR != x/ ]] ; then
> -   XKBOUTPUT="$XKB_BASE_DIRECTORY/$XKBOUTPUT"
> +   AC_MSG_ERROR([xkb-output must be an absolute path.])
>  fi
>  
>  # XKM_OUTPUT_DIR (used in code) must end in / or file names get hosed
> diff --git a/xkb/README.compiled b/xkb/README.compiled
> index 71caa2f..a4a2ae0 100644
> --- a/xkb/README.compiled
> +++ b/xkb/README.compiled
> @@ -4,10 +4,10 @@ current keymap and/or any scratch keymaps used by clients.  
> The X server
>  or some other tool might destroy or replace the files in this directory,
>  so it is not a safe place to store compiled keymaps for long periods of
>  time.  The default keymap for any server is usually stored in:
> -     X<num>-default.xkm
> -where <num> is the display number of the server in question, which makes
> -it possible for several servers *on the same host* to share the same 
> -directory.
> +     server-<SHA1>.xkm
> +
> +where <SHA1> is the SHA1 hash of keymap source, so that compiled
> +keymap of different keymap sources are stored in different files.
>  
>  Unless the X server is modified, sharing this directory between servers on
>  different hosts could cause problems.
> diff --git a/xkb/ddxLoad.c b/xkb/ddxLoad.c
> index 4d5dfb6..8819353 100644
> --- a/xkb/ddxLoad.c
> +++ b/xkb/ddxLoad.c
> @@ -32,6 +32,12 @@ THE USE OR PERFORMANCE OF THIS SOFTWARE.
>  #include <xkb-config.h>
>  #endif
>  
> +#ifdef HAVE_SHA1_IN_LIBMD /* Use libmd for SHA1 */
> +# include <sha1.h>
> +#else /* Use OpenSSL's libcrypto */
> +# include <stddef.h>  /* buggy openssl/sha.h wants size_t */
> +# include <openssl/sha.h>
> +#endif
>  #include <stdio.h>
>  #include <ctype.h>
>  #define      NEED_EVENTS 1
> @@ -52,18 +58,6 @@ THE USE OR PERFORMANCE OF THIS SOFTWARE.
>  #include <paths.h>
>  #endif
>  
> -     /*
> -      * If XKM_OUTPUT_DIR specifies a path without a leading slash, it is
> -      * relative to the top-level XKB configuration directory.
> -      * Making the server write to a subdirectory of that directory
> -      * requires some work in the general case (install procedure
> -      * has to create links to /var or somesuch on many machines),
> -      * so we just compile into /usr/tmp for now.
> -      */
> -#ifndef XKM_OUTPUT_DIR
> -#define      XKM_OUTPUT_DIR  "compiled/"
> -#endif
> -
>  #define      PRE_ERROR_MSG "\"The XKEYBOARD keymap compiler (xkbcomp) 
> reports:\""
>  #define      ERROR_PREFIX    "\"> \""
>  #define      POST_ERROR_MSG1 "\"Errors from xkbcomp are not fatal to the X 
> server\""
> @@ -179,6 +173,45 @@ OutputDirectory(
>  }
>  
>  static Bool          
> +Sha1Asc(char sha1Asc[SHA_DIGEST_LENGTH*2+1], const char * input)
> +{
> +    int i;
> +    unsigned char sha1[SHA_DIGEST_LENGTH];
> +
> +#ifdef HAVE_SHA1_IN_LIBMD /* Use libmd for SHA1 */
> +    SHA1_CTX ctx;
> +
> +    SHA1Init (&ctx);
> +    SHA1Update (&ctx, input, strlen(input));
> +    SHA1Final (sha1, &ctx);
> +#else /* Use OpenSSL's libcrypto */
> +    SHA_CTX ctx;
> +    int success;
> +
> +    success = SHA1_Init (&ctx);
> +    if (! success)
> +     return BadAlloc;
> +
> +    success = SHA1_Update (&ctx, input, strlen(input));
> +    if (! success)
> +     return BadAlloc;
> +
> +    success = SHA1_Final (sha1, &ctx);
> +    if (! success)
> +     return BadAlloc;
> +#endif
> +
> +    /* convert sha1 to sha1_asc */
> +    for(i=0; i<SHA_DIGEST_LENGTH; ++i) {
> +        sprintf(sha1Asc+i*2, "%02X", sha1[i]);
> +    }
> +
> +    return Success;
> +}
> +
> +/* call xkbcomp and compile XKB keymap, return xkm file name in
> +   nameRtrn */
> +static Bool
>  XkbDDXCompileKeymapByNames(  XkbDescPtr              xkb,
>                               XkbComponentNamesPtr    names,
>                               unsigned                want,
> @@ -187,7 +220,10 @@ XkbDDXCompileKeymapByNames(      XkbDescPtr              
> xkb,
>                               int                     nameRtrnLen)
>  {
>      FILE *   out;
> -    char     *buf = NULL, keymap[PATH_MAX], xkm_output_dir[PATH_MAX];
> +    char *   buf = NULL, xkmfile[PATH_MAX], xkm_output_dir[PATH_MAX];
> +    char *   canonicalXkmfileName;
> +    char     sha1Asc[SHA_DIGEST_LENGTH*2+1], xkbKeyMapBuf[1024];
> +    int      ret;
>  
>      const char       *emptystring = "";
>      const char       *xkbbasedirflag = emptystring;
> @@ -198,16 +234,65 @@ XkbDDXCompileKeymapByNames(     XkbDescPtr              
> xkb,
>      /* WIN32 has no popen. The input must be stored in a file which is
>         used as input for xkbcomp. xkbcomp does not read from stdin. */
>      char tmpname[PATH_MAX];
> -    const char *xkmfile = tmpname;
> +    const char *xkbfile = tmpname;
>  #else
> -    const char *xkmfile = "-";
> +    const char *xkbfile = "-";
> +#endif
> +
> +    /* Write keymap source (xkbfile) to memory buffer `xkbKeyMapBuf',
> +       of which SHA1 is generated and used as result xkm file name  */
> +    memset(xkbKeyMapBuf, 0, sizeof(xkbKeyMapBuf));
> +    out = fmemopen(xkbKeyMapBuf, sizeof(xkbKeyMapBuf), "w");
> +    if (NULL == out) {
> +        ErrorF("[xkb] Open xkbKeyMapBuf for writing failed\n");
> +        return False;
> +    }
> +    ret = XkbWriteXKBKeymapForNames(out, names, xkb, want, need);
> +    fclose(out);
> +#ifdef DEBUG
> +    if (xkbDebugFlags) {
> +       ErrorF("[xkb] XkbDDXCompileKeymapByNames compiling keymap:\n");
> +       fputs(xkbKeyMapBuf, stderr);
> +    }
>  #endif
> +    if (!ret) {
> +        ErrorF("[xkb] Generating XKB Keymap failed, giving up compiling 
> keymap\n");
> +        return False;
> +    }
>  
> -    snprintf(keymap, sizeof(keymap), "server-%s", display);
> +    DebugF("[xkb] computing SHA1 of keymap\n");
> +    if (Success == Sha1Asc(sha1Asc, xkbKeyMapBuf)) {
> +        snprintf(xkmfile, sizeof(xkmfile), "server-%s", sha1Asc);
> +    }
> +    else {
> +        ErrorF("[xkb] Computing SHA1 of keymap failed, "
> +               "using display name instead as xkm file name\n");
> +        snprintf(xkmfile, sizeof(xkmfile), "server-%s", display);
> +    }
>  
> -    XkbEnsureSafeMapName(keymap);
> +    XkbEnsureSafeMapName(xkmfile);
>      OutputDirectory(xkm_output_dir, sizeof(xkm_output_dir));
>  
> +    /* set nameRtrn, fail if it's too small */
> +    if ((strlen(xkmfile)+1 > nameRtrnLen) && nameRtrn) {
> +        ErrorF("[xkb] nameRtrn too small to hold xkmfile name\n");
> +        return False;
> +    }
> +    strncpy(nameRtrn, xkmfile, nameRtrnLen);
> +
> +    /* if the xkm file already exists, reuse it */
> +    canonicalXkmfileName = Xprintf("%s%s.xkm", xkm_output_dir, xkmfile);
> +    if (access(canonicalXkmfileName, R_OK) == 0) {
> +        /* yes, we can reuse the old xkm file */
> +        LogMessage(X_INFO, "XKB: reuse xkmfile %s\n", canonicalXkmfileName);
> +        xfree(canonicalXkmfileName);
> +        return True;
> +    }
> +    LogMessage(X_INFO, "XKB: generating xkmfile %s\n", canonicalXkmfileName);
> +    xfree(canonicalXkmfileName);
> +
> +    /* continue to call xkbcomp to compile the keymap */
> +
>  #ifdef WIN32
>      strcpy(tmpname, Win32TempDir());
>      strcat(tmpname, "\\xkb_XXXXXX");
> @@ -235,9 +320,9 @@ XkbDDXCompileKeymapByNames(       XkbDescPtr              
> xkb,
>                 xkbbindir, xkbbindirsep,
>                 ( (xkbDebugFlags < 2) ? 1 :
>                   ((xkbDebugFlags > 10) ? 10 : (int)xkbDebugFlags) ),
> -               xkbbasedirflag, xkmfile,
> +               xkbbasedirflag, xkbfile,
>                 PRE_ERROR_MSG, ERROR_PREFIX, POST_ERROR_MSG1,
> -               xkm_output_dir, keymap);
> +               xkm_output_dir, xkmfile);
>  
>      if (xkbbasedirflag != emptystring) {
>       xfree(xkbbasedirflag);
> @@ -250,13 +335,12 @@ XkbDDXCompileKeymapByNames(     XkbDescPtr              
> xkb,
>  #endif
>      
>      if (out!=NULL) {
> -#ifdef DEBUG
> -    if (xkbDebugFlags) {
> -       ErrorF("[xkb] XkbDDXCompileKeymapByNames compiling keymap:\n");
> -       XkbWriteXKBKeymapForNames(stderr,names,xkb,want,need);
> +        /* write XKBKeyMapBuf to xkbcomp */
> +        if (EOF==fputs(xkbKeyMapBuf, out))
> +        {
> +            ErrorF("[xkb] Sending keymap to xkbcomp failed\n");
> +            return False;
>      }
> -#endif
> -     XkbWriteXKBKeymapForNames(out,names,xkb,want,need);
>  #ifndef WIN32
>       if (Pclose(out)==0)
>  #else
> @@ -265,16 +349,12 @@ XkbDDXCompileKeymapByNames(     XkbDescPtr              
> xkb,
>       {
>              if (xkbDebugFlags)
>                  DebugF("[xkb] xkb executes: %s\n",buf);
> -         if (nameRtrn) {
> -             strncpy(nameRtrn,keymap,nameRtrnLen);
> -             nameRtrn[nameRtrnLen-1]= '\0';
> -         }
>              if (buf != NULL)
>                  xfree (buf);
>           return True;
>       }
>       else
> -         LogMessage(X_ERROR, "Error compiling keymap (%s)\n", keymap);
> +            LogMessage(X_ERROR, "Error compiling keymap (%s)\n", xkbfile);
>  #ifdef WIN32
>          /* remove the temporary file */
>          unlink(tmpname);
> @@ -375,7 +455,6 @@ unsigned  missing;
>       DebugF("Loaded XKB keymap %s, 
> defined=0x%x\n",fileName,(*xkbRtrn)->defined);
>      }
>      fclose(file);
> -    (void) unlink (fileName);
>      return (need|want)&(~missing);
>  }
>  

-- 
        -Alan Coopersmith-           alan.coopersm...@sun.com
         Sun Microsystems, Inc. - X Window System Engineering

_______________________________________________
xorg mailing list
xorg@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/xorg

Reply via email to