[PATCH] xfree86: Allow a config directory for multiple config files

Dan Nicholson dbn.lists at gmail.com
Fri Nov 27 09:34:00 PST 2009


Currently there is a single file, xorg.conf, for configuring the server.
This works fine most of the time, but it becomes a problem when packages
or system services need to adjust the configuration. Instead, allow
multiple configuration files to live in a directory. Typically this will
be /etc/X11/xorg.conf.d.

Right now this uses the same matching templates as the config file but
with a different default name. The matching code was refactored a bit to
make this more coherent. It also won't fall back to using the auto
configuration unless both the config file and config directory don't
exist.

Signed-off-by: Dan Nicholson <dbn.lists at gmail.com>
---
 hw/xfree86/common/xf86Config.c |   29 +++--
 hw/xfree86/parser/scan.c       |  229 +++++++++++++++++++++++++++-------------
 hw/xfree86/parser/xf86Parser.h |   12 ++-
 hw/xwin/winconfig.c            |   34 ++++--
 4 files changed, 208 insertions(+), 96 deletions(-)

diff --git a/hw/xfree86/common/xf86Config.c b/hw/xfree86/common/xf86Config.c
index 40f65bd..04c4e01 100644
--- a/hw/xfree86/common/xf86Config.c
+++ b/hw/xfree86/common/xf86Config.c
@@ -2437,7 +2437,7 @@ checkInput(serverLayoutPtr layout, Bool implicit_layout) {
 ConfigStatus
 xf86HandleConfigFile(Bool autoconfig)
 {
-    const char *filename;
+    XF86ConfPathsPtr paths;
     char *searchpath;
     MessageType from = X_DEFAULT;
     char *scanptr;
@@ -2453,18 +2453,25 @@ xf86HandleConfigFile(Bool autoconfig)
 	if (xf86ConfigFile)
 	    from = X_CMDLINE;
 
-	filename = xf86openConfigFile(searchpath, xf86ConfigFile, PROJECTROOT);
-	if (filename) {
-	    xf86MsgVerb(from, 0, "Using config file: \"%s\"\n", filename);
-	    xf86ConfigFile = xnfstrdup(filename);
-	} else {
-	    if (xf86ConfigFile)
-		xf86Msg(X_ERROR, "Unable to locate/open config file: \"%s\"\n",
-			xf86ConfigFile);
+	paths = xf86openConfigFile(searchpath, xf86ConfigFile, PROJECTROOT);
+	if (paths && (paths->file || paths->dir)) {
+	    if (paths->file) {
+		xf86MsgVerb(from, 0, "Using config file: \"%s\"\n",
+			    paths->file);
+		xf86ConfigFile = xnfstrdup(paths->file);
+	    } else {
+		if (xf86ConfigFile)
+		    xf86Msg(X_ERROR,
+			    "Unable to locate/open config file: \"%s\"\n",
+			    xf86ConfigFile);
+	    }
+	    if (paths && paths->dir)
+		xf86MsgVerb(X_DEFAULT, 0, "Using config directory: \"%s\"\n",
+			    paths->dir);
+	} else
 	    return CONFIG_NOFILE;
-	}
     }
-     
+
     if ((xf86configptr = xf86readConfigFile ()) == NULL) {
 	xf86Msg(X_ERROR, "Problem parsing the config file\n");
 	return CONFIG_PARSE_ERROR;
diff --git a/hw/xfree86/parser/scan.c b/hw/xfree86/parser/scan.c
index 270dbd5..659b9d0 100644
--- a/hw/xfree86/parser/scan.c
+++ b/hw/xfree86/parser/scan.c
@@ -62,6 +62,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
 #include <unistd.h>
 #include <stdarg.h>
 #include <X11/Xfuncproto.h>
@@ -90,17 +92,23 @@
 #include "xf86tokens.h"
 
 #define CONFIG_BUF_LEN     1024
+#define CONFIG_MAX_FILES   64
 
 static int StringToToken (char *, xf86ConfigSymTabRec *);
 
-static FILE *configFile = NULL;
+static struct {
+	FILE *file;
+	char *path;
+} configFiles[CONFIG_MAX_FILES];
 static const char **builtinConfig = NULL;
 static int builtinIndex = 0;
 static int configPos = 0;		/* current readers position */
 static int configLineNo = 0;	/* linenumber */
 static char *configBuf, *configRBuf;	/* buffer for lines */
-static char *configPath;		/* path to config file */
+static XF86ConfPathsRec configPaths = { NULL };	/* paths to configuration */
 static char *configSection = NULL;	/* name of current section being parsed */
+static int numFiles = 0;		/* number of config files */
+static int curFileIndex = 0;		/* index of current config file */
 static int pushToken = LOCK_TOKEN;
 static int eol_seen = 0;		/* private state to handle comments */
 LexRec val;
@@ -155,7 +163,7 @@ xf86strToUL (char *str)
 /*
  * xf86getNextLine --
  *
- *  read from the configFile FILE stream until we encounter a new
+ *  read from the configFiles FILE stream until we encounter a new
  *  line; this is effectively just a big wrapper for fgets(3).
  *
  *  xf86getToken() assumes that we will read up to the next
@@ -213,9 +221,18 @@ xf86getNextLine(void)
 	/* read in another block of chars */
 
 	do {
-		ret = fgets(configBuf + pos, configBufLen - pos - 1, configFile);
+		ret = fgets(configBuf + pos, configBufLen - pos - 1,
+			    configFiles[curFileIndex].file);
 
-		if (!ret) break;
+		if (!ret) {
+			/* stop if there are no more files */
+			if (++curFileIndex >= numFiles) {
+				curFileIndex = 0;
+				break;
+			}
+			configLineNo = 0;
+			continue;
+		}
 
 		/* search for EOL in the new block of chars */
 
@@ -306,7 +323,7 @@ again:
 		if (!c)
 		{
 			char *ret;
-			if (configFile)
+			if (numFiles > 0)
 				ret = xf86getNextLine();
 			else {
 				if (builtinConfig[builtinIndex] == NULL)
@@ -575,6 +592,9 @@ xf86pathIsSafe(const char *path)
 #ifndef XCONFIGFILE
 #define XCONFIGFILE	"xorg.conf"
 #endif
+#ifndef XCONFIGDIR
+#define XCONFIGDIR	"xorg.conf.d"
+#endif
 #ifndef PROJECTROOT
 #define PROJECTROOT	"/usr/X11R6"
 #endif
@@ -616,7 +636,8 @@ xf86pathIsSafe(const char *path)
 
 static char *
 DoSubstitution(const char *template, const char *cmdline, const char *projroot,
-				int *cmdlineUsed, int *envUsed, char *XConfigFile)
+				int *cmdlineUsed, int *envUsed,
+				const char *XConfigFile)
 {
 	char *result;
 	int i, l;
@@ -745,6 +766,102 @@ DoSubstitution(const char *template, const char *cmdline, const char *projroot,
 	return result;
 }
 
+static char *
+DoConfigFile(const char *path, const char *cmdline, const char *projroot,
+	     const char *confname)
+{
+	char *filepath = NULL;
+	char *pathcopy;
+	const char *template;
+	int cmdlineUsed = 0;
+	FILE *file = NULL;
+
+	pathcopy = strdup(path);
+	template = strtok(pathcopy, ",");
+	while (template && !file) {
+		if ((filepath = DoSubstitution(template, cmdline,
+					       projroot, &cmdlineUsed,
+					       NULL, confname))) {
+			if ((file = fopen(filepath, "r")) != 0) {
+				if (cmdline && !cmdlineUsed) {
+					fclose(file);
+					file = NULL;
+				}
+			}
+		}
+		if (filepath && !file) {
+			free(filepath);
+			filepath = NULL;
+		}
+		template = strtok(NULL, ",");
+	}
+
+	if (file) {
+		configFiles[numFiles].file = file;
+		configFiles[numFiles].path = strdup(filepath);
+		numFiles++;
+	}
+	return filepath;
+}
+
+static char *
+DoConfigDir(const char *path, const char *projroot, const char *confname)
+{
+	char *dirpath, *pathcopy;
+	const char *template;
+	DIR *dir = NULL;
+
+	pathcopy = strdup(path);
+	template = strtok(pathcopy, ",");
+	while (template && !dir) {
+		if ((dirpath = DoSubstitution(template, NULL, projroot,
+					      NULL, NULL, confname))) {
+			if ((dir = opendir(dirpath))) {
+				struct dirent *de;
+				char *name, *path;
+				size_t len;
+
+				/* match files named *.conf */
+				while ((de = readdir(dir))) {
+					name = de->d_name;
+					len = strlen(name);
+					if (name[0] == '.')
+						continue;
+					if (len <= 5)
+						continue;
+					if (strcmp(&name[len-5], ".conf") != 0)
+						continue;
+					path = malloc(PATH_MAX + 1);
+					snprintf(path, PATH_MAX + 1, "%s/%s",
+						 dirpath, name);
+					configFiles[numFiles].file =
+						fopen(path, "r");
+					if (configFiles[numFiles].file)
+						configFiles[numFiles++].path =
+							path;
+					else
+						free(path);
+					if (numFiles >= CONFIG_MAX_FILES) {
+						ErrorF("Maximum number of "
+						       "configuration files "
+						       "opened\n");
+						break;
+					}
+				}
+			}
+		}
+		if (dirpath && !dir) {
+			free(dirpath);
+			dirpath = NULL;
+		}
+		template = strtok(NULL, ",");
+	}
+
+	if (dir)
+		closedir(dir);
+	return dirpath;
+}
+
 /* 
  * xf86openConfigFile --
  *
@@ -777,107 +894,69 @@ DoSubstitution(const char *template, const char *cmdline, const char *projroot,
 							"%P/lib/X11/%X"
 #endif
 
-const char *
+const XF86ConfPathsPtr
 xf86openConfigFile(const char *path, const char *cmdline, const char *projroot)
 {
-	char *pathcopy;
-	const char *template;
-	int cmdlineUsed = 0;
-
-	configFile = NULL;
+	memset(configFiles, 0, sizeof(configFiles));
+	numFiles = 0;
+	curFileIndex = 0;
 	configPos = 0;		/* current readers position */
 	configLineNo = 0;	/* linenumber */
 	pushToken = LOCK_TOKEN;
 
 	if (!path || !path[0])
 		path = DEFAULT_CONF_PATH;
-	pathcopy = malloc(strlen(path) + 1);
-	strcpy(pathcopy, path);
 	if (!projroot || !projroot[0])
 		projroot = PROJECTROOT;
 
-	template = strtok(pathcopy, ",");
-
 	/* First, search for a config file. */
-	while (template && !configFile) {
-		if ((configPath = DoSubstitution(template, cmdline, projroot,
-						 &cmdlineUsed, NULL,
-						 XCONFIGFILE))) {
-			if ((configFile = fopen(configPath, "r")) != 0) {
-				if (cmdline && !cmdlineUsed) {
-					fclose(configFile);
-					configFile = NULL;
-				}
-			}
-		}
-		if (configPath && !configFile) {
-			free(configPath);
-			configPath = NULL;
-		}
-		template = strtok(NULL, ",");
-	}
-	
-	/* Then search for fallback */
-	if (!configFile) {
-	    strcpy(pathcopy, path);
-	    template = strtok(pathcopy, ",");
-
-	    while (template && !configFile) {
-		if ((configPath = DoSubstitution(template, cmdline, projroot,
-						 &cmdlineUsed, NULL,
-						 XFREE86CFGFILE))) {
-		    if ((configFile = fopen(configPath, "r")) != 0) {
-			if (cmdline && !cmdlineUsed) {
-			    fclose(configFile);
-			    configFile = NULL;
-			}
-		    }
-		}
-		if (configPath && !configFile) {
-		    free(configPath);
-		    configPath = NULL;
-		}
-		template = strtok(NULL, ",");
-	    }
-	}
-	
-	free(pathcopy);
-	if (!configFile) {
+	configPaths.file = DoConfigFile(path, cmdline, projroot, XCONFIGFILE);
 
-		return NULL;
-	}
+	/* Then search for fallback */
+	if (!configPaths.file)
+		configPaths.file = DoConfigFile(path, cmdline, projroot,
+						XFREE86CFGFILE);
+	/* Search for the multiconf directory */
+	configPaths.dir = DoConfigDir(path, projroot, XCONFIGDIR);
 
 	configBuf = malloc (CONFIG_BUF_LEN);
 	configRBuf = malloc (CONFIG_BUF_LEN);
 	configBuf[0] = '\0';		/* sanity ... */
 
-	return configPath;
+	return &configPaths;
 }
 
 void
 xf86closeConfigFile (void)
 {
-	free (configPath);
-	configPath = NULL;
+	int i;
+
+	free (configPaths.file);
+	configPaths.file = NULL;
+	free (configPaths.dir);
+	configPaths.dir = NULL;
 	free (configRBuf);
 	configRBuf = NULL;
 	free (configBuf);
 	configBuf = NULL;
 
-	if (configFile) {
-		fclose (configFile);
-		configFile = NULL;
-	} else {
+	if (numFiles == 0) {
 		builtinConfig = NULL;
 		builtinIndex = 0;
 	}
+	for (i = 0; i < numFiles; i++) {
+		fclose(configFiles[i].file);
+		configFiles[i].file = NULL;
+		free(configFiles[i].path);
+		configFiles[i].path = NULL;
+	}
+	numFiles = 0;
 }
 
 void
 xf86setBuiltinConfig(const char *config[])
 {
 	builtinConfig = config;
-	configPath = strdup("<builtin configuration>");
 	configBuf = malloc (CONFIG_BUF_LEN);
 	configRBuf = malloc (CONFIG_BUF_LEN);
 	configBuf[0] = '\0';		/* sanity ... */
@@ -888,9 +967,11 @@ void
 xf86parseError (char *format,...)
 {
 	va_list ap;
+	char *filename = numFiles ? configFiles[curFileIndex].path :
+			 "<builtin configuration>";
 
 	ErrorF ("Parse error on line %d of section %s in file %s\n\t",
-		 configLineNo, configSection, configPath);
+		 configLineNo, configSection, filename);
 	va_start (ap, format);
 	VErrorF (format, ap);
 	va_end (ap);
@@ -902,8 +983,10 @@ void
 xf86validationError (char *format,...)
 {
 	va_list ap;
+	char *filename = numFiles ? configFiles[curFileIndex].path :
+			 "<builtin configuration>";
 
-	ErrorF ("Data incomplete in file %s\n\t", configPath);
+	ErrorF ("Data incomplete in file %s\n\t", filename);
 	va_start (ap, format);
 	VErrorF (format, ap);
 	va_end (ap);
diff --git a/hw/xfree86/parser/xf86Parser.h b/hw/xfree86/parser/xf86Parser.h
index b4837d5..16b841a 100644
--- a/hw/xfree86/parser/xf86Parser.h
+++ b/hw/xfree86/parser/xf86Parser.h
@@ -476,11 +476,19 @@ typedef struct
 }
 xf86ConfigSymTabRec, *xf86ConfigSymTabPtr;
 
+typedef struct
+{
+	char *file;	/* config file */
+	char *dir;	/* multiconf directory */
+}
+XF86ConfPathsRec, *XF86ConfPathsPtr;
+
 /*
  * prototypes for public functions
  */
-extern _X_EXPORT const char *xf86openConfigFile (const char *, const char *,
-					const char *);
+extern _X_EXPORT const XF86ConfPathsPtr xf86openConfigFile(const char *path,
+							const char *cmdline,
+							const char *projroot);
 extern _X_EXPORT void xf86setBuiltinConfig(const char *config[]);
 extern _X_EXPORT XF86ConfigPtr xf86readConfigFile (void);
 extern _X_EXPORT void xf86closeConfigFile (void);
diff --git a/hw/xwin/winconfig.c b/hw/xwin/winconfig.c
index 3e1908c..63ead08 100644
--- a/hw/xwin/winconfig.c
+++ b/hw/xwin/winconfig.c
@@ -109,7 +109,9 @@ Bool
 winReadConfigfile ()
 {
   Bool		retval = TRUE;
+  XF86ConfPathsPtr	paths;
   const char	*filename;
+  const char	*dirname;
   MessageType	from = X_DEFAULT;
   char		*xf86ConfigFile = NULL;
 
@@ -121,24 +123,36 @@ winReadConfigfile ()
 
   /* Parse config file into data structure */
 
-  filename = xf86openConfigFile (CONFIGPATH, xf86ConfigFile, PROJECTROOT);
-    
+  paths = xf86openConfigFile (CONFIGPATH, xf86ConfigFile, PROJECTROOT);
+
   /* Hack for backward compatibility */
-  if (!filename && from == X_DEFAULT)
-    filename = xf86openConfigFile (CONFIGPATH, "XF86Config", PROJECTROOT);
+  if (!(paths && paths->file) && from == X_DEFAULT)
+    paths = xf86openConfigFile (CONFIGPATH, "XF86Config", PROJECTROOT);
 
-  if (filename)
+  if (paths && (paths->file || paths->dir))
     {
-      winMsg (from, "Using config file: \"%s\"\n", filename);
+      if (paths && paths->file)
+	{
+	  winMsg (from, "Using config file: \"%s\"\n", paths->file);
+	}
+      else
+	{
+	  winMsg (X_ERROR, "Unable to locate/open config file");
+	  if (xf86ConfigFile)
+	    ErrorF (": \"%s\"", xf86ConfigFile);
+	  ErrorF ("\n");
+	}
+
+      if (paths && paths->dir)
+	{
+	  winMsg (X_DEFAULT, "Using config directory \"%s\"\n", paths->dir);
+	}
     }
   else
     {
-      winMsg (X_ERROR, "Unable to locate/open config file");
-      if (xf86ConfigFile)
-	ErrorF (": \"%s\"", xf86ConfigFile);
-      ErrorF ("\n");
       return FALSE;
     }
+
   if ((g_xf86configptr = xf86readConfigFile ()) == NULL)
     {
       winMsg (X_ERROR, "Problem parsing the config file\n");
-- 
1.6.2.5



More information about the xorg-devel mailing list