diff options
Diffstat (limited to '')
-rw-r--r-- | lib/names-cache.c | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/lib/names-cache.c b/lib/names-cache.c new file mode 100644 index 0000000..16e9e9a --- /dev/null +++ b/lib/names-cache.c @@ -0,0 +1,266 @@ +/* + * The PCI Library -- ID to Name Cache + * + * Copyright (c) 2008--2009 Martin Mares <mj@ucw.cz> + * + * 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> + +static const char cache_version[] = "#PCI-CACHE-1.0"; + +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; + + 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]; + FILE *f; + int lino; + + if (a->id_cache_status > 0) + return 0; + a->id_cache_status = 1; + + 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"); + a->id_cache_status = 2; + return 0; + } + + f = fopen(name, "rb"); + if (!f) + { + a->debug("Cache file does not exist\n"); + return 0; + } + /* FIXME: Compare timestamp with the pci.ids file? */ + + lino = 0; + while (fgets(line, sizeof(line), f)) + { + char *p = strchr(line, '\n'); + lino++; + if (p) + { + *p = 0; + if (lino == 1) + { + if (strcmp(line, cache_version)) + { + a->debug("Unrecognized cache version %s, ignoring\n", line); + break; + } + continue; + } + else + { + int cat, id1, id2, id3, id4, cnt; + if (sscanf(line, "%d%x%x%x%x%n", &cat, &id1, &id2, &id3, &id4, &cnt) >= 5) + { + p = line + cnt; + while (*p && *p == ' ') + p++; + pci_id_insert(a, cat, id1, id2, id3, id4, p, SRC_CACHE); + continue; + } + } + } + a->warning("Malformed cache file %s (line %d), ignoring", name, lino); + break; + } + + if (ferror(f)) + a->warning("Error while reading %s", name); + fclose(f); + return 1; +} + +void +pci_id_cache_flush(struct pci_access *a) +{ + int orig_status = a->id_cache_status; + 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; + name = get_cache_name(a); + if (!name) + return; + + 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 to %s: %s", name, strerror(errno)); + pci_mfree(tmpname); + return; + } + 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) && + e2->cat == e->cat && + e2->id12 == e->id12 && e2->id34 == e->id34) + break; + if (e2 == e) + fprintf(f, "%d %x %x %x %x %s\n", + e->cat, + pair_first(e->id12), pair_second(e->id12), + pair_first(e->id34), pair_second(e->id34), + e->name); + } + + fflush(f); + if (ferror(f)) + 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 + +int pci_id_cache_load(struct pci_access *a UNUSED, int flags UNUSED) +{ + a->id_cache_status = 1; + return 0; +} + +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 + +void +pci_id_cache_dirty(struct pci_access *a) +{ + if (a->id_cache_status >= 1) + a->id_cache_status = 2; +} |