]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * dev_legacy.cpp | |
3 | * | |
4 | * Home page of code is: http://www.smartmontools.org | |
5 | * | |
6 | * Copyright (C) 2008-18 Christian Franke | |
7 | * | |
8 | * SPDX-License-Identifier: GPL-2.0-or-later | |
9 | */ | |
10 | ||
11 | #include "config.h" | |
12 | ||
13 | #include "utility.h" | |
14 | #include "atacmds.h" | |
15 | #include "scsicmds.h" | |
16 | #include "dev_interface.h" | |
17 | #include "dev_ata_cmd_set.h" | |
18 | ||
19 | #include <errno.h> | |
20 | ||
21 | const char * dev_legacy_cpp_cvsid = "$Id: dev_legacy.cpp 4760 2018-08-19 18:45:53Z chrfranke $" | |
22 | DEV_INTERFACE_H_CVSID; | |
23 | ||
24 | ///////////////////////////////////////////////////////////////////////////// | |
25 | ||
26 | // Legacy interface declarations (now commented out globally): | |
27 | ||
28 | // from utility.h: | |
29 | int guess_device_type(const char * dev_name); | |
30 | int make_device_names (char ***devlist, const char* name); | |
31 | int deviceopen(const char *pathname, char *type); | |
32 | int deviceclose(int fd); | |
33 | ||
34 | // from atacmds.h: | |
35 | int ata_command_interface(int device, smart_command_set command, int select, char *data); | |
36 | ||
37 | // from scsicmds.h: | |
38 | int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report); | |
39 | ||
40 | // from smartctl.h: | |
41 | void print_smartctl_examples(); | |
42 | ||
43 | ///////////////////////////////////////////////////////////////////////////// | |
44 | ||
45 | namespace os { // No need to publish anything, name provided for Doxygen | |
46 | ||
47 | ///////////////////////////////////////////////////////////////////////////// | |
48 | /// Implement shared open/close routines with old functions. | |
49 | ||
50 | class legacy_smart_device | |
51 | : virtual public /*implements*/ smart_device | |
52 | { | |
53 | public: | |
54 | explicit legacy_smart_device(const char * mode) | |
55 | : smart_device(never_called), | |
56 | m_fd(-1), m_mode(mode) { } | |
57 | ||
58 | virtual ~legacy_smart_device() throw(); | |
59 | ||
60 | virtual bool is_open() const; | |
61 | ||
62 | virtual bool open(); | |
63 | ||
64 | virtual bool close(); | |
65 | ||
66 | protected: | |
67 | /// Return filedesc for derived classes. | |
68 | int get_fd() const | |
69 | { return m_fd; } | |
70 | ||
71 | private: | |
72 | int m_fd; ///< filedesc, -1 if not open. | |
73 | const char * m_mode; ///< Mode string for deviceopen(). | |
74 | }; | |
75 | ||
76 | ||
77 | legacy_smart_device::~legacy_smart_device() throw() | |
78 | { | |
79 | if (m_fd >= 0) | |
80 | ::deviceclose(m_fd); | |
81 | } | |
82 | ||
83 | bool legacy_smart_device::is_open() const | |
84 | { | |
85 | return (m_fd >= 0); | |
86 | } | |
87 | ||
88 | bool legacy_smart_device::open() | |
89 | { | |
90 | m_fd = ::deviceopen(get_dev_name(), const_cast<char*>(m_mode)); | |
91 | if (m_fd < 0) { | |
92 | set_err((errno==ENOENT || errno==ENOTDIR) ? ENODEV : errno); | |
93 | return false; | |
94 | } | |
95 | return true; | |
96 | } | |
97 | ||
98 | bool legacy_smart_device::close() | |
99 | { | |
100 | int fd = m_fd; m_fd = -1; | |
101 | if (::deviceclose(fd) < 0) { | |
102 | set_err(errno); | |
103 | return false; | |
104 | } | |
105 | return true; | |
106 | } | |
107 | ||
108 | ///////////////////////////////////////////////////////////////////////////// | |
109 | /// Implement standard ATA support with old functions | |
110 | ||
111 | class legacy_ata_device | |
112 | : public /*implements*/ ata_device_with_command_set, | |
113 | public /*extends*/ legacy_smart_device | |
114 | { | |
115 | public: | |
116 | legacy_ata_device(smart_interface * intf, const char * dev_name, const char * req_type); | |
117 | ||
118 | protected: | |
119 | virtual int ata_command_interface(smart_command_set command, int select, char * data); | |
120 | }; | |
121 | ||
122 | legacy_ata_device::legacy_ata_device(smart_interface * intf, const char * dev_name, const char * req_type) | |
123 | : smart_device(intf, dev_name, "ata", req_type), | |
124 | legacy_smart_device("ATA") | |
125 | { | |
126 | } | |
127 | ||
128 | int legacy_ata_device::ata_command_interface(smart_command_set command, int select, char * data) | |
129 | { | |
130 | return ::ata_command_interface(get_fd(), command, select, data); | |
131 | } | |
132 | ||
133 | ||
134 | ///////////////////////////////////////////////////////////////////////////// | |
135 | /// Implement standard SCSI support with old functions | |
136 | ||
137 | class legacy_scsi_device | |
138 | : public /*implements*/ scsi_device, | |
139 | public /*extends*/ legacy_smart_device | |
140 | { | |
141 | public: | |
142 | legacy_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type); | |
143 | ||
144 | virtual smart_device * autodetect_open(); | |
145 | ||
146 | virtual bool scsi_pass_through(scsi_cmnd_io * iop); | |
147 | }; | |
148 | ||
149 | legacy_scsi_device::legacy_scsi_device(smart_interface * intf, | |
150 | const char * dev_name, const char * req_type) | |
151 | : smart_device(intf, dev_name, "scsi", req_type), | |
152 | legacy_smart_device("SCSI") | |
153 | { | |
154 | } | |
155 | ||
156 | bool legacy_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) | |
157 | { | |
158 | int status = ::do_scsi_cmnd_io(get_fd(), iop, scsi_debugmode); | |
159 | if (status < 0) { | |
160 | set_err(-status); | |
161 | return false; | |
162 | } | |
163 | return true; | |
164 | } | |
165 | ||
166 | ||
167 | ///////////////////////////////////////////////////////////////////////////// | |
168 | /// SCSI open with autodetection support | |
169 | ||
170 | smart_device * legacy_scsi_device::autodetect_open() | |
171 | { | |
172 | // Open device | |
173 | if (!open()) | |
174 | return this; | |
175 | ||
176 | // No Autodetection if device type was specified by user | |
177 | if (*get_req_type()) | |
178 | return this; | |
179 | ||
180 | // The code below is based on smartd.cpp:SCSIFilterKnown() | |
181 | ||
182 | // Get INQUIRY | |
183 | unsigned char req_buff[64] = {0, }; | |
184 | int req_len = 36; | |
185 | if (scsiStdInquiry(this, req_buff, req_len)) { | |
186 | // Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices | |
187 | // watch this spot ... other devices could lock up here | |
188 | req_len = 64; | |
189 | if (scsiStdInquiry(this, req_buff, req_len)) { | |
190 | // device doesn't like INQUIRY commands | |
191 | close(); | |
192 | set_err(EIO, "INQUIRY failed"); | |
193 | return this; | |
194 | } | |
195 | } | |
196 | ||
197 | int avail_len = req_buff[4] + 5; | |
198 | int len = (avail_len < req_len ? avail_len : req_len); | |
199 | if (len < 36) | |
200 | return this; | |
201 | ||
202 | // Use INQUIRY to detect type | |
203 | ||
204 | // SAT or USB ? | |
205 | { | |
206 | smart_device * newdev = smi()->autodetect_sat_device(this, req_buff, len); | |
207 | if (newdev) | |
208 | // NOTE: 'this' is now owned by '*newdev' | |
209 | return newdev; | |
210 | } | |
211 | ||
212 | // Nothing special found | |
213 | return this; | |
214 | } | |
215 | ||
216 | ||
217 | ///////////////////////////////////////////////////////////////////////////// | |
218 | /// Implement platform interface with old functions. | |
219 | ||
220 | class legacy_smart_interface | |
221 | : public /*implements*/ smart_interface | |
222 | { | |
223 | public: | |
224 | virtual std::string get_app_examples(const char * appname); | |
225 | ||
226 | virtual bool scan_smart_devices(smart_device_list & devlist, const char * type, | |
227 | const char * pattern = 0); | |
228 | ||
229 | protected: | |
230 | virtual ata_device * get_ata_device(const char * name, const char * type); | |
231 | ||
232 | virtual scsi_device * get_scsi_device(const char * name, const char * type); | |
233 | ||
234 | virtual smart_device * autodetect_smart_device(const char * name); | |
235 | }; | |
236 | ||
237 | ||
238 | ////////////////////////////////////////////////////////////////////// | |
239 | ||
240 | std::string legacy_smart_interface::get_app_examples(const char * appname) | |
241 | { | |
242 | if (!strcmp(appname, "smartctl")) | |
243 | ::print_smartctl_examples(); // this prints to stdout ... | |
244 | return ""; // ... so don't print again. | |
245 | } | |
246 | ||
247 | ata_device * legacy_smart_interface::get_ata_device(const char * name, const char * type) | |
248 | { | |
249 | return new legacy_ata_device(this, name, type); | |
250 | } | |
251 | ||
252 | scsi_device * legacy_smart_interface::get_scsi_device(const char * name, const char * type) | |
253 | { | |
254 | return new legacy_scsi_device(this, name, type); | |
255 | } | |
256 | ||
257 | ||
258 | smart_device * legacy_smart_interface::autodetect_smart_device(const char * name) | |
259 | { | |
260 | switch (::guess_device_type(name)) { | |
261 | case CONTROLLER_ATA : return new legacy_ata_device(this, name, ""); | |
262 | case CONTROLLER_SCSI: return new legacy_scsi_device(this, name, ""); | |
263 | } | |
264 | // TODO: Test autodetect device here | |
265 | return 0; | |
266 | } | |
267 | ||
268 | ||
269 | static void free_devnames(char * * devnames, int numdevs) | |
270 | { | |
271 | if (!devnames) | |
272 | return; | |
273 | for (int i = 0; i < numdevs; i++) { | |
274 | if (devnames[i]) | |
275 | free(devnames[i]); | |
276 | } | |
277 | free(devnames); | |
278 | } | |
279 | ||
280 | bool legacy_smart_interface::scan_smart_devices(smart_device_list & devlist, | |
281 | const char * type, const char * pattern /*= 0*/) | |
282 | { | |
283 | if (pattern) { | |
284 | set_err(EINVAL, "DEVICESCAN with pattern not implemented yet"); | |
285 | return false; | |
286 | } | |
287 | ||
288 | // Make namelists | |
289 | char * * atanames = 0; int numata = 0; | |
290 | if (!type || !strcmp(type, "ata")) { | |
291 | numata = ::make_device_names(&atanames, "ATA"); | |
292 | if (numata < 0) { | |
293 | set_err(ENOMEM); | |
294 | return false; | |
295 | } | |
296 | } | |
297 | ||
298 | char * * scsinames = 0; int numscsi = 0; | |
299 | if (!type || !strcmp(type, "scsi")) { | |
300 | numscsi = ::make_device_names(&scsinames, "SCSI"); | |
301 | if (numscsi < 0) { | |
302 | free_devnames(atanames, numata); | |
303 | set_err(ENOMEM); | |
304 | return false; | |
305 | } | |
306 | } | |
307 | ||
308 | // Add to devlist | |
309 | int i; | |
310 | if (!type) | |
311 | type=""; | |
312 | for (i = 0; i < numata; i++) { | |
313 | ata_device * atadev = get_ata_device(atanames[i], type); | |
314 | if (atadev) | |
315 | devlist.push_back(atadev); | |
316 | } | |
317 | free_devnames(atanames, numata); | |
318 | ||
319 | for (i = 0; i < numscsi; i++) { | |
320 | scsi_device * scsidev = get_scsi_device(scsinames[i], type); | |
321 | if (scsidev) | |
322 | devlist.push_back(scsidev); | |
323 | } | |
324 | free_devnames(scsinames, numscsi); | |
325 | return true; | |
326 | } | |
327 | ||
328 | } // namespace | |
329 | ||
330 | ||
331 | ///////////////////////////////////////////////////////////////////////////// | |
332 | /// Initialize platform interface and register with smi() | |
333 | ||
334 | void smart_interface::init() | |
335 | { | |
336 | static os::legacy_smart_interface the_interface; | |
337 | smart_interface::set(&the_interface); | |
338 | } |