]>
Commit | Line | Data |
---|---|---|
752d4d9a MM |
1 | /* |
2 | * The PCI Library -- ID to Name Cache | |
3 | * | |
fda7c18d | 4 | * Copyright (c) 2008--2009 Martin Mares <mj@ucw.cz> |
752d4d9a | 5 | * |
61829219 MM |
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 | |
752d4d9a MM |
9 | */ |
10 | ||
fda7c18d MM |
11 | #include "internal.h" |
12 | #include "names.h" | |
13 | ||
14 | #ifdef PCI_USE_DNS | |
15 | ||
752d4d9a MM |
16 | #include <stdio.h> |
17 | #include <stdlib.h> | |
18 | #include <string.h> | |
19 | #include <errno.h> | |
20 | #include <sys/types.h> | |
f022f467 | 21 | #include <sys/stat.h> |
752d4d9a MM |
22 | #include <pwd.h> |
23 | #include <unistd.h> | |
24 | ||
752d4d9a MM |
25 | static const char cache_version[] = "#PCI-CACHE-1.0"; |
26 | ||
98ccf6d6 MM |
27 | static char *get_cache_name(struct pci_access *a) |
28 | { | |
f022f467 MM |
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 | } | |
98ccf6d6 MM |
98 | } |
99 | ||
752d4d9a MM |
100 | int |
101 | pci_id_cache_load(struct pci_access *a, int flags) | |
102 | { | |
103 | char *name; | |
104 | char line[MAX_LINE]; | |
752d4d9a MM |
105 | FILE *f; |
106 | int lino; | |
107 | ||
f022f467 MM |
108 | if (a->id_cache_status > 0) |
109 | return 0; | |
752d4d9a | 110 | a->id_cache_status = 1; |
f022f467 | 111 | |
98ccf6d6 MM |
112 | name = get_cache_name(a); |
113 | if (!name) | |
114 | return 0; | |
115 | a->debug("Using cache %s\n", name); | |
f022f467 | 116 | |
752d4d9a MM |
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 | ||
98ccf6d6 | 124 | f = fopen(name, "rb"); |
752d4d9a MM |
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 | } | |
98ccf6d6 | 162 | a->warning("Malformed cache file %s (line %d), ignoring", name, lino); |
752d4d9a MM |
163 | break; |
164 | } | |
165 | ||
166 | if (ferror(f)) | |
98ccf6d6 | 167 | a->warning("Error while reading %s", name); |
752d4d9a MM |
168 | fclose(f); |
169 | return 1; | |
170 | } | |
171 | ||
752d4d9a MM |
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; | |
98ccf6d6 | 179 | char hostname[256], *tmpname, *name; |
61bc0b58 | 180 | int this_pid; |
752d4d9a MM |
181 | |
182 | a->id_cache_status = 0; | |
183 | if (orig_status < 2) | |
184 | return; | |
98ccf6d6 MM |
185 | name = get_cache_name(a); |
186 | if (!name) | |
752d4d9a | 187 | return; |
61bc0b58 | 188 | |
f022f467 MM |
189 | create_parent_dirs(a, name); |
190 | ||
61bc0b58 MM |
191 | this_pid = getpid(); |
192 | if (gethostname(hostname, sizeof(hostname)) < 0) | |
193 | hostname[0] = 0; | |
194 | else | |
195 | hostname[sizeof(hostname)-1] = 0; | |
98ccf6d6 MM |
196 | tmpname = pci_malloc(a, strlen(name) + strlen(hostname) + 64); |
197 | sprintf(tmpname, "%s.tmp-%s-%d", name, hostname, this_pid); | |
61bc0b58 MM |
198 | |
199 | f = fopen(tmpname, "wb"); | |
752d4d9a MM |
200 | if (!f) |
201 | { | |
98ccf6d6 | 202 | a->warning("Cannot write to %s: %s", name, strerror(errno)); |
61bc0b58 | 203 | pci_mfree(tmpname); |
752d4d9a MM |
204 | return; |
205 | } | |
98ccf6d6 | 206 | a->debug("Writing cache to %s\n", name); |
752d4d9a MM |
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 | { | |
61bc0b58 MM |
213 | /* Negative entries are not written */ |
214 | if (!e->name[0]) | |
215 | continue; | |
216 | ||
752d4d9a MM |
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)) | |
98ccf6d6 | 233 | a->warning("Error writing %s", name); |
752d4d9a | 234 | fclose(f); |
61bc0b58 | 235 | |
98ccf6d6 | 236 | if (rename(tmpname, name) < 0) |
61bc0b58 | 237 | { |
98ccf6d6 | 238 | a->warning("Cannot rename %s to %s: %s", tmpname, name, strerror(errno)); |
61bc0b58 MM |
239 | unlink(tmpname); |
240 | } | |
241 | pci_mfree(tmpname); | |
752d4d9a MM |
242 | } |
243 | ||
94d1b5e0 MM |
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; | |
f022f467 MM |
255 | pci_mfree(a->id_cache_name); |
256 | a->id_cache_name = NULL; | |
94d1b5e0 MM |
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 | } |