1 /*
2  * virscsi.c: helper APIs for managing host SCSI devices
3  *
4  * Copyright (C) 2013-2014 Red Hat, Inc.
5  * Copyright (C) 2013 Fujitsu, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library.  If not, see
19  * <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <config.h>
23 
24 #include <dirent.h>
25 #include <fcntl.h>
26 #include <inttypes.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 
31 #include "virlog.h"
32 #include "virscsi.h"
33 #include "virfile.h"
34 #include "virstring.h"
35 #include "virerror.h"
36 #include "viralloc.h"
37 
38 #define SYSFS_SCSI_DEVICES "/sys/bus/scsi/devices"
39 
40 #define VIR_FROM_THIS VIR_FROM_NONE
41 
42 VIR_LOG_INIT("util.scsi");
43 
44 struct _virUsedByInfo {
45     char *drvname; /* which driver */
46     char *domname; /* which domain */
47 };
48 typedef struct _virUsedByInfo virUsedByInfo;
49 
50 struct _virSCSIDevice {
51     unsigned int adapter;
52     unsigned int bus;
53     unsigned int target;
54     unsigned long long unit;
55 
56     char *name; /* adapter:bus:target:unit */
57     char *id;   /* model:vendor */
58     char *sg_path; /* e.g. /dev/sg2 */
59     virUsedByInfo **used_by; /* driver:domain(s) using this dev */
60     size_t n_used_by; /* how many domains are using this dev */
61 
62     bool readonly;
63     bool shareable;
64 };
65 
66 struct _virSCSIDeviceList {
67     virObjectLockable parent;
68     size_t count;
69     virSCSIDevice **devs;
70 };
71 
72 static virClass *virSCSIDeviceListClass;
73 
74 static void virSCSIDeviceListDispose(void *obj);
75 
76 static int
virSCSIOnceInit(void)77 virSCSIOnceInit(void)
78 {
79     if (!VIR_CLASS_NEW(virSCSIDeviceList, virClassForObjectLockable()))
80         return -1;
81 
82     return 0;
83 }
84 
85 VIR_ONCE_GLOBAL_INIT(virSCSI);
86 
87 static int
virSCSIDeviceGetAdapterId(const char * adapter,unsigned int * adapter_id)88 virSCSIDeviceGetAdapterId(const char *adapter,
89                           unsigned int *adapter_id)
90 {
91     if (STRPREFIX(adapter, "scsi_host") &&
92         virStrToLong_ui(adapter + strlen("scsi_host"),
93                         NULL, 0, adapter_id) == 0)
94         return 0;
95     virReportError(VIR_ERR_INTERNAL_ERROR,
96                    _("Cannot parse adapter '%s'"), adapter);
97     return -1;
98 }
99 
100 char *
virSCSIDeviceGetSgName(const char * sysfs_prefix,const char * adapter,unsigned int bus,unsigned int target,unsigned long long unit)101 virSCSIDeviceGetSgName(const char *sysfs_prefix,
102                        const char *adapter,
103                        unsigned int bus,
104                        unsigned int target,
105                        unsigned long long unit)
106 {
107     g_autoptr(DIR) dir = NULL;
108     struct dirent *entry;
109     g_autofree char *path = NULL;
110     unsigned int adapter_id;
111     const char *prefix = sysfs_prefix ? sysfs_prefix : SYSFS_SCSI_DEVICES;
112 
113     if (virSCSIDeviceGetAdapterId(adapter, &adapter_id) < 0)
114         return NULL;
115 
116     path = g_strdup_printf("%s/%d:%u:%u:%llu/scsi_generic", prefix, adapter_id,
117                            bus, target, unit);
118 
119     if (virDirOpen(&dir, path) < 0)
120         return NULL;
121 
122     /* Assume a single directory entry */
123     if (virDirRead(dir, &entry, path) > 0)
124         return  g_strdup(entry->d_name);
125 
126     return NULL;
127 }
128 
129 /* Returns device name (e.g. "sdc") on success, or NULL
130  * on failure.
131  */
132 char *
virSCSIDeviceGetDevName(const char * sysfs_prefix,const char * adapter,unsigned int bus,unsigned int target,unsigned long long unit)133 virSCSIDeviceGetDevName(const char *sysfs_prefix,
134                         const char *adapter,
135                         unsigned int bus,
136                         unsigned int target,
137                         unsigned long long unit)
138 {
139     g_autoptr(DIR) dir = NULL;
140     struct dirent *entry;
141     g_autofree char *path = NULL;
142     unsigned int adapter_id;
143     const char *prefix = sysfs_prefix ? sysfs_prefix : SYSFS_SCSI_DEVICES;
144 
145     if (virSCSIDeviceGetAdapterId(adapter, &adapter_id) < 0)
146         return NULL;
147 
148     path = g_strdup_printf("%s/%d:%u:%u:%llu/block", prefix, adapter_id, bus,
149                            target, unit);
150 
151     if (virDirOpen(&dir, path) < 0)
152         return NULL;
153 
154     if (virDirRead(dir, &entry, path) > 0)
155         return g_strdup(entry->d_name);
156 
157     return NULL;
158 }
159 
160 virSCSIDevice *
virSCSIDeviceNew(const char * sysfs_prefix,const char * adapter,unsigned int bus,unsigned int target,unsigned long long unit,bool readonly,bool shareable)161 virSCSIDeviceNew(const char *sysfs_prefix,
162                  const char *adapter,
163                  unsigned int bus,
164                  unsigned int target,
165                  unsigned long long unit,
166                  bool readonly,
167                  bool shareable)
168 {
169     g_autoptr(virSCSIDevice) dev = NULL;
170     g_autofree char *sg = NULL;
171     g_autofree char *vendor_path = NULL;
172     g_autofree char *model_path = NULL;
173     g_autofree char *vendor = NULL;
174     g_autofree char *model = NULL;
175     const char *prefix = sysfs_prefix ? sysfs_prefix : SYSFS_SCSI_DEVICES;
176 
177     dev = g_new0(virSCSIDevice, 1);
178 
179     dev->bus = bus;
180     dev->target = target;
181     dev->unit = unit;
182     dev->readonly = readonly;
183     dev->shareable = shareable;
184 
185     if (!(sg = virSCSIDeviceGetSgName(prefix, adapter, bus, target, unit)))
186         return NULL;
187 
188     if (virSCSIDeviceGetAdapterId(adapter, &dev->adapter) < 0)
189         return NULL;
190 
191     dev->name = g_strdup_printf("%d:%u:%u:%llu", dev->adapter,
192                                 dev->bus, dev->target, dev->unit);
193     dev->sg_path = g_strdup_printf("%s/%s",
194                                    sysfs_prefix ? sysfs_prefix : "/dev", sg);
195 
196     if (!virFileExists(dev->sg_path)) {
197         virReportSystemError(errno,
198                              _("SCSI device '%s': could not access %s"),
199                              dev->name, dev->sg_path);
200         return NULL;
201     }
202 
203     vendor_path = g_strdup_printf("%s/%s/vendor", prefix, dev->name);
204     model_path = g_strdup_printf("%s/%s/model", prefix, dev->name);
205 
206     if (virFileReadAll(vendor_path, 1024, &vendor) < 0)
207         return NULL;
208 
209     if (virFileReadAll(model_path, 1024, &model) < 0)
210         return NULL;
211 
212     virTrimSpaces(vendor, NULL);
213     virTrimSpaces(model, NULL);
214 
215     dev->id = g_strdup_printf("%s:%s", vendor, model);
216 
217     return g_steal_pointer(&dev);
218 }
219 
220 static void
virSCSIDeviceUsedByInfoFree(virUsedByInfo * used_by)221 virSCSIDeviceUsedByInfoFree(virUsedByInfo *used_by)
222 {
223     g_free(used_by->drvname);
224     g_free(used_by->domname);
225     g_free(used_by);
226 }
227 G_DEFINE_AUTOPTR_CLEANUP_FUNC(virUsedByInfo, virSCSIDeviceUsedByInfoFree);
228 
229 void
virSCSIDeviceFree(virSCSIDevice * dev)230 virSCSIDeviceFree(virSCSIDevice *dev)
231 {
232     size_t i;
233 
234     if (!dev)
235         return;
236 
237     g_free(dev->id);
238     g_free(dev->name);
239     g_free(dev->sg_path);
240     for (i = 0; i < dev->n_used_by; i++)
241         virSCSIDeviceUsedByInfoFree(dev->used_by[i]);
242     g_free(dev->used_by);
243     g_free(dev);
244 }
245 
246 int
virSCSIDeviceSetUsedBy(virSCSIDevice * dev,const char * drvname,const char * domname)247 virSCSIDeviceSetUsedBy(virSCSIDevice *dev,
248                        const char *drvname,
249                        const char *domname)
250 {
251     g_autoptr(virUsedByInfo) copy = NULL;
252 
253     copy = g_new0(virUsedByInfo, 1);
254     copy->drvname = g_strdup(drvname);
255     copy->domname = g_strdup(domname);
256 
257     VIR_APPEND_ELEMENT(dev->used_by, dev->n_used_by, copy);
258 
259     return 0;
260 }
261 
262 bool
virSCSIDeviceIsAvailable(virSCSIDevice * dev)263 virSCSIDeviceIsAvailable(virSCSIDevice *dev)
264 {
265     return dev->n_used_by == 0;
266 }
267 
268 const char *
virSCSIDeviceGetName(virSCSIDevice * dev)269 virSCSIDeviceGetName(virSCSIDevice *dev)
270 {
271     return dev->name;
272 }
273 
274 const char *
virSCSIDeviceGetPath(virSCSIDevice * dev)275 virSCSIDeviceGetPath(virSCSIDevice *dev)
276 {
277     return dev->sg_path;
278 }
279 
280 unsigned int
virSCSIDeviceGetAdapter(virSCSIDevice * dev)281 virSCSIDeviceGetAdapter(virSCSIDevice *dev)
282 {
283     return dev->adapter;
284 }
285 
286 unsigned int
virSCSIDeviceGetBus(virSCSIDevice * dev)287 virSCSIDeviceGetBus(virSCSIDevice *dev)
288 {
289     return dev->bus;
290 }
291 
292 unsigned int
virSCSIDeviceGetTarget(virSCSIDevice * dev)293 virSCSIDeviceGetTarget(virSCSIDevice *dev)
294 {
295     return dev->target;
296 }
297 
298 unsigned long long
virSCSIDeviceGetUnit(virSCSIDevice * dev)299 virSCSIDeviceGetUnit(virSCSIDevice *dev)
300 {
301     return dev->unit;
302 }
303 
304 bool
virSCSIDeviceGetReadonly(virSCSIDevice * dev)305 virSCSIDeviceGetReadonly(virSCSIDevice *dev)
306 {
307     return dev->readonly;
308 }
309 
310 bool
virSCSIDeviceGetShareable(virSCSIDevice * dev)311 virSCSIDeviceGetShareable(virSCSIDevice *dev)
312 {
313     return dev->shareable;
314 }
315 
316 int
virSCSIDeviceFileIterate(virSCSIDevice * dev,virSCSIDeviceFileActor actor,void * opaque)317 virSCSIDeviceFileIterate(virSCSIDevice *dev,
318                          virSCSIDeviceFileActor actor,
319                          void *opaque)
320 {
321     return (actor)(dev, dev->sg_path, opaque);
322 }
323 
324 virSCSIDeviceList *
virSCSIDeviceListNew(void)325 virSCSIDeviceListNew(void)
326 {
327     virSCSIDeviceList *list;
328 
329     if (virSCSIInitialize() < 0)
330         return NULL;
331 
332     if (!(list = virObjectLockableNew(virSCSIDeviceListClass)))
333         return NULL;
334 
335     return list;
336 }
337 
338 static void
virSCSIDeviceListDispose(void * obj)339 virSCSIDeviceListDispose(void *obj)
340 {
341     virSCSIDeviceList *list = obj;
342     size_t i;
343 
344     for (i = 0; i < list->count; i++)
345         virSCSIDeviceFree(list->devs[i]);
346 
347     g_free(list->devs);
348 }
349 
350 int
virSCSIDeviceListAdd(virSCSIDeviceList * list,virSCSIDevice * dev)351 virSCSIDeviceListAdd(virSCSIDeviceList *list,
352                      virSCSIDevice *dev)
353 {
354     if (virSCSIDeviceListFind(list, dev)) {
355         virReportError(VIR_ERR_INTERNAL_ERROR,
356                        _("Device %s already exists"),
357                        dev->name);
358         return -1;
359     }
360 
361     VIR_APPEND_ELEMENT(list->devs, list->count, dev);
362 
363     return 0;
364 }
365 
366 virSCSIDevice *
virSCSIDeviceListGet(virSCSIDeviceList * list,int idx)367 virSCSIDeviceListGet(virSCSIDeviceList *list, int idx)
368 {
369     if (idx >= list->count || idx < 0)
370         return NULL;
371 
372     return list->devs[idx];
373 }
374 
375 size_t
virSCSIDeviceListCount(virSCSIDeviceList * list)376 virSCSIDeviceListCount(virSCSIDeviceList *list)
377 {
378     return list->count;
379 }
380 
381 virSCSIDevice *
virSCSIDeviceListSteal(virSCSIDeviceList * list,virSCSIDevice * dev)382 virSCSIDeviceListSteal(virSCSIDeviceList *list,
383                        virSCSIDevice *dev)
384 {
385     virSCSIDevice *ret = NULL;
386     size_t i;
387 
388     for (i = 0; i < list->count; i++) {
389         if (list->devs[i]->adapter == dev->adapter &&
390             list->devs[i]->bus == dev->bus &&
391             list->devs[i]->target == dev->target &&
392             list->devs[i]->unit == dev->unit) {
393             ret = list->devs[i];
394             VIR_DELETE_ELEMENT(list->devs, i, list->count);
395             break;
396         }
397     }
398 
399     return ret;
400 }
401 
402 void
virSCSIDeviceListDel(virSCSIDeviceList * list,virSCSIDevice * dev,const char * drvname,const char * domname)403 virSCSIDeviceListDel(virSCSIDeviceList *list,
404                      virSCSIDevice *dev,
405                      const char *drvname,
406                      const char *domname)
407 {
408     size_t i;
409 
410     for (i = 0; i < dev->n_used_by; i++) {
411         if (STREQ_NULLABLE(dev->used_by[i]->drvname, drvname) &&
412             STREQ_NULLABLE(dev->used_by[i]->domname, domname)) {
413             if (dev->n_used_by > 1) {
414                 virSCSIDeviceUsedByInfoFree(dev->used_by[i]);
415                 VIR_DELETE_ELEMENT(dev->used_by, i, dev->n_used_by);
416             } else {
417                 virSCSIDeviceFree(virSCSIDeviceListSteal(list, dev));
418             }
419             break;
420         }
421     }
422 }
423 
424 virSCSIDevice *
virSCSIDeviceListFind(virSCSIDeviceList * list,virSCSIDevice * dev)425 virSCSIDeviceListFind(virSCSIDeviceList *list,
426                       virSCSIDevice *dev)
427 {
428     size_t i;
429 
430     for (i = 0; i < list->count; i++) {
431         if (list->devs[i]->adapter == dev->adapter &&
432             list->devs[i]->bus == dev->bus &&
433             list->devs[i]->target == dev->target &&
434             list->devs[i]->unit == dev->unit)
435             return list->devs[i];
436     }
437 
438     return NULL;
439 }
440