/ src / core / sysfs.cc
sysfs.cc
  1  /*
  2   * sysfs.cc
  3   *
  4   *
  5   */
  6  
  7  #include "version.h"
  8  #include "sysfs.h"
  9  #include "osutils.h"
 10  #include <limits.h>
 11  #include <unistd.h>
 12  #include <stdlib.h>
 13  #include <stdio.h>
 14  #include <string.h>
 15  #include <dirent.h>
 16  #include <sys/stat.h>
 17  #include <sys/types.h>
 18  #include <sys/mount.h>
 19  
 20  
 21  __ID("@(#) $Id$");
 22  
 23  using namespace sysfs;
 24  
 25  struct sysfs::entry_i
 26  {
 27    string devpath;
 28  };
 29  
 30  struct sysfs_t
 31  {
 32    sysfs_t():path("/sys"),
 33      temporary(false),
 34      has_sysfs(false)
 35    {
 36      has_sysfs = exists(path + "/class/.");
 37  
 38      if (!has_sysfs)                               // sysfs doesn't seem to be mounted
 39  // try to mount it in a temporary directory
 40      {
 41        char buffer[50];
 42        char *tmpdir = NULL;
 43  
 44        strncpy(buffer,
 45          "/var/tmp/sys-XXXXXX",
 46          sizeof(buffer));
 47        tmpdir = mkdtemp(buffer);
 48  
 49        if (tmpdir)
 50        {
 51          temporary = true;
 52          path = string(tmpdir);
 53          chmod(tmpdir,
 54            0000);                                  // to make clear it is a mount point
 55          mount("none",
 56            path.c_str(),
 57            "sysfs",
 58            0,
 59            NULL);
 60        }
 61  
 62        has_sysfs = exists(path + "/class/.");
 63      }
 64    }
 65  
 66    ~sysfs_t()
 67    {
 68      if (temporary)
 69      {
 70        umount(path.c_str());
 71        rmdir(path.c_str());
 72      }
 73    }
 74  
 75    string path;
 76    bool temporary;
 77    bool has_sysfs;
 78  };
 79  
 80  static sysfs_t fs;
 81  
 82  static string sysfs_getbustype(const string & path)
 83  {
 84    struct dirent **namelist;
 85    int i, n;
 86    string bustype = "";
 87  
 88  /*
 89    to determine to which kind of bus a device is connected:
 90    - for each subdirectory of /sys/bus,
 91    - look in ./devices/ for a link with the same basename as 'path'
 92    - check if this link and 'path' point to the same inode
 93    - if they do, the bus type is the name of the current directory
 94   */
 95    pushd(fs.path + "/bus");
 96    n = scandir(".", &namelist, selectdir, alphasort);
 97    popd();
 98  
 99    if (n <= 0)
100      return "";
101  
102    for (i = 0; i < n; i++)
103    {
104      string devname =
105        string(fs.path + "/bus/") + string(namelist[i]->d_name) +
106        "/devices/" + shortname(path);
107  
108      if (samefile(devname, path))
109      {
110        bustype = string(namelist[i]->d_name);
111        break;
112      }
113      free(namelist[i]);
114    }
115  
116    for (int j = i; j < n; j++)
117      free(namelist[j]);
118    free(namelist);
119  
120    return bustype;
121  }
122  
123  
124  static string sysfstopci(const string & path)
125  {
126    if (path.length() > strlen("XXXX:XX:XX.X"))
127      return "pci@" + path.substr(path.length() - strlen("XXXX:XX:XX.X"));
128    else
129      return "";
130  }
131  
132  
133  static string sysfstoide(const string & path)
134  {
135    if (path.substr(0, 3) == "ide")
136      return "ide@" + path.substr(path.length() - 3);
137    else
138      return "ide@" + path;
139  }
140  
141  
142  static string sysfstobusinfo(const string & path)
143  {
144    string bustype = sysfs_getbustype(path);
145  
146    if (bustype == "pci")
147      return sysfstopci(path);
148  
149    if (bustype == "ide")
150      return sysfstoide(path);
151  
152    if (bustype == "usb")
153    {
154      string name = shortname(path);
155      if (matches(name, "^[0-9]+-[0-9]+(\\.[0-9]+)*:[0-9]+\\.[0-9]+$"))
156      {
157        size_t colon = name.rfind(":");
158        size_t dash = name.find("-");
159        return "usb@" + name.substr(0, dash) + ":" + name.substr(dash+1, colon-dash-1);
160      }
161    }
162  
163    if (bustype == "virtio")
164    {
165      string name = shortname(path);
166      if (name.compare(0, 6, "virtio") == 0)
167        return "virtio@" + name.substr(6);
168      else
169        return "virtio@" + name;
170    }
171  
172    if (bustype == "vio")
173      return string("vio@") + shortname(path);
174  
175    if (bustype == "ccw")
176      return string("ccw@") + shortname(path);
177  
178    if (bustype == "ccwgroup")
179    {
180      // just report businfo for the first device in the group
181      // because the group doesn't really fit into lshw's tree model
182      string firstdev = realpath(path + "/cdev0");
183      return sysfstobusinfo(firstdev);
184    }
185  
186    return "";
187  }
188  
189  
190  string entry::businfo() const
191  {
192    if(!This) return "";
193    string result = sysfstobusinfo(This->devpath);
194    if (result.empty())
195      result = sysfstobusinfo(dirname(This->devpath));
196    return result;
197  }
198  
199  
200  static string finddevice(const string & name, const string & root = "")
201  {
202    struct dirent **namelist;
203    int n;
204    string result = "";
205  
206    if(exists(name))
207      return root + "/" + name;
208  
209    n = scandir(".", &namelist, selectdir, alphasort);
210  
211    for (int i = 0; i < n; i++)
212    {
213      pushd(namelist[i]->d_name);
214      string findinchild = finddevice(name, root + "/" + string(namelist[i]->d_name));
215      popd();
216  
217      free(namelist[i]);
218      if(findinchild != "")
219      {
220        result = findinchild;
221      }
222    }
223    free(namelist);
224  
225    return result;
226  }
227  
228  
229  string sysfs_finddevice(const string & name)
230  {
231    string devices = fs.path + string("/devices/");
232    string result = "";
233  
234    if(!pushd(devices))
235      return "";
236    result = finddevice(name);
237    popd();
238  
239    return result;
240  }
241  
242  entry entry::leaf() const
243  {
244    if (hassubdir("device"))
245      return entry(This->devpath+"/device");
246  
247    return entry(This->devpath);
248  }
249  
250  string entry::driver() const
251  {
252    string driverlink = This->devpath + "/driver";
253    if (!exists(driverlink))
254      return "";
255    return shortname(readlink(driverlink));
256  }
257  
258  
259  entry entry::byBus(string devbus, string devname)
260  {
261    entry e(fs.path + "/bus/" + devbus + "/devices/" + devname);
262    return e;
263  }
264  
265  
266  entry entry::byClass(string devclass, string devname)
267  {
268    entry e(fs.path + "/class/" + devclass + "/" + devname);
269    return e;
270  }
271  
272  
273  entry entry::byPath(string path)
274  {
275    entry e(fs.path + "/devices" + path);
276    return e;
277  }
278  
279  
280  entry::entry(const string & devpath)
281  {
282    This = new entry_i;
283    This->devpath = realpath(devpath);
284  }
285  
286  
287  entry & entry::operator =(const entry & e)
288  {
289  
290    *This = *(e.This);
291    return *this;
292  }
293  
294  
295  entry::entry(const entry & e)
296  {
297    This = new entry_i;
298  
299    *This = *(e.This);
300  }
301  
302  
303  entry::~entry()
304  {
305    delete This;
306  }
307  
308  bool entry::hassubdir(const string & s) const
309  {
310    return exists(This->devpath + "/" + s);
311  }
312  
313  
314  string entry::name_in_class(const string & classname) const
315  {
316    string result = "";
317  
318    string classdir = This->devpath + "/" + classname;
319    if (!pushd(classdir))
320      return result;
321  
322    struct dirent **namelist = NULL;
323    int count = scandir(".", &namelist, selectdir, alphasort);
324    popd();
325  
326    if (count < 0)
327      return result;
328  
329    // there should be at most one
330    for (int i = 0; i < count; i++)
331    {
332      result = namelist[i]->d_name;
333      free(namelist[i]);
334    }
335    free(namelist);
336  
337    return result;
338  }
339  
340  
341  string entry::name() const
342  {
343    return shortname(This->devpath);
344  }
345  
346  
347  entry entry::parent() const
348  {
349    entry e(dirname(This->devpath));
350    return e;
351  }
352  
353  string entry::classname() const
354  {
355    return shortname(dirname(This->devpath));
356  }
357  
358  string entry::subsystem() const
359  {
360    return shortname(realpath(This->devpath+"/subsystem"));
361  }
362  
363  bool entry::isvirtual() const
364  {
365    return shortname(dirname(dirname(This->devpath))) == "virtual";
366  }
367  
368  string entry::string_attr(const string & name, const string & def) const
369  {
370    return hw::strip(get_string(This->devpath + "/" + name, def));
371  }
372  
373  
374  unsigned long long entry::hex_attr(const string & name, unsigned long long def) const
375  {
376    string val = string_attr(name, "");
377    if (val.empty())
378      return def;
379    return strtoull(val.c_str(), NULL, 16);
380  }
381  
382  
383  vector < string > entry::multiline_attr(const string & name) const
384  {
385    vector < string > lines;
386    loadfile(This->devpath + "/" + name, lines);
387    return lines;
388  }
389  
390  
391  string entry::modalias() const
392  {
393    return get_string(This->devpath+"/modalias");
394  }
395  
396  string entry::device() const
397  {
398    return get_string(This->devpath+"/device");
399  }
400  
401  string entry::vendor() const
402  {
403    return get_string(This->devpath+"/vendor");
404  }
405  
406  vector < entry > entry::devices() const
407  {
408    vector < entry > result;
409  
410    if (!pushd(This->devpath))
411      return result;
412  
413    struct dirent **namelist;
414    int count = scandir(".", &namelist, selectdir, alphasort);
415    for (int i = 0; i < count; i ++)
416    {
417      entry e = sysfs::entry(This->devpath + "/" + string(namelist[i]->d_name));
418      if(e.hassubdir("subsystem"))
419  	    result.push_back(e);
420      free(namelist[i]);
421    }
422    if (namelist)
423      free(namelist);
424  
425    if(pushd("block"))
426    {
427      int count = scandir(".", &namelist, selectdir, alphasort);
428      for (int i = 0; i < count; i ++)
429      {
430        entry e = sysfs::entry(This->devpath + "/block/" + string(namelist[i]->d_name));
431        if(e.hassubdir("subsystem"))
432  	      result.push_back(e);
433        free(namelist[i]);
434      }
435      if (namelist)
436        free(namelist);
437      popd();
438    }
439    popd();
440    return result;
441  }
442  
443  vector < entry > sysfs::entries_by_bus(const string & busname)
444  {
445    vector < entry > result;
446  
447    if (!pushd(fs.path + "/bus/" + busname + "/devices"))
448      return result;
449  
450    struct dirent **namelist;
451    int count;
452    count = scandir(".", &namelist, selectlink, alphasort);
453    for (int i = 0; i < count; i ++)
454    {
455      entry e = sysfs::entry::byBus(busname, namelist[i]->d_name);
456      result.push_back(e);
457      free(namelist[i]);
458    }
459    popd();
460    if (namelist)
461      free(namelist);
462    return result;
463  }
464  
465  vector < entry > sysfs::entries_by_class(const string & classname)
466  {
467    vector < entry > result;
468  
469    if (!pushd(fs.path + "/class/" + classname))
470      return result;
471  
472    struct dirent **namelist;
473    int count;
474    count = scandir(".", &namelist, selectlink, alphasort);
475    for (int i = 0; i < count; i ++)
476    {
477      entry e = sysfs::entry::byClass(classname, namelist[i]->d_name);
478      result.push_back(e);
479      free(namelist[i]);
480    }
481    popd();
482    if (namelist)
483      free(namelist);
484    return result;
485  }
486  
487  bool scan_sysfs(hwNode & n)
488  {
489    return false;
490  }