Device.cpp 10.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/******************************************************************************
 *
 * This file is part of the Castor project.
 * See http://castor.web.cern.ch/castor
 *
 * Copyright (C) 2003  CERN
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * 
 *
 * @author Castor Dev team, castor-dev@cern.ch
 *****************************************************************************/
23
24
25
26

#include <cstdlib>
#include <scsi/sg.h>

27
#include "Device.hpp"
28
#include "common/exception/Errnum.hpp"
29

30
31
using namespace castor::tape;

32
33
34
35
36
37
/**
 * Fill up the array that the device list is with all the system's
 * SCSI devices information.
 * 
 * (all code using templates must be in the header file)
 */
38
SCSI::DeviceVector::DeviceVector(System::virtualWrapper& sysWrapper) : m_sysWrapper(sysWrapper) {
39
  std::string sysDevsPath = "/sys/bus/scsi/devices";
Eric Cano's avatar
Eric Cano committed
40
  cta::utils::Regex ifFirstCharIsDigit("^[[:digit:]]");
41
  std::vector<std::string> checkResult;
42
  DIR* dirp;
43
  cta::exception::Errnum::throwOnNull(
44
45
      dirp = m_sysWrapper.opendir(sysDevsPath.c_str()),
      "Error opening sysfs scsi devs");
46
47
48
  while (struct dirent * dent = m_sysWrapper.readdir(dirp)) {
    std::string dn(dent->d_name);
    if ("." == dn || ".." == dn) continue;
49
50
    checkResult = ifFirstCharIsDigit.exec(dn);
    if (0 == checkResult.size()) continue; /* we only use digital names like 6:0:3:0 */
51
52
53
    std::string fullpath = sysDevsPath + "/" + std::string(dent->d_name);
    /* We expect only symbolic links in this directory, */
    char rp[PATH_MAX];
54
    cta::exception::Errnum::throwOnNull(
55
56
        m_sysWrapper.realpath(fullpath.c_str(), rp),
        "Could not find realpath for " + fullpath);
57
58
59
60
61
    this->push_back(getDeviceInfo(rp));
  }
  sysWrapper.closedir(dirp);
}

62
63
SCSI::DeviceInfo & SCSI::DeviceVector::findBySymlink(std::string path) {
  struct stat sbuff;
64
  cta::exception::Errnum::throwOnMinusOne(
65
66
67
68
69
70
71
72
73
74
75
      m_sysWrapper.stat(path.c_str(), &sbuff),
      std::string("Could not stat path: ")+path);
  for(std::vector<DeviceInfo>::iterator i = begin(); i!= end(); i++) {
    if (i->nst == sbuff || i->st == sbuff) {
      return *i;
    }
  }
  throw SCSI::DeviceVector::NotFound(
      std::string("Could not find tape device pointed to by ") + path);
}

76
std::string SCSI::DeviceVector::readfile(std::string path) {
77
  int fd;
78
  cta::exception::Errnum::throwOnMinusOne(
79
80
      fd = m_sysWrapper.open(path.c_str(), 0),
      std::string("Could not open file ") + path);
81
82
83
  char buf[readfileBlockSize];
  std::string ret;
  while (ssize_t sread = m_sysWrapper.read(fd, buf, readfileBlockSize)) {
84
    cta::exception::Errnum::throwOnMinusOne(sread,
85
      std::string("Could not read from open file ") + path);
86
87
    ret.append(buf, sread);
  }
88
  cta::exception::Errnum::throwOnNonZero(
89
90
      m_sysWrapper.close(fd),
      std::string("Error closing file ") + path);
91
92
93
94
95
96
  return ret;
}

SCSI::DeviceInfo::DeviceFile SCSI::DeviceVector::readDeviceFile(std::string path) {
  DeviceInfo::DeviceFile ret;
  std::string file = readfile(path);
97
  if (!::sscanf(file.c_str(), "%u:%u\n", &ret.major, &ret.minor))
98
    throw cta::exception::Exception(std::string("Could not parse file: ") + path);
99
100
101
102
103
  return ret;
}

SCSI::DeviceInfo::DeviceFile SCSI::DeviceVector::statDeviceFile(std::string path) {
  struct stat sbuf;
104
  cta::exception::Errnum::throwOnNonZero(
105
106
     m_sysWrapper.stat(path.c_str(), &sbuf),
     std::string("Could not stat file ") + path);
107
  if (!S_ISCHR(sbuf.st_mode))
108
    throw cta::exception::Exception("Device file " + path + " is not a character device");
109
110
111
112
113
114
115
116
117
118
119
120
121
  DeviceInfo::DeviceFile ret;
  ret.major = major(sbuf.st_rdev);
  ret.minor = minor(sbuf.st_rdev);
  return ret;
}

/**
 * Part factored out of getDeviceInfo: get the tape specifics
 * from sysfs.
 * @param devinfo
 */
void SCSI::DeviceVector::getTapeInfo(DeviceInfo & devinfo) {
  /* Find the st and nst devices for this SCSI device */
122
123
124
125
126
127
128
129
130
131
132
133
134
135
  DIR * dirp; 
  /* SLC6 default tapeDir and scsiPrefix */
  std::string tapeDir="/scsi_tape"; /* The name of the tape dir inside of
                                     * devinfo.sysfs_entry. /scsi_tape/ on SLC6.
                                     */
  std::string scsiPrefix="^";        /* The prefix for the name for the tape 
                                      * device name for the regexp.
                                      * scsi_tape: on SLC5.
                                      */ 
  /* we try to open /scsi_tape first for SLC6 */
  dirp = m_sysWrapper.opendir((devinfo.sysfs_entry+tapeDir).c_str());
  
  if (!dirp) {
    /* here we open sysfs_entry for SLC5 */
136
    cta::exception::Errnum::throwOnNull(
137
138
139
140
141
142
        dirp = m_sysWrapper.opendir(devinfo.sysfs_entry.c_str()),
        std::string("Error opening tape device directory ") +
            devinfo.sysfs_entry + tapeDir+" or "+devinfo.sysfs_entry);
    /* we are not on SLC6 */
    scsiPrefix="^scsi_tape:"; 
    tapeDir ="";
143
144
  } 
  
Eric Cano's avatar
Eric Cano committed
145
146
  cta::utils::Regex st_re((scsiPrefix+"(st[[:digit:]]+)$").c_str());
  cta::utils::Regex nst_re((scsiPrefix+"(nst[[:digit:]]+)$").c_str());
147
  
148
149
150
  while (struct dirent * dent = m_sysWrapper.readdir(dirp)) {
    std::vector<std::string> res;
    /* Check if it's the st information */
151
    res = st_re.exec(dent->d_name);
152
153
    if (res.size()) {
      if (!devinfo.st_dev.size()) {
154
        devinfo.st_dev = std::string("/dev/") + res[1];     
155
      } else
156
        throw cta::exception::Exception("Matched st device several times!");
157
      /* Read the major and major number */
158
      devinfo.st = readDeviceFile(devinfo.sysfs_entry + tapeDir+ "/"
159
160
161
162
163
164
165
166
167
168
          + std::string(dent->d_name) + "/dev");
      /* Check the actual device file */
      DeviceInfo::DeviceFile realFile = statDeviceFile(devinfo.st_dev);
      if (devinfo.st != realFile) {
        std::stringstream err;
        err << "Mismatch between sysfs info and actual device file: "
            << devinfo.sysfs_entry + "/" + dent->d_name << " indicates "
            << devinfo.st.major << ":" << devinfo.st.minor
            << " while " << devinfo.st_dev << " is: "
            << realFile.major << ":" << realFile.minor;
169
        throw cta::exception::Exception(err.str());
170
171
172
      }
    }
    /* Check if it's the nst information */
173
    res = nst_re.exec(dent->d_name);
174
175
176
177
    if (res.size()) {
      if (!devinfo.nst_dev.size()) {
        devinfo.nst_dev = std::string("/dev/") + res[1];
      } else
178
        throw cta::exception::Exception("Matched nst device several times!");
179
      /* Read the major and major number */
180
      devinfo.nst = readDeviceFile(devinfo.sysfs_entry + tapeDir + "/"
181
182
183
184
185
186
187
188
189
190
          + std::string(dent->d_name) + "/dev");
      /* Check the actual device file */
      DeviceInfo::DeviceFile realFile = statDeviceFile(devinfo.nst_dev);
      if (devinfo.nst != realFile) {
        std::stringstream err;
        err << "Mismatch between sysfs info and actual device file: "
            << devinfo.sysfs_entry + "/" + dent->d_name << " indicates "
            << devinfo.nst.major << ":" << devinfo.nst.minor
            << " while " << devinfo.st_dev << " is: "
            << realFile.major << ":" << realFile.minor;
191
        throw cta::exception::Exception(err.str());
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
      }
    }
  }
  m_sysWrapper.closedir(dirp);
}

/**
 * Extract information from sysfs about a SCSI device.
 * @param path Path to the directory with information about 
 * @return 
 */
SCSI::DeviceInfo SCSI::DeviceVector::getDeviceInfo(const char * path) {
  DeviceInfo ret;
  ret.sysfs_entry = path;
  std::string buf;
  /* Get device type */
  {
    buf = readfile(ret.sysfs_entry + "/type");
    if (!sscanf(buf.c_str(), "%d", &ret.type))
211
      throw cta::exception::Exception(std::string("Could not parse file: ") + ret.sysfs_entry + "/type");
212
  }
Eric Cano's avatar
Eric Cano committed
213
214
215
  /* Get vendor (trimmed of trailing newline, not of spaces) */
  {
    buf = readfile(ret.sysfs_entry + "/vendor");
216
    size_t endpos  = buf.find_first_of("\n ");
Eric Cano's avatar
Eric Cano committed
217
218
219
220
221
    ret.vendor = buf.substr(0, endpos);
  }
  /* Get model (trimmed of trailing newline, not of spaces) */
  {
    buf = readfile(ret.sysfs_entry + "/model");
222
    size_t endpos  = buf.find_first_of("\n ");
Eric Cano's avatar
Eric Cano committed
223
224
225
226
227
    ret.product = buf.substr(0, endpos);
  }
  /* Get revision (trimmed of trailing newline, not of spaces) */
  {
    buf = readfile(ret.sysfs_entry + "/rev");
228
    size_t endpos  = buf.find_first_of("\n ");
Eric Cano's avatar
Eric Cano committed
229
230
    ret.productRevisionLevel = buf.substr(0, endpos);
  }
231
232
233
234
  /* Get name of sg device */
  {
    char rl[PATH_MAX];
    std::string lp = ret.sysfs_entry + "/generic";
235
    ssize_t rlSize;
236
    cta::exception::Errnum::throwOnMinusOne(
237
238
        rlSize = m_sysWrapper.readlink(lp.c_str(), rl, sizeof (rl) - 1),
        std::string("Could not read link ") + lp);
239
    rl[rlSize] = '\0';
240
241
242
    std::string gl(rl);
    size_t pos = gl.find_last_of("/");
    if (pos == std::string::npos)
243
      throw cta::exception::Exception(std::string("Could not find last / in link: ") + gl +
244
245
246
247
248
249
250
251
252
253
254
255
256
257
        " read from " + ret.sysfs_entry + "/generic");
    ret.sg_dev = std::string("/dev/") + gl.substr(pos + 1);
  }
  /* Get the major and minor number of the device file */
  ret.sg = readDeviceFile(ret.sysfs_entry + "/generic/dev");
  /* Check that we have an agreement with the actual device file */
  DeviceInfo::DeviceFile realFile = statDeviceFile(ret.sg_dev);
  if (ret.sg != realFile) {
    std::stringstream err;
    err << "Mismatch between sysfs info and actual device file: "
        << ret.sysfs_entry + "/generic/dev" << " indicates "
        << ret.sg.major << ":" << ret.sg.minor
        << " while " << ret.sg_dev << " is: "
        << realFile.major << ":" << realFile.minor;
258
    throw cta::exception::Exception(err.str());
259
260
261
262
263
264
  }
  /* Handle more if we have a tape device */
  if (Types::tape == ret.type)
    getTapeInfo(ret);
  return ret;
}