xref: /pciutils/lib/names-cache.c (revision f022f467)
1752d4d9aSMartin Mares /*
2752d4d9aSMartin Mares  *	The PCI Library -- ID to Name Cache
3752d4d9aSMartin Mares  *
4fda7c18dSMartin Mares  *	Copyright (c) 2008--2009 Martin Mares <[email protected]>
5752d4d9aSMartin Mares  *
661829219SMartin Mares  *	Can be freely distributed and used under the terms of the GNU GPL v2+.
761829219SMartin Mares  *
861829219SMartin Mares  *	SPDX-License-Identifier: GPL-2.0-or-later
9752d4d9aSMartin Mares  */
10752d4d9aSMartin Mares 
11fda7c18dSMartin Mares #include "internal.h"
12fda7c18dSMartin Mares #include "names.h"
13fda7c18dSMartin Mares 
14fda7c18dSMartin Mares #ifdef PCI_USE_DNS
15fda7c18dSMartin Mares 
16752d4d9aSMartin Mares #include <stdio.h>
17752d4d9aSMartin Mares #include <stdlib.h>
18752d4d9aSMartin Mares #include <string.h>
19752d4d9aSMartin Mares #include <errno.h>
20752d4d9aSMartin Mares #include <sys/types.h>
21*f022f467SMartin Mares #include <sys/stat.h>
22752d4d9aSMartin Mares #include <pwd.h>
23752d4d9aSMartin Mares #include <unistd.h>
24752d4d9aSMartin Mares 
25752d4d9aSMartin Mares static const char cache_version[] = "#PCI-CACHE-1.0";
26752d4d9aSMartin Mares 
get_cache_name(struct pci_access * a)2798ccf6d6SMartin Mares static char *get_cache_name(struct pci_access *a)
2898ccf6d6SMartin Mares {
29*f022f467SMartin Mares   if (!a->id_cache_name)
30*f022f467SMartin Mares     {
31*f022f467SMartin Mares       char *name = pci_get_param(a, "net.cache_name");
32159b4709SMartin Mares       if (!name || !name[0])
3398ccf6d6SMartin Mares 	return NULL;
3498ccf6d6SMartin Mares 
35*f022f467SMartin Mares       if (strncmp(name, "~/", 2))
36*f022f467SMartin Mares 	a->id_cache_name = pci_strdup(a, name);
37*f022f467SMartin Mares       else
38*f022f467SMartin Mares 	{
3998ccf6d6SMartin Mares 	  uid_t uid = getuid();
4098ccf6d6SMartin Mares 	  struct passwd *pw = getpwuid(uid);
4198ccf6d6SMartin Mares 	  if (!pw)
4298ccf6d6SMartin Mares 	    return name;
4398ccf6d6SMartin Mares 
44*f022f467SMartin Mares 	  a->id_cache_name = pci_malloc(a, strlen(pw->pw_dir) + strlen(name+1) + 1);
45*f022f467SMartin Mares 	  sprintf(a->id_cache_name, "%s%s", pw->pw_dir, name+1);
46*f022f467SMartin Mares 	}
47*f022f467SMartin Mares     }
48*f022f467SMartin Mares 
49*f022f467SMartin Mares   return a->id_cache_name;
50*f022f467SMartin Mares }
51*f022f467SMartin Mares 
create_parent_dirs(struct pci_access * a,char * name)52*f022f467SMartin Mares static void create_parent_dirs(struct pci_access *a, char *name)
53*f022f467SMartin Mares {
54*f022f467SMartin Mares   // Assumes that we have a private copy of the name we can modify
55*f022f467SMartin Mares 
56*f022f467SMartin Mares   char *p = name + strlen(name);
57*f022f467SMartin Mares   while (p > name && *p != '/')
58*f022f467SMartin Mares     p--;
59*f022f467SMartin Mares   if (p == name)
60*f022f467SMartin Mares     return;
61*f022f467SMartin Mares 
62*f022f467SMartin Mares   while (p > name)
63*f022f467SMartin Mares     {
64*f022f467SMartin Mares       // We stand at a slash. Check if the current prefix exists.
65*f022f467SMartin Mares       *p = 0;
66*f022f467SMartin Mares       struct stat st;
67*f022f467SMartin Mares       int res = stat(name, &st);
68*f022f467SMartin Mares       *p = '/';
69*f022f467SMartin Mares       if (res >= 0)
70*f022f467SMartin Mares 	break;
71*f022f467SMartin Mares 
72*f022f467SMartin Mares       // Does not exist yet, move up one directory
73*f022f467SMartin Mares       p--;
74*f022f467SMartin Mares       while (p > name && *p != '/')
75*f022f467SMartin Mares 	p--;
76*f022f467SMartin Mares     }
77*f022f467SMartin Mares 
78*f022f467SMartin Mares   // We now stand at the end of the longest existing prefix.
79*f022f467SMartin Mares   // Create all directories to the right of it.
80*f022f467SMartin Mares   for (;;)
81*f022f467SMartin Mares     {
82*f022f467SMartin Mares       p++;
83*f022f467SMartin Mares       while (*p && *p != '/')
84*f022f467SMartin Mares 	p++;
85*f022f467SMartin Mares       if (!*p)
86*f022f467SMartin Mares 	break;
87*f022f467SMartin Mares 
88*f022f467SMartin Mares       *p = 0;
89*f022f467SMartin Mares       int res = mkdir(name, 0777);
90*f022f467SMartin Mares       if (res < 0)
91*f022f467SMartin Mares 	{
92*f022f467SMartin Mares 	  a->warning("Cannot create directory %s: %s", name, strerror(errno));
93*f022f467SMartin Mares 	  *p = '/';
94*f022f467SMartin Mares 	  break;
95*f022f467SMartin Mares 	}
96*f022f467SMartin Mares       *p = '/';
97*f022f467SMartin Mares     }
9898ccf6d6SMartin Mares }
9998ccf6d6SMartin Mares 
100752d4d9aSMartin Mares int
pci_id_cache_load(struct pci_access * a,int flags)101752d4d9aSMartin Mares pci_id_cache_load(struct pci_access *a, int flags)
102752d4d9aSMartin Mares {
103752d4d9aSMartin Mares   char *name;
104752d4d9aSMartin Mares   char line[MAX_LINE];
105752d4d9aSMartin Mares   FILE *f;
106752d4d9aSMartin Mares   int lino;
107752d4d9aSMartin Mares 
108*f022f467SMartin Mares   if (a->id_cache_status > 0)
109*f022f467SMartin Mares     return 0;
110752d4d9aSMartin Mares   a->id_cache_status = 1;
111*f022f467SMartin Mares 
11298ccf6d6SMartin Mares   name = get_cache_name(a);
11398ccf6d6SMartin Mares   if (!name)
114752d4d9aSMartin Mares     return 0;
11598ccf6d6SMartin Mares   a->debug("Using cache %s\n", name);
116*f022f467SMartin Mares 
117752d4d9aSMartin Mares   if (flags & PCI_LOOKUP_REFRESH_CACHE)
118752d4d9aSMartin Mares     {
119752d4d9aSMartin Mares       a->debug("Not loading cache, will refresh everything\n");
120752d4d9aSMartin Mares       a->id_cache_status = 2;
121752d4d9aSMartin Mares       return 0;
122752d4d9aSMartin Mares     }
123752d4d9aSMartin Mares 
12498ccf6d6SMartin Mares   f = fopen(name, "rb");
125752d4d9aSMartin Mares   if (!f)
126752d4d9aSMartin Mares     {
127752d4d9aSMartin Mares       a->debug("Cache file does not exist\n");
128752d4d9aSMartin Mares       return 0;
129752d4d9aSMartin Mares     }
130752d4d9aSMartin Mares   /* FIXME: Compare timestamp with the pci.ids file? */
131752d4d9aSMartin Mares 
132752d4d9aSMartin Mares   lino = 0;
133752d4d9aSMartin Mares   while (fgets(line, sizeof(line), f))
134752d4d9aSMartin Mares     {
135752d4d9aSMartin Mares       char *p = strchr(line, '\n');
136752d4d9aSMartin Mares       lino++;
137752d4d9aSMartin Mares       if (p)
138752d4d9aSMartin Mares         {
139752d4d9aSMartin Mares 	  *p = 0;
140752d4d9aSMartin Mares 	  if (lino == 1)
141752d4d9aSMartin Mares 	    {
142752d4d9aSMartin Mares 	      if (strcmp(line, cache_version))
143752d4d9aSMartin Mares 	        {
144752d4d9aSMartin Mares 		  a->debug("Unrecognized cache version %s, ignoring\n", line);
145752d4d9aSMartin Mares 		  break;
146752d4d9aSMartin Mares 		}
147752d4d9aSMartin Mares 	      continue;
148752d4d9aSMartin Mares 	    }
149752d4d9aSMartin Mares 	  else
150752d4d9aSMartin Mares 	    {
151752d4d9aSMartin Mares 	      int cat, id1, id2, id3, id4, cnt;
152752d4d9aSMartin Mares 	      if (sscanf(line, "%d%x%x%x%x%n", &cat, &id1, &id2, &id3, &id4, &cnt) >= 5)
153752d4d9aSMartin Mares 	        {
154752d4d9aSMartin Mares 		  p = line + cnt;
155752d4d9aSMartin Mares 		  while (*p && *p == ' ')
156752d4d9aSMartin Mares 		    p++;
157752d4d9aSMartin Mares 		  pci_id_insert(a, cat, id1, id2, id3, id4, p, SRC_CACHE);
158752d4d9aSMartin Mares 		  continue;
159752d4d9aSMartin Mares 		}
160752d4d9aSMartin Mares 	    }
161752d4d9aSMartin Mares 	}
16298ccf6d6SMartin Mares       a->warning("Malformed cache file %s (line %d), ignoring", name, lino);
163752d4d9aSMartin Mares       break;
164752d4d9aSMartin Mares     }
165752d4d9aSMartin Mares 
166752d4d9aSMartin Mares   if (ferror(f))
16798ccf6d6SMartin Mares     a->warning("Error while reading %s", name);
168752d4d9aSMartin Mares   fclose(f);
169752d4d9aSMartin Mares   return 1;
170752d4d9aSMartin Mares }
171752d4d9aSMartin Mares 
172752d4d9aSMartin Mares void
pci_id_cache_flush(struct pci_access * a)173752d4d9aSMartin Mares pci_id_cache_flush(struct pci_access *a)
174752d4d9aSMartin Mares {
175752d4d9aSMartin Mares   int orig_status = a->id_cache_status;
176752d4d9aSMartin Mares   FILE *f;
177752d4d9aSMartin Mares   unsigned int h;
178752d4d9aSMartin Mares   struct id_entry *e, *e2;
17998ccf6d6SMartin Mares   char hostname[256], *tmpname, *name;
18061bc0b58SMartin Mares   int this_pid;
181752d4d9aSMartin Mares 
182752d4d9aSMartin Mares   a->id_cache_status = 0;
183752d4d9aSMartin Mares   if (orig_status < 2)
184752d4d9aSMartin Mares     return;
18598ccf6d6SMartin Mares   name = get_cache_name(a);
18698ccf6d6SMartin Mares   if (!name)
187752d4d9aSMartin Mares     return;
18861bc0b58SMartin Mares 
189*f022f467SMartin Mares   create_parent_dirs(a, name);
190*f022f467SMartin Mares 
19161bc0b58SMartin Mares   this_pid = getpid();
19261bc0b58SMartin Mares   if (gethostname(hostname, sizeof(hostname)) < 0)
19361bc0b58SMartin Mares     hostname[0] = 0;
19461bc0b58SMartin Mares   else
19561bc0b58SMartin Mares     hostname[sizeof(hostname)-1] = 0;
19698ccf6d6SMartin Mares   tmpname = pci_malloc(a, strlen(name) + strlen(hostname) + 64);
19798ccf6d6SMartin Mares   sprintf(tmpname, "%s.tmp-%s-%d", name, hostname, this_pid);
19861bc0b58SMartin Mares 
19961bc0b58SMartin Mares   f = fopen(tmpname, "wb");
200752d4d9aSMartin Mares   if (!f)
201752d4d9aSMartin Mares     {
20298ccf6d6SMartin Mares       a->warning("Cannot write to %s: %s", name, strerror(errno));
20361bc0b58SMartin Mares       pci_mfree(tmpname);
204752d4d9aSMartin Mares       return;
205752d4d9aSMartin Mares     }
20698ccf6d6SMartin Mares   a->debug("Writing cache to %s\n", name);
207752d4d9aSMartin Mares   fprintf(f, "%s\n", cache_version);
208752d4d9aSMartin Mares 
209752d4d9aSMartin Mares   for (h=0; h<HASH_SIZE; h++)
210752d4d9aSMartin Mares     for (e=a->id_hash[h]; e; e=e->next)
211752d4d9aSMartin Mares       if (e->src == SRC_CACHE || e->src == SRC_NET)
212752d4d9aSMartin Mares 	{
21361bc0b58SMartin Mares 	  /* Negative entries are not written */
21461bc0b58SMartin Mares 	  if (!e->name[0])
21561bc0b58SMartin Mares 	    continue;
21661bc0b58SMartin Mares 
217752d4d9aSMartin Mares 	  /* Verify that every entry is written at most once */
218752d4d9aSMartin Mares 	  for (e2=a->id_hash[h]; e2 != e; e2=e2->next)
219752d4d9aSMartin Mares 	    if ((e2->src == SRC_CACHE || e2->src == SRC_NET) &&
220752d4d9aSMartin Mares 	        e2->cat == e->cat &&
221752d4d9aSMartin Mares 		e2->id12 == e->id12 && e2->id34 == e->id34)
222752d4d9aSMartin Mares 	    break;
223752d4d9aSMartin Mares 	  if (e2 == e)
224752d4d9aSMartin Mares 	    fprintf(f, "%d %x %x %x %x %s\n",
225752d4d9aSMartin Mares 	            e->cat,
226752d4d9aSMartin Mares 		    pair_first(e->id12), pair_second(e->id12),
227752d4d9aSMartin Mares 		    pair_first(e->id34), pair_second(e->id34),
228752d4d9aSMartin Mares 		    e->name);
229752d4d9aSMartin Mares 	}
230752d4d9aSMartin Mares 
231752d4d9aSMartin Mares   fflush(f);
232752d4d9aSMartin Mares   if (ferror(f))
23398ccf6d6SMartin Mares     a->warning("Error writing %s", name);
234752d4d9aSMartin Mares   fclose(f);
23561bc0b58SMartin Mares 
23698ccf6d6SMartin Mares   if (rename(tmpname, name) < 0)
23761bc0b58SMartin Mares     {
23898ccf6d6SMartin Mares       a->warning("Cannot rename %s to %s: %s", tmpname, name, strerror(errno));
23961bc0b58SMartin Mares       unlink(tmpname);
24061bc0b58SMartin Mares     }
24161bc0b58SMartin Mares   pci_mfree(tmpname);
242752d4d9aSMartin Mares }
243752d4d9aSMartin Mares 
24494d1b5e0SMartin Mares #else
24594d1b5e0SMartin Mares 
pci_id_cache_load(struct pci_access * a UNUSED,int flags UNUSED)24694d1b5e0SMartin Mares int pci_id_cache_load(struct pci_access *a UNUSED, int flags UNUSED)
24794d1b5e0SMartin Mares {
24894d1b5e0SMartin Mares   a->id_cache_status = 1;
24994d1b5e0SMartin Mares   return 0;
25094d1b5e0SMartin Mares }
25194d1b5e0SMartin Mares 
pci_id_cache_flush(struct pci_access * a)25294d1b5e0SMartin Mares void pci_id_cache_flush(struct pci_access *a)
25394d1b5e0SMartin Mares {
25494d1b5e0SMartin Mares   a->id_cache_status = 0;
255*f022f467SMartin Mares   pci_mfree(a->id_cache_name);
256*f022f467SMartin Mares   a->id_cache_name = NULL;
25794d1b5e0SMartin Mares }
25894d1b5e0SMartin Mares 
25994d1b5e0SMartin Mares #endif
26094d1b5e0SMartin Mares 
26194d1b5e0SMartin Mares void
pci_id_cache_dirty(struct pci_access * a)26294d1b5e0SMartin Mares pci_id_cache_dirty(struct pci_access *a)
26394d1b5e0SMartin Mares {
26494d1b5e0SMartin Mares   if (a->id_cache_status >= 1)
26594d1b5e0SMartin Mares     a->id_cache_status = 2;
26694d1b5e0SMartin Mares }
267