smartmontools  SVN Rev 4814
Utility to control and monitor storage systems with "S.M.A.R.T."
os_os2.cpp
Go to the documentation of this file.
1 /*
2  * os_os2.c
3  *
4  * Home page of code is: http://www.smartmontools.org
5  *
6  * Copyright (C) 2004-8 Yuri Dario
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later
9  */
10 
11 /*
12  *
13  * Thanks to Daniela Engert for providing sample code for SMART ioctl access.
14  *
15  */
16 
17 // These are needed to define prototypes for the functions defined below
18 #include "config.h"
19 
20 #include <ctype.h>
21 #include <errno.h>
22 #include "atacmds.h"
23 #include "scsicmds.h"
24 #include "utility.h"
25 
26 // This is to include whatever prototypes you define in os_generic.h
27 #include "os_os2.h"
28 
29 // Needed by '-V' option (CVS versioning) of smartd/smartctl
30 const char *os_XXXX_c_cvsid="$Id: os_os2.cpp 4773 2018-09-12 20:14:44Z chrfranke $" \
32 
33 // global handle to device driver
34 static HFILE hDevice;
35 
36 // print examples for smartctl. You should modify this function so
37 // that the device paths are sensible for your OS, and to eliminate
38 // unsupported commands (eg, 3ware controllers).
40  printf("=================================================== SMARTCTL EXAMPLES =====\n\n"
41  " smartctl -a hd0 (Prints all SMART information)\n\n"
42  " smartctl --smart=on --offlineauto=on --saveauto=on hd0\n"
43  " (Enables SMART on first disk)\n\n"
44  " smartctl -t long hd0 (Executes extended disk self-test)\n\n"
45  " smartctl --attributes --log=selftest --quietmode=errorsonly hd0\n"
46  " (Prints Self-Test & Attribute errors)\n"
47  );
48  return;
49 }
50 
51 static const char * skipdev(const char * s)
52 {
53  return (!strncmp(s, "/dev/", 5) ? s + 5 : s);
54 }
55 
56 // tries to guess device type given the name (a path). See utility.h
57 // for return values.
58 int guess_device_type (const char* dev_name) {
59 
60  //printf( "dev_name %s\n", dev_name);
61  dev_name = skipdev(dev_name);
62  if (!strncmp(dev_name, "hd", 2) || !strncmp(dev_name, "ahci", 4))
63  return CONTROLLER_ATA;
64  return CONTROLLER_UNKNOWN;
65 }
66 
67 // makes a list of ATA or SCSI devices for the DEVICESCAN directive of
68 // smartd. Returns number N of devices, or -1 if out of
69 // memory. Allocates N+1 arrays: one of N pointers (devlist); the
70 // other N arrays each contain null-terminated character strings. In
71 // the case N==0, no arrays are allocated because the array of 0
72 // pointers has zero length, equivalent to calling malloc(0).
73 
74 int make_device_names (char*** devlist, const char* name) {
75 
76  int result;
77  int index;
78  const int max_dev = 32; // scan only first 32 devices
79 
80  // SCSI is not supported
81  if (strcmp (name, "ATA") != 0)
82  return 0;
83 
84  // try to open DANIS
85  APIRET rc;
86  ULONG ActionTaken;
87  HFILE danisDev, ahciDev;
88  bool is_danis = 0, is_ahci = 0;
89 
90  rc = DosOpen ((const char unsigned *)danisdev, &danisDev, &ActionTaken, 0, FILE_SYSTEM,
91  OPEN_ACTION_OPEN_IF_EXISTS, OPEN_SHARE_DENYNONE |
92  OPEN_FLAGS_NOINHERIT | OPEN_ACCESS_READONLY, NULL);
93  if (!rc)
94  is_danis = 1;
95 
96  rc = DosOpen ((const char unsigned *)ahcidev, &ahciDev, &ActionTaken, 0, FILE_SYSTEM,
97  OPEN_ACTION_OPEN_IF_EXISTS, OPEN_SHARE_DENYNONE |
98  OPEN_FLAGS_NOINHERIT | OPEN_ACCESS_READONLY, NULL);
99  if (!rc)
100  is_ahci = 1;
101 
102  // Count the devices.
103  result = 0;
104 
106  ULONG PLen = 1;
107  ULONG IDLen = 512;
108  struct ata_identify_device Id;
109 
110  for(int i = 0; i < max_dev; i++) {
111  if (is_ahci) {
112  Parms.byPhysicalUnit = i;
113  rc = DosDevIOCtl (ahciDev, DSKSP_CAT_GENERIC, DSKSP_GET_INQUIRY_DATA,
114  (PVOID)&Parms, PLen, &PLen, (PVOID)&Id, IDLen, &IDLen);
115  if (!rc) result++;
116  }
117  if (is_danis) {
118  Parms.byPhysicalUnit = i + 0x80;
119  rc = DosDevIOCtl (danisDev, DSKSP_CAT_GENERIC, DSKSP_GET_INQUIRY_DATA,
120  (PVOID)&Parms, PLen, &PLen, (PVOID)&Id, IDLen, &IDLen);
121  if (!rc) result++;
122  }
123  }
124  *devlist = (char**)calloc (result, sizeof (char *));
125  if (! *devlist)
126  goto error;
127  index = 0;
128 
129  // add devices
130  for(int i = 0; i < max_dev; i++) {
131  if (is_ahci) {
132  Parms.byPhysicalUnit = i;
133  rc = DosDevIOCtl (ahciDev, DSKSP_CAT_GENERIC, DSKSP_GET_INQUIRY_DATA,
134  (PVOID)&Parms, PLen, &PLen, (PVOID)&Id, IDLen, &IDLen);
135  if (!rc) {
136  asprintf(&(*devlist)[index], "ahci%d", i);
137  if (! (*devlist)[index])
138  goto error;
139  index++;
140  }
141  }
142  if (is_danis) {
143  Parms.byPhysicalUnit = i + 0x80;
144  rc = DosDevIOCtl (danisDev, DSKSP_CAT_GENERIC, DSKSP_GET_INQUIRY_DATA,
145  (PVOID)&Parms, PLen, &PLen, (PVOID)&Id, IDLen, &IDLen);
146  if (!rc) {
147  asprintf(&(*devlist)[index], "hd%d", i);
148  if (! (*devlist)[index])
149  goto error;
150  index++;
151  }
152  }
153  }
154 
155  if (is_danis)
156  DosClose( danisDev);
157 
158  if (is_ahci)
159  DosClose( ahciDev);
160 
161  return result;
162 
163  error:
164  if (*devlist)
165  {
166  for (index = 0; index < result; index++)
167  if ((*devlist)[index])
168  free ((*devlist)[index]);
169  free (*devlist);
170  }
171  if (is_danis)
172  DosClose( danisDev);
173 
174  if (is_ahci)
175  DosClose( ahciDev);
176 
177  return -1;
178 }
179 
180 // Like open(). Return non-negative integer handle, only used by the
181 // functions below. type=="ATA" or "SCSI". If you need to store
182 // extra information about your devices, create a private internal
183 // array within this file (see os_freebsd.cpp for an example). If you
184 // can not open the device (permission denied, does not exist, etc)
185 // set errno as open() does and return <0.
186 int deviceopen(const char *pathname, char * /* type */ ){
187 
188  int fd = 0;
189  APIRET rc;
190  ULONG ActionTaken;
191 
192  char * activedev = NULL;
193 
194  pathname = skipdev(pathname);
195  // DANIS506 driver
196  if(strlen(pathname) > strlen(danispref)
197  && strncmp(pathname, danispref, strlen(danispref)) == 0) {
198  fd = strtol(pathname + strlen(danispref), NULL, 10) + 0x80;
199  activedev = (char *)danisdev;
200  }
201  // OS2AHCI driver
202  if(strlen(pathname) > strlen(ahcipref)
203  && strncmp(pathname, ahcipref, strlen(ahcipref)) == 0) {
204  fd = strtol(pathname + strlen(ahcipref), NULL, 10);
205  activedev = (char *)ahcidev;
206  }
207 
208  if(!activedev) {
209  pout("Error: please specify hdX or ahciX device name\n");
210  return -1;
211  }
212  //printf( "deviceopen pathname %s\n", pathname);
213  rc = DosOpen ((const char unsigned *)activedev, &hDevice, &ActionTaken, 0, FILE_SYSTEM,
214  OPEN_ACTION_OPEN_IF_EXISTS, OPEN_SHARE_DENYNONE |
215  OPEN_FLAGS_NOINHERIT | OPEN_ACCESS_READONLY, NULL);
216  if (rc) {
217  char errmsg[256];
218  snprintf(errmsg,256,"Smartctl open driver %s failed (%lu)", activedev, rc);
219  errmsg[255]='\0';
220  syserror(errmsg);
221  return -1;
222  }
223 
224  return fd;
225 }
226 
227 // Like close(). Acts only on integer handles returned by
228 // deviceopen() above.
229 int deviceclose(int /* fd */){
230 
231  DosClose( hDevice);
232  hDevice = NULL;
233 
234  return 0;
235 }
236 
237 //
238 // OS/2 direct ioctl interface to IBMS506$/OS2AHCI$
239 //
240 static int dani_ioctl( int device, void* arg)
241 {
242  unsigned char* buff = (unsigned char*) arg;
243  APIRET rc;
245  ULONG PLen = 1;
246  ULONG DLen = 512; //sizeof (*buf);
247  ULONG value = 0;
248 
249  // printf( "device %d, request 0x%x, arg[0] 0x%x, arg[2] 0x%x\n", device, request, buff[0], buff[2]);
250 
251  Parms.byPhysicalUnit = device;
252  switch( buff[0]) {
253  case ATA_IDENTIFY_DEVICE:
255  (PVOID)&Parms, PLen, &PLen, (UCHAR *)arg+4, DLen, &DLen);
256  if (rc != 0)
257  {
258  printf ("DANIS506 ATA DSKSP_GET_INQUIRY_DATA failed (%lu)\n", rc);
259  return -1;
260  }
261  break;
262  case ATA_SMART_CMD:
263  switch( buff[2]) {
264  case ATA_SMART_STATUS:
265  DLen = sizeof(value);
266  // OS/2 already checks CL/CH in IBM1S506 code!! see s506rte.c (ddk)
267  // value: -1=not supported, 0=ok, 1=failing
268  rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_GETSTATUS,
269  (PVOID)&Parms, PLen, &PLen, (PVOID)&value, DLen, &DLen);
270  if (rc)
271  {
272  printf ("DANIS506 ATA GET SMART_STATUS failed (%lu)\n", rc);
273  return -1;
274  }
275  buff[4] = (unsigned char)value;
276  break;
279  (PVOID)&Parms, PLen, &PLen, (UCHAR *)arg+4, DLen, &DLen);
280  if (rc)
281  {
282  printf ("DANIS506 ATA GET DSKSP_SMART_GET_ATTRIBUTES failed (%lu)\n", rc);
283  return -1;
284  }
285  break;
288  (PVOID)&Parms, PLen, &PLen, (UCHAR *)arg+4, DLen, &DLen);
289  if (rc)
290  {
291  printf ("DANIS506 ATA GET DSKSP_SMART_GET_THRESHOLDS failed (%lu)\n", rc);
292  return -1;
293  }
294  break;
296  buff[4] = buff[1]; // copy select field
297  rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_GET_LOG,
298  (PVOID)&Parms, PLen, &PLen, (UCHAR *)arg+4, DLen, &DLen);
299  if (rc)
300  {
301  printf ("DANIS506 ATA GET DSKSP_SMART_GET_LOG failed (%lu)\n", rc);
302  return -1;
303  }
304  break;
305  case ATA_SMART_ENABLE:
306  buff[0] = 1; // enable
307  DLen = 1;
308  rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_ONOFF,
309  (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
310  if (rc) {
311  printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc);
312  return -1;
313  }
314  break;
315  case ATA_SMART_DISABLE:
316  buff[0] = 0; // disable
317  DLen = 1;
318  rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_ONOFF,
319  (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
320  if (rc) {
321  printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc);
322  return -1;
323  }
324  break;
325 #if 0
327  buff[0] = buff[3]; // select field
328  DLen = 1;
330  (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
331  if (rc) {
332  printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc);
333  return -1;
334  }
335  break;
336 #endif
337  case ATA_SMART_AUTOSAVE:
338  buff[0] = buff[3]; // select field
339  DLen = 1;
341  (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
342  if (rc) {
343  printf ("DANIS506 ATA DSKSP_SMART_AUTOSAVE_ONOFF failed (%lu)\n", rc);
344  return -1;
345  }
346  break;
348  buff[0] = buff[1]; // select field
349  DLen = 1;
351  (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
352  if (rc) {
353  printf ("DANIS506 ATA GET DSKSP_SMART_EXEC_OFFLINE failed (%lu)\n", rc);
354  return -1;
355  }
356  break;
357 
358  default:
359  fprintf( stderr, "device %d, arg[0] 0x%x, arg[2] 0x%x\n", device, buff[0], buff[2]);
360  fprintf( stderr, "unknown ioctl\n");
361  return -1;
362  break;
363  }
364  break;
365  //case WIN_PIDENTIFY:
366  // break;
367  default:
368  fprintf( stderr, "unknown ioctl\n");
369  return -1;
370  break;
371  }
372 
373  // ok
374  return 0;
375 }
376 
377 // Interface to ATA devices. See os_linux.cpp for the cannonical example.
378 // DETAILED DESCRIPTION OF ARGUMENTS
379 // device: is the integer handle provided by deviceopen()
380 // command: defines the different operations, see atacmds.h
381 // select: additional input data IF NEEDED (which log, which type of
382 // self-test).
383 // data: location to write output data, IF NEEDED (1 or 512 bytes).
384 // Note: not all commands use all arguments.
385 // RETURN VALUES (for all commands BUT command==STATUS_CHECK)
386 // -1 if the command failed
387 // 0 if the command succeeded,
388 // RETURN VALUES if command==STATUS_CHECK
389 // -1 if the command failed OR the disk SMART status can't be determined
390 // 0 if the command succeeded and disk SMART status is "OK"
391 // 1 if the command succeeded and disk SMART status is "FAILING"
392 
393 // huge value of buffer size needed because HDIO_DRIVE_CMD assumes
394 // that buff[3] is the data size. Since the ATA_SMART_AUTOSAVE and
395 // ATA_SMART_AUTO_OFFLINE use values of 0xf1 and 0xf8 we need the space.
396 // Otherwise a 4+512 byte buffer would be enough.
397 #define STRANGE_BUFFER_LENGTH (4+512*0xf8)
398 
399 int ata_command_interface(int device, smart_command_set command, int select, char *data){
400  unsigned char buff[STRANGE_BUFFER_LENGTH];
401  // positive: bytes to write to caller. negative: bytes to READ from
402  // caller. zero: non-data command
403  int copydata=0;
404 
405  const int HDIO_DRIVE_CMD_OFFSET = 4;
406 
407  // See struct hd_drive_cmd_hdr in hdreg.h. Before calling ioctl()
408  // buff[0]: ATA COMMAND CODE REGISTER
409  // buff[1]: ATA SECTOR NUMBER REGISTER == LBA LOW REGISTER
410  // buff[2]: ATA FEATURES REGISTER
411  // buff[3]: ATA SECTOR COUNT REGISTER
412 
413  // Note that on return:
414  // buff[2] contains the ATA SECTOR COUNT REGISTER
415 
416  // clear out buff. Large enough for HDIO_DRIVE_CMD (4+512 bytes)
417  memset(buff, 0, STRANGE_BUFFER_LENGTH);
418 
419  //printf( "command, select %d,%d\n", command, select);
420  buff[0]=ATA_SMART_CMD;
421  switch (command){
422  case CHECK_POWER_MODE:
423  buff[0]=ATA_CHECK_POWER_MODE;
424  copydata=1;
425  break;
426  case READ_VALUES:
427  buff[2]=ATA_SMART_READ_VALUES;
428  buff[3]=1;
429  copydata=512;
430  break;
431  case READ_THRESHOLDS:
433  buff[1]=buff[3]=1;
434  copydata=512;
435  break;
436  case READ_LOG:
438  buff[1]=select;
439  buff[3]=1;
440  copydata=512;
441  break;
442  case WRITE_LOG:
443  break;
444  case IDENTIFY:
445  buff[0]=ATA_IDENTIFY_DEVICE;
446  buff[3]=1;
447  copydata=512;
448  break;
449  case PIDENTIFY:
451  buff[3]=1;
452  copydata=512;
453  break;
454  case ENABLE:
455  buff[2]=ATA_SMART_ENABLE;
456  buff[1]=1;
457  break;
458  case DISABLE:
459  buff[2]=ATA_SMART_DISABLE;
460  buff[1]=1;
461  break;
462  case STATUS:
463  case STATUS_CHECK:
464  // this command only says if SMART is working. It could be
465  // replaced with STATUS_CHECK below.
466  buff[2]=ATA_SMART_STATUS;
467  buff[4]=0;
468  break;
469  case AUTO_OFFLINE:
470  buff[2]=ATA_SMART_AUTO_OFFLINE;
471  buff[3]=select; // YET NOTE - THIS IS A NON-DATA COMMAND!!
472  break;
473  case AUTOSAVE:
474  buff[2]=ATA_SMART_AUTOSAVE;
475  buff[3]=select; // YET NOTE - THIS IS A NON-DATA COMMAND!!
476  break;
477  case IMMEDIATE_OFFLINE:
479  buff[1]=select;
480  break;
481  //case STATUS_CHECK:
482  // // This command uses HDIO_DRIVE_TASK and has different syntax than
483  // // the other commands.
484  // buff[1]=ATA_SMART_STATUS;
485  // break;
486  default:
487  pout("Unrecognized command %d in linux_ata_command_interface()\n"
488  "Please contact " PACKAGE_BUGREPORT "\n", command);
489  errno=ENOSYS;
490  return -1;
491  }
492 
493  // We are now calling ioctl wrapper to the driver.
494  // TODO: use PASSTHRU in case of OS2AHCI driver
495  if ((dani_ioctl(device, buff)))
496  return -1;
497 
498  // There are two different types of ioctls(). The HDIO_DRIVE_TASK
499  // one is this:
500  if (command==STATUS_CHECK){
501  // Cyl low and Cyl high unchanged means "Good SMART status"
502  if (buff[4]==0)
503  return 0;
504 
505  // These values mean "Bad SMART status"
506  if (buff[4]==1)
507  return 1;
508 
509  // We haven't gotten output that makes sense; print out some debugging info
510  syserror("Error SMART Status command failed");
511  pout("Please get assistance from " PACKAGE_HOMEPAGE "\n");
512  return -1;
513  }
514 
515  // CHECK POWER MODE command returns information in the Sector Count
516  // register (buff[3]). Copy to return data buffer.
517  if (command==CHECK_POWER_MODE)
518  buff[HDIO_DRIVE_CMD_OFFSET]=buff[2];
519 
520  // if the command returns data then copy it back
521  if (copydata)
522  memcpy(data, buff+HDIO_DRIVE_CMD_OFFSET, copydata);
523 
524  return 0;
525 }
526 
527 // Interface to SCSI devices. N/A under OS/2
528 int do_scsi_cmnd_io(int /* fd */, struct scsi_cmnd_io * /* iop */, int /* report */) {
529  pout("SCSI interface is not implemented\n");
530  return -ENOSYS;
531 }
#define ATA_SMART_READ_LOG_SECTOR
Definition: atacmds.h:93
u16 s[6]
Definition: megaraid.h:107
#define ATACMDS_H_CVSID
Definition: atacmds.h:16
#define ATA_SMART_CMD
Definition: atacmds.h:63
static HFILE hDevice
Definition: os_os2.cpp:34
int deviceopen(const char *pathname, char *)
Definition: os_os2.cpp:186
#define ATA_SMART_DISABLE
Definition: atacmds.h:97
const char * os_XXXX_c_cvsid
Definition: os_os2.cpp:30
int do_scsi_cmnd_io(int, struct scsi_cmnd_io *, int)
Definition: os_os2.cpp:528
#define ATA_SMART_AUTOSAVE
Definition: atacmds.h:90
#define snprintf
Definition: utility.h:63
#define DSKSP_SMART_GET_ATTRIBUTES
Definition: os_os2.h:34
int ata_command_interface(int device, smart_command_set command, int select, char *data)
Definition: os_os2.cpp:399
#define ATA_IDENTIFY_DEVICE
Definition: atacmds.h:60
#define ATA_IDENTIFY_PACKET_DEVICE
Definition: atacmds.h:61
#define SCSICMDS_H_CVSID
Definition: scsicmds.h:22
ptr_t data
Definition: megaraid.h:104
#define DSKSP_SMART_GET_LOG
Definition: os_os2.h:36
#define ATA_CHECK_POWER_MODE
Definition: atacmds.h:59
#define DSKSP_SMART_ONOFF
Definition: os_os2.h:30
void syserror(const char *message)
Definition: utility.cpp:338
#define OS_XXXX_H_CVSID
Definition: os_os2.h:14
void pout(const char *fmt,...)
Definition: smartctl.cpp:1322
#define DSKSP_CAT_SMART
Definition: os_os2.h:29
int deviceclose(int)
Definition: os_os2.cpp:229
#define ATA_SMART_READ_VALUES
Definition: atacmds.h:88
#define DSKSP_SMART_GET_THRESHOLDS
Definition: os_os2.h:35
#define DSKSP_SMART_EXEC_OFFLINE
Definition: os_os2.h:38
Definition: atacmds.h:43
#define ATA_SMART_AUTO_OFFLINE
Definition: atacmds.h:101
#define DSKSP_SMART_AUTO_OFFLINE
Definition: os_os2.h:37
#define DSKSP_CAT_GENERIC
Definition: os_os2.h:43
smart_command_set
Definition: atacmds.h:36
void print_smartctl_examples()
Definition: os_os2.cpp:39
int guess_device_type(const char *dev_name)
Definition: os_os2.cpp:58
#define ATA_SMART_READ_THRESHOLDS
Definition: atacmds.h:89
const char * danisdev
Definition: os_os2.h:61
#define ATA_SMART_IMMEDIATE_OFFLINE
Definition: atacmds.h:92
#define DSKSP_SMART_GETSTATUS
Definition: os_os2.h:33
const char * ahcidev
Definition: os_os2.h:63
int make_device_names(char ***devlist, const char *name)
Definition: os_os2.cpp:74
#define STRANGE_BUFFER_LENGTH
Definition: os_os2.cpp:397
static int dani_ioctl(int device, void *arg)
Definition: os_os2.cpp:240
#define ATA_SMART_ENABLE
Definition: atacmds.h:96
#define DSKSP_GET_INQUIRY_DATA
Definition: os_os2.h:44
#define DSKSP_SMART_AUTOSAVE_ONOFF
Definition: os_os2.h:31
const char * ahcipref
Definition: os_os2.h:64
Definition: atacmds.h:38
static const char * skipdev(const char *s)
Definition: os_os2.cpp:51
const char * danispref
Definition: os_os2.h:62
#define UTILITY_H_CVSID
Definition: utility.h:16
#define ATA_SMART_STATUS
Definition: atacmds.h:98