rtld: Add two special directives to libmap.conf
authorJohn Marino <draco@marino.st>
Fri, 4 May 2012 16:54:27 +0000 (18:54 +0200)
committerJohn Marino <draco@marino.st>
Sat, 5 May 2012 07:33:18 +0000 (09:33 +0200)
include <file>:
    Parse the contents of file before continuing with the current file.

includedir <dir>:
    Parse the contents of every file in dir that ends in .conf before
    continuing with the current file.

Any file or directory encountered while processing include or includedir
directives will be parsed exactly once, even if it is encountered multiple
times.

Taken from FreeBSD SVN 234851 (30 APR 2012) with modification:

1) DragonFly realpath works differently than FreeBSD's and doesn't
   accept a null value for the resolved_path argument.
2) FreeBSD's debug lines reflect the wrong function, lm_init, instead
   of lmc_parse_file.  lmc_parse_dir also calls lmc_parse_file, so
   the debug message is definitely wrong and was corrected.
3) FreeBSD keeps using path even after determining realpath and putting
   the result in the rpath variable.  It uses path for debug messages
   and opening a file descriptor.  DragonFly doesn't use path again and
   only uses rpath after it is determined.
4) FreeBSD's lmc_parse_file code had a bug in the linked list used to
   track which conf files had already been parsed.  Memory for the
   filename was allocated so it wouldn't get overwritten after multiple
   passes, which is standard for the includedir functionality.

libexec/rtld-elf/libmap.c
share/man/man5/libmap.conf.5

index 2851a11..b5ee79e 100644 (file)
@@ -2,11 +2,13 @@
  * $FreeBSD$
  */
 
+#include <sys/types.h>
 #include <sys/param.h>
 #include <sys/fcntl.h>
 #include <sys/mman.h>
 #include <sys/queue.h>
 #include <sys/stat.h>
+#include <dirent.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
@@ -34,9 +36,17 @@ struct lmp {
        TAILQ_ENTRY(lmp) lmp_link;
 };
 
+static TAILQ_HEAD(lmc_list, lmc) lmc_head = TAILQ_HEAD_INITIALIZER(lmc_head);
+struct lmc {
+       char *path;
+       TAILQ_ENTRY(lmc) next;
+};
+
 static int lm_count;
 
 static void lmc_parse(char *, size_t);
+static void lmc_parse_file(char *);
+static void lmc_parse_dir(char *);
 static void lm_add(const char *, const char *, const char *);
 static void lm_free(struct lm_list *);
 static char *lml_find(struct lm_list *, const char *);
@@ -56,37 +66,13 @@ static const char *quickbasename(const char *);
 int
 lm_init(char *libmap_override)
 {
-       struct stat st;
-       char *lm_map, *p;
-       int fd;
+       char *p;
 
        dbg("lm_init(\"%s\")", libmap_override);
        TAILQ_INIT(&lmp_head);
 
-       fd = open(_PATH_LIBMAP_CONF, O_RDONLY);
-       if (fd == -1) {
-               dbg("lm_init: open(\"%s\") failed, %s", _PATH_LIBMAP_CONF,
-                   rtld_strerror(errno));
-               goto override;
-       }
-       if (fstat(fd, &st) == -1) {
-               close(fd);
-               dbg("lm_init: fstat(\"%s\") failed, %s", _PATH_LIBMAP_CONF,
-                   rtld_strerror(errno));
-               goto override;
-       }
-       lm_map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
-       if (lm_map == (const char *)MAP_FAILED) {
-               close(fd);
-               dbg("lm_init: mmap(\"%s\") failed, %s", _PATH_LIBMAP_CONF,
-                   rtld_strerror(errno));
-               goto override;
-       }
-       close(fd);
-       lmc_parse(lm_map, st.st_size);
-       munmap(lm_map, st.st_size);
+       lmc_parse_file(_PATH_LIBMAP_CONF);
 
-override:
        if (libmap_override) {
                /*
                 * Do some character replacement to make $LIBMAP look
@@ -110,12 +96,117 @@ override:
        return (lm_count == 0);
 }
 
+static void
+lmc_parse_file(char *path)
+{
+       struct lmc *p;
+       struct stat st;
+       int fd;
+       char *rpath;
+       char *lm_map;
+       char resolved[MAXPATHLEN];
+
+       rpath = realpath(path, resolved);
+       if (rpath == NULL)
+               return;
+
+       TAILQ_FOREACH(p, &lmc_head, next) {
+               if (strcmp(p->path, rpath) == 0) {
+                       free(rpath);
+                       return;
+               }
+       }
+
+       fd = open(rpath, O_RDONLY);
+       if (fd == -1) {
+               dbg("lm_parse_file: open(\"%s\") failed, %s", rpath,
+                   rtld_strerror(errno));
+               free(rpath);
+               return;
+       }
+       if (fstat(fd, &st) == -1) {
+               close(fd);
+               dbg("lm_parse_file: fstat(\"%s\") failed, %s", rpath,
+                   rtld_strerror(errno));
+               free(rpath);
+               return;
+       }
+       lm_map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       if (lm_map == (const char *)MAP_FAILED) {
+               close(fd);
+               dbg("lm_parse_file: mmap(\"%s\") failed, %s", rpath,
+                   rtld_strerror(errno));
+               free(rpath);
+               return;
+       }
+       close(fd);
+       p = xmalloc(sizeof(struct lmc));
+       p->path = xmalloc(strlen(rpath)+1);
+       strcpy(p->path, rpath);
+       TAILQ_INSERT_HEAD(&lmc_head, p, next);
+       lmc_parse(lm_map, st.st_size);
+       munmap(lm_map, st.st_size);
+}
+
+static void
+lmc_parse_dir(char *idir)
+{
+       DIR *d;
+       struct dirent *dp;
+       struct lmc *p;
+       char conffile[MAXPATHLEN];
+       char resolved[MAXPATHLEN];
+       char *ext;
+       char *rpath;
+
+       rpath = realpath(idir, resolved);
+       if (rpath == NULL)
+               return;
+
+       TAILQ_FOREACH(p, &lmc_head, next) {
+               if (strcmp(p->path, rpath) == 0) {
+                       free(rpath);
+                       return;
+               }
+       }
+       d = opendir(idir);
+       if (d == NULL) {
+               free(rpath);
+               return;
+       }
+
+       p = xmalloc(sizeof(struct lmc));
+       p->path = rpath;
+       TAILQ_INSERT_HEAD(&lmc_head, p, next);
+
+       while ((dp = readdir(d)) != NULL) {
+               if (dp->d_ino == 0)
+                       continue;
+               if (dp->d_type != DT_REG)
+                       continue;
+               ext = strrchr(dp->d_name, '.');
+               if (ext == NULL)
+                       continue;
+               if (strcmp(ext, ".conf") != 0)
+                       continue;
+               if (strlcpy(conffile, idir, MAXPATHLEN) >= MAXPATHLEN)
+                       continue; /* too long */
+               if (strlcat(conffile, "/", MAXPATHLEN) >= MAXPATHLEN)
+                       continue; /* too long */
+               if (strlcat(conffile, dp->d_name, MAXPATHLEN) >= MAXPATHLEN)
+                       continue; /* too long */
+               lmc_parse_file(conffile);
+       }
+       closedir(d);
+}
+
 static void
 lmc_parse(char *lm_p, size_t lm_len)
 {
        char *cp, *f, *t, *c, *p;
        char prog[MAXPATHLEN];
-       char line[MAXPATHLEN + 2];
+       /* allow includedir + full length path */
+       char line[MAXPATHLEN + 13];
        size_t cnt;
        int i;
 
@@ -176,7 +267,8 @@ lmc_parse(char *lm_p, size_t lm_len)
                        while(rtld_isspace(*cp)) cp++;
                        if (!iseol(*cp)) continue;
 
-                       strcpy(prog, c);
+                       if (strlcpy(prog, c, sizeof prog) >= sizeof prog)
+                               continue;
                        p = prog;
                        continue;
                }
@@ -202,6 +294,11 @@ lmc_parse(char *lm_p, size_t lm_len)
                if (!iseol(*cp)) continue;
 
                *cp = '\0';
+               if (strcmp(f, "includedir") == 0)
+                       lmc_parse_dir(t);
+               else if (strcmp(f, "include") == 0)
+                       lmc_parse_file(t);
+               else
                lm_add(p, f, t);
        }
 }
@@ -227,9 +324,17 @@ void
 lm_fini (void)
 {
        struct lmp *lmp;
+       struct lmc *p;
 
        dbg("%s()", __func__);
 
+       while (!TAILQ_EMPTY(&lmc_head)) {
+               p = TAILQ_FIRST(&lmc_head);
+               TAILQ_REMOVE(&lmc_head, p, next);
+               free(p->path);
+               free(p);
+       }
+
        while (!TAILQ_EMPTY(&lmp_head)) {
                lmp = TAILQ_FIRST(&lmp_head);
                TAILQ_REMOVE(&lmp_head, lmp, lmp_link);
index 6d357ff..6a03c30 100644 (file)
@@ -22,9 +22,9 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $FreeBSD: head/share/man/man5/libmap.conf.5 157178 2006-03-27 14:37:54Z pav $
+.\" $FreeBSD: share/man/man5/libmap.conf.5 234851 2012-04-30 bapt $
 .\"
-.Dd February 20, 2011
+.Dd May 3, 2012
 .Dt LIBMAP.CONF 5
 .Os
 .Sh NAME
@@ -43,6 +43,27 @@ left hand side containing the mapping candidate and the right hand
 side containing the mapping.
 Dependencies are matched against candidates and replaced with the mappings.
 .Pp
+Two special directives are available:
+.Bl -tag -width indent
+.It Cm include Ar file
+Parse the contents of
+.Ar file
+before continuing with the current file.
+.It Cm includedir Ar dir
+Parse the contents of every file in
+.Ar dir
+that ends in
+.Pa .conf
+before continuing with the current file.
+.El
+.Pp
+Any file or directory encountered while processing
+.Cm include
+or
+.Cm includedir
+directives will be parsed exactly once, even if it is encountered
+multiple times.
+.Pp
 Constrained mappings may be specified by enclosing the name of the
 executable or library in brackets.
 All mappings following a constraint will only be evaluated for that constraint.