1 /* 2 * The PCI Library -- ID to Name Cache 3 * 4 * Copyright (c) 2008--2009 Martin Mares <[email protected]> 5 * 6 * Can be freely distributed and used under the terms of the GNU GPL v2+. 7 * 8 * SPDX-License-Identifier: GPL-2.0-or-later 9 */ 10 11 #include "internal.h" 12 #include "names.h" 13 14 #ifdef PCI_USE_DNS 15 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <string.h> 19 #include <errno.h> 20 #include <sys/types.h> 21 #include <sys/stat.h> 22 #include <pwd.h> 23 #include <unistd.h> 24 25 static const char cache_version[] = "#PCI-CACHE-1.0"; 26 27 static char *get_cache_name(struct pci_access *a) 28 { 29 if (!a->id_cache_name) 30 { 31 char *name = pci_get_param(a, "net.cache_name"); 32 if (!name || !name[0]) 33 return NULL; 34 35 if (strncmp(name, "~/", 2)) 36 a->id_cache_name = pci_strdup(a, name); 37 else 38 { 39 uid_t uid = getuid(); 40 struct passwd *pw = getpwuid(uid); 41 if (!pw) 42 return name; 43 44 a->id_cache_name = pci_malloc(a, strlen(pw->pw_dir) + strlen(name+1) + 1); 45 sprintf(a->id_cache_name, "%s%s", pw->pw_dir, name+1); 46 } 47 } 48 49 return a->id_cache_name; 50 } 51 52 static void create_parent_dirs(struct pci_access *a, char *name) 53 { 54 // Assumes that we have a private copy of the name we can modify 55 56 char *p = name + strlen(name); 57 while (p > name && *p != '/') 58 p--; 59 if (p == name) 60 return; 61 62 while (p > name) 63 { 64 // We stand at a slash. Check if the current prefix exists. 65 *p = 0; 66 struct stat st; 67 int res = stat(name, &st); 68 *p = '/'; 69 if (res >= 0) 70 break; 71 72 // Does not exist yet, move up one directory 73 p--; 74 while (p > name && *p != '/') 75 p--; 76 } 77 78 // We now stand at the end of the longest existing prefix. 79 // Create all directories to the right of it. 80 for (;;) 81 { 82 p++; 83 while (*p && *p != '/') 84 p++; 85 if (!*p) 86 break; 87 88 *p = 0; 89 int res = mkdir(name, 0777); 90 if (res < 0) 91 { 92 a->warning("Cannot create directory %s: %s", name, strerror(errno)); 93 *p = '/'; 94 break; 95 } 96 *p = '/'; 97 } 98 } 99 100 int 101 pci_id_cache_load(struct pci_access *a, int flags) 102 { 103 char *name; 104 char line[MAX_LINE]; 105 FILE *f; 106 int lino; 107 108 if (a->id_cache_status > 0) 109 return 0; 110 a->id_cache_status = 1; 111 112 name = get_cache_name(a); 113 if (!name) 114 return 0; 115 a->debug("Using cache %s\n", name); 116 117 if (flags & PCI_LOOKUP_REFRESH_CACHE) 118 { 119 a->debug("Not loading cache, will refresh everything\n"); 120 a->id_cache_status = 2; 121 return 0; 122 } 123 124 f = fopen(name, "rb"); 125 if (!f) 126 { 127 a->debug("Cache file does not exist\n"); 128 return 0; 129 } 130 /* FIXME: Compare timestamp with the pci.ids file? */ 131 132 lino = 0; 133 while (fgets(line, sizeof(line), f)) 134 { 135 char *p = strchr(line, '\n'); 136 lino++; 137 if (p) 138 { 139 *p = 0; 140 if (lino == 1) 141 { 142 if (strcmp(line, cache_version)) 143 { 144 a->debug("Unrecognized cache version %s, ignoring\n", line); 145 break; 146 } 147 continue; 148 } 149 else 150 { 151 int cat, id1, id2, id3, id4, cnt; 152 if (sscanf(line, "%d%x%x%x%x%n", &cat, &id1, &id2, &id3, &id4, &cnt) >= 5) 153 { 154 p = line + cnt; 155 while (*p && *p == ' ') 156 p++; 157 pci_id_insert(a, cat, id1, id2, id3, id4, p, SRC_CACHE); 158 continue; 159 } 160 } 161 } 162 a->warning("Malformed cache file %s (line %d), ignoring", name, lino); 163 break; 164 } 165 166 if (ferror(f)) 167 a->warning("Error while reading %s", name); 168 fclose(f); 169 return 1; 170 } 171 172 void 173 pci_id_cache_flush(struct pci_access *a) 174 { 175 int orig_status = a->id_cache_status; 176 FILE *f; 177 unsigned int h; 178 struct id_entry *e, *e2; 179 char hostname[256], *tmpname, *name; 180 int this_pid; 181 182 a->id_cache_status = 0; 183 if (orig_status < 2) 184 return; 185 name = get_cache_name(a); 186 if (!name) 187 return; 188 189 create_parent_dirs(a, name); 190 191 this_pid = getpid(); 192 if (gethostname(hostname, sizeof(hostname)) < 0) 193 hostname[0] = 0; 194 else 195 hostname[sizeof(hostname)-1] = 0; 196 tmpname = pci_malloc(a, strlen(name) + strlen(hostname) + 64); 197 sprintf(tmpname, "%s.tmp-%s-%d", name, hostname, this_pid); 198 199 f = fopen(tmpname, "wb"); 200 if (!f) 201 { 202 a->warning("Cannot write to %s: %s", name, strerror(errno)); 203 pci_mfree(tmpname); 204 return; 205 } 206 a->debug("Writing cache to %s\n", name); 207 fprintf(f, "%s\n", cache_version); 208 209 for (h=0; h<HASH_SIZE; h++) 210 for (e=a->id_hash[h]; e; e=e->next) 211 if (e->src == SRC_CACHE || e->src == SRC_NET) 212 { 213 /* Negative entries are not written */ 214 if (!e->name[0]) 215 continue; 216 217 /* Verify that every entry is written at most once */ 218 for (e2=a->id_hash[h]; e2 != e; e2=e2->next) 219 if ((e2->src == SRC_CACHE || e2->src == SRC_NET) && 220 e2->cat == e->cat && 221 e2->id12 == e->id12 && e2->id34 == e->id34) 222 break; 223 if (e2 == e) 224 fprintf(f, "%d %x %x %x %x %s\n", 225 e->cat, 226 pair_first(e->id12), pair_second(e->id12), 227 pair_first(e->id34), pair_second(e->id34), 228 e->name); 229 } 230 231 fflush(f); 232 if (ferror(f)) 233 a->warning("Error writing %s", name); 234 fclose(f); 235 236 if (rename(tmpname, name) < 0) 237 { 238 a->warning("Cannot rename %s to %s: %s", tmpname, name, strerror(errno)); 239 unlink(tmpname); 240 } 241 pci_mfree(tmpname); 242 } 243 244 #else 245 246 int pci_id_cache_load(struct pci_access *a UNUSED, int flags UNUSED) 247 { 248 a->id_cache_status = 1; 249 return 0; 250 } 251 252 void pci_id_cache_flush(struct pci_access *a) 253 { 254 a->id_cache_status = 0; 255 pci_mfree(a->id_cache_name); 256 a->id_cache_name = NULL; 257 } 258 259 #endif 260 261 void 262 pci_id_cache_dirty(struct pci_access *a) 263 { 264 if (a->id_cache_status >= 1) 265 a->id_cache_status = 2; 266 } 267