]> mj.ucw.cz Git - pciutils.git/blobdiff - lib/names-cache.c
libpci: Do not build physmem-posix.c when not needed
[pciutils.git] / lib / names-cache.c
index 09f29b2ecb938e5cb0cd9d5e6d70b7a12be1be45..16e9e9af2faf0cfa10c447e458ff7c70cd0a3f74 100644 (file)
 /*
  *     The PCI Library -- ID to Name Cache
  *
- *     Copyright (c) 2008 Martin Mares <mj@ucw.cz>
+ *     Copyright (c) 2008--2009 Martin Mares <mj@ucw.cz>
  *
- *     Can be freely distributed and used under the terms of the GNU GPL.
+ *     Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ *     SPDX-License-Identifier: GPL-2.0-or-later
  */
 
+#include "internal.h"
+#include "names.h"
+
+#ifdef PCI_USE_DNS
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <pwd.h>
 #include <unistd.h>
 
-#include "internal.h"
-#include "names.h"
+static const char cache_version[] = "#PCI-CACHE-1.0";
 
-#ifdef PCI_USE_DNS
+static char *get_cache_name(struct pci_access *a)
+{
+  if (!a->id_cache_name)
+    {
+      char *name = pci_get_param(a, "net.cache_name");
+      if (!name || !name[0])
+       return NULL;
 
-static const char cache_version[] = "#PCI-CACHE-1.0";
+      if (strncmp(name, "~/", 2))
+       a->id_cache_name = pci_strdup(a, name);
+      else
+       {
+         uid_t uid = getuid();
+         struct passwd *pw = getpwuid(uid);
+         if (!pw)
+           return name;
+
+         a->id_cache_name = pci_malloc(a, strlen(pw->pw_dir) + strlen(name+1) + 1);
+         sprintf(a->id_cache_name, "%s%s", pw->pw_dir, name+1);
+       }
+    }
+
+  return a->id_cache_name;
+}
+
+static void create_parent_dirs(struct pci_access *a, char *name)
+{
+  // Assumes that we have a private copy of the name we can modify
+
+  char *p = name + strlen(name);
+  while (p > name && *p != '/')
+    p--;
+  if (p == name)
+    return;
+
+  while (p > name)
+    {
+      // We stand at a slash. Check if the current prefix exists.
+      *p = 0;
+      struct stat st;
+      int res = stat(name, &st);
+      *p = '/';
+      if (res >= 0)
+       break;
+
+      // Does not exist yet, move up one directory
+      p--;
+      while (p > name && *p != '/')
+       p--;
+    }
+
+  // We now stand at the end of the longest existing prefix.
+  // Create all directories to the right of it.
+  for (;;)
+    {
+      p++;
+      while (*p && *p != '/')
+       p++;
+      if (!*p)
+       break;
+
+      *p = 0;
+      int res = mkdir(name, 0777);
+      if (res < 0)
+       {
+         a->warning("Cannot create directory %s: %s", name, strerror(errno));
+         *p = '/';
+         break;
+       }
+      *p = '/';
+    }
+}
 
 int
 pci_id_cache_load(struct pci_access *a, int flags)
 {
   char *name;
   char line[MAX_LINE];
-  const char default_name[] = "/.pciids-cache";
   FILE *f;
   int lino;
 
+  if (a->id_cache_status > 0)
+    return 0;
   a->id_cache_status = 1;
-  if (!a->id_cache_file)
-    {
-      /* Construct the default ID cache name */
-      uid_t uid = getuid();
-      struct passwd *pw = getpwuid(uid);
-      if (!pw)
-        return 0;
-      name = pci_malloc(a, strlen(pw->pw_dir) + sizeof(default_name));
-      sprintf(name, "%s%s", pw->pw_dir, default_name);
-      pci_set_id_cache(a, name, 1);
-    }
-  a->debug("Using cache %s\n", a->id_cache_file);
+
+  name = get_cache_name(a);
+  if (!name)
+    return 0;
+  a->debug("Using cache %s\n", name);
+
   if (flags & PCI_LOOKUP_REFRESH_CACHE)
     {
       a->debug("Not loading cache, will refresh everything\n");
@@ -50,7 +121,7 @@ pci_id_cache_load(struct pci_access *a, int flags)
       return 0;
     }
 
-  f = fopen(a->id_cache_file, "rb");
+  f = fopen(name, "rb");
   if (!f)
     {
       a->debug("Cache file does not exist\n");
@@ -88,12 +159,12 @@ pci_id_cache_load(struct pci_access *a, int flags)
                }
            }
        }
-      a->warning("Malformed cache file %s (line %d), ignoring", a->id_cache_file, lino);
+      a->warning("Malformed cache file %s (line %d), ignoring", name, lino);
       break;
     }
 
   if (ferror(f))
-    a->warning("Error while reading %s", a->id_cache_file);
+    a->warning("Error while reading %s", name);
   fclose(f);
   return 1;
 }
@@ -105,25 +176,44 @@ pci_id_cache_flush(struct pci_access *a)
   FILE *f;
   unsigned int h;
   struct id_entry *e, *e2;
+  char hostname[256], *tmpname, *name;
+  int this_pid;
 
   a->id_cache_status = 0;
   if (orig_status < 2)
     return;
-  if (!a->id_cache_file)
+  name = get_cache_name(a);
+  if (!name)
     return;
-  f = fopen(a->id_cache_file, "wb");
+
+  create_parent_dirs(a, name);
+
+  this_pid = getpid();
+  if (gethostname(hostname, sizeof(hostname)) < 0)
+    hostname[0] = 0;
+  else
+    hostname[sizeof(hostname)-1] = 0;
+  tmpname = pci_malloc(a, strlen(name) + strlen(hostname) + 64);
+  sprintf(tmpname, "%s.tmp-%s-%d", name, hostname, this_pid);
+
+  f = fopen(tmpname, "wb");
   if (!f)
     {
-      a->warning("Cannot write %s: %s", a->id_cache_file, strerror(errno));
+      a->warning("Cannot write to %s: %s", name, strerror(errno));
+      pci_mfree(tmpname);
       return;
     }
-  a->debug("Writing cache to %s\n", a->id_cache_file);
+  a->debug("Writing cache to %s\n", name);
   fprintf(f, "%s\n", cache_version);
 
   for (h=0; h<HASH_SIZE; h++)
     for (e=a->id_hash[h]; e; e=e->next)
       if (e->src == SRC_CACHE || e->src == SRC_NET)
        {
+         /* Negative entries are not written */
+         if (!e->name[0])
+           continue;
+
          /* Verify that every entry is written at most once */
          for (e2=a->id_hash[h]; e2 != e; e2=e2->next)
            if ((e2->src == SRC_CACHE || e2->src == SRC_NET) &&
@@ -140,8 +230,15 @@ pci_id_cache_flush(struct pci_access *a)
 
   fflush(f);
   if (ferror(f))
-    a->warning("Error writing %s", a->id_cache_file);
+    a->warning("Error writing %s", name);
   fclose(f);
+
+  if (rename(tmpname, name) < 0)
+    {
+      a->warning("Cannot rename %s to %s: %s", tmpname, name, strerror(errno));
+      unlink(tmpname);
+    }
+  pci_mfree(tmpname);
 }
 
 #else
@@ -155,6 +252,8 @@ int pci_id_cache_load(struct pci_access *a UNUSED, int flags UNUSED)
 void pci_id_cache_flush(struct pci_access *a)
 {
   a->id_cache_status = 0;
+  pci_mfree(a->id_cache_name);
+  a->id_cache_name = NULL;
 }
 
 #endif
@@ -165,12 +264,3 @@ pci_id_cache_dirty(struct pci_access *a)
   if (a->id_cache_status >= 1)
     a->id_cache_status = 2;
 }
-
-void
-pci_set_id_cache(struct pci_access *a, char *name, int to_be_freed)
-{
-  if (a->free_id_cache_file)
-    free(a->id_cache_file);
-  a->id_cache_file = name;
-  a->free_id_cache_file = to_be_freed;
-}