1 /*
2  * Copyright (c) 2008-2011 Willem Dijkstra
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  *    - Redistributions of source code must retain the above copyright
10  *      notice, this list of conditions and the following disclaimer.
11  *    - Redistributions in binary form must reproduce the above
12  *      copyright notice, this list of conditions and the following
13  *      disclaimer in the documentation and/or other materials provided
14  *      with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  *
29  */
30 
31 #include <sys/ioctl.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <strings.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <linux/hdreg.h>
41 
42 #include "conf.h"
43 #include "data.h"
44 #include "error.h"
45 #include "xmalloc.h"
46 #include "smart.h"
47 #include "diskname.h"
48 
49 #ifndef HAS_HDDRIVECMDHDR
50 typedef unsigned char task_ioreg_t;
51 
52 struct hd_drive_cmd_hdr {
53     task_ioreg_t command;
54     task_ioreg_t sector_number;
55     task_ioreg_t feature;
56     task_ioreg_t sector_count;
57 };
58 #endif
59 
60 /* Ata command register set for requesting smart values */
61 static struct hd_drive_cmd_hdr smart_cmd = {
62     WIN_SMART, /* command code */
63     0, /* sector number */
64     SMART_READ_VALUES, /* feature */
65     1 /* sector count */
66 };
67 
68 /* Per drive storage structure; the ata cmd is followed by the data buffer that
69  * is filled by the ioctl. There can be no room between the two; hence the
70  * pragma for byte alignment.
71  */
72 #pragma pack(1)
73 struct smart_device {
74     struct hd_drive_cmd_hdr cmd;
75     struct smart_values data;
76     char name[MAX_PATH_LEN];
77     int fd;
78     int type;
79     int failed;
80 };
81 #pragma pack()
82 
83 static struct smart_device *smart_devs = NULL;
84 static int smart_cur = 0;
85 
86 void
init_smart(struct stream * st)87 init_smart(struct stream *st)
88 {
89     struct disknamectx c;
90     int fd;
91     int i;
92     char drivename[MAX_PATH_LEN];
93 
94     if (sizeof(struct smart_values) != DISK_BLOCK_LEN) {
95         fatal("smart: internal error: smart values structure is broken");
96     }
97 
98     if (st->arg == NULL)
99         fatal("smart: need a <disk device|name> argument");
100 
101     initdisknamectx(&c, st->arg, drivename, sizeof(drivename));
102 
103     fd = -1;
104     while (nextdiskname(&c))
105         if ((fd = open(drivename, O_RDONLY | O_NONBLOCK)) != -1)
106             break;
107 
108 
109     if (fd < 0)
110         fatal("smart: cannot open '%.200s'", st->arg);
111 
112     /* look for drive in our global table */
113     for (i = 0; i < smart_cur; i++) {
114         if (strncmp(smart_devs[i].name, drivename, sizeof(drivename)) == 0) {
115             st->parg.smart = i;
116             return;
117         }
118     }
119 
120     /* this is a new drive; allocate the command and data block */
121     if (smart_cur > SYMON_MAX_DOBJECTS) {
122         fatal("%s:%d: dynamic object limit (%d) exceeded for smart data",
123               __FILE__, __LINE__, SYMON_MAX_DOBJECTS);
124     }
125 
126     smart_devs = xrealloc(smart_devs, (smart_cur + 1) * sizeof(struct smart_device));
127 
128     bzero(&smart_devs[smart_cur], sizeof(struct smart_device));
129 
130     /* store drivename in new block */
131     strlcpy(smart_devs[smart_cur].name, drivename, sizeof(smart_devs[0].name));
132 
133     /* store filedescriptor to device */
134     smart_devs[smart_cur].fd = fd;
135 
136     /* store smart dev entry in stream to facilitate quick get */
137     st->parg.smart = smart_cur;
138 
139     smart_cur++;
140 
141     info("started module smart(%.200s = %.200s)", st->arg, smart_devs[st->parg.smart].name);
142 }
143 
144 void
gets_smart()145 gets_smart()
146 {
147   int i;
148 
149     for (i = 0; i < smart_cur; i++) {
150         /* populate ata command header */
151         memcpy(&smart_devs[i].cmd, (void *) &smart_cmd, sizeof(struct hd_drive_cmd_hdr));
152         if (ioctl(smart_devs[i].fd, HDIO_DRIVE_CMD, &smart_devs[i].cmd)) {
153             warning("smart: ioctl for drive '%.200s' failed: %.200s",
154                     &smart_devs[i].name, strerror(errno));
155             smart_devs[i].failed = 1;
156         }
157 
158         /* Linux does not allow checking the smart return code using the
159          * HDIO_DRIVE_CMD */
160 
161         /* Some drives do not calculate the smart checksum correctly;
162          * additional code that identifies these drives would increase our
163          * footprint and the amount of datajuggling we need to do; we would
164          * rather ignore the checksums.
165          */
166         smart_devs[i].failed = 0;
167     }
168     return;
169 }
170 
171 int
get_smart(char * symon_buf,int maxlen,struct stream * st)172 get_smart(char *symon_buf, int maxlen, struct stream *st)
173 {
174     struct smart_report sr;
175 
176     if ((st->parg.smart < smart_cur) &&
177         (!smart_devs[st->parg.smart].failed))
178     {
179         smart_parse(&smart_devs[st->parg.smart].data, &sr);
180         return snpack(symon_buf, maxlen, st->arg, MT_SMART,
181                       sr.read_error_rate,
182                       sr.reallocated_sectors,
183                       sr.spin_retries,
184                       sr.air_flow_temp,
185                       sr.temperature,
186                       sr.reallocations,
187                       sr.current_pending,
188                       sr.uncorrectables,
189                       sr.soft_read_error_rate,
190                       sr.g_sense_error_rate,
191                       sr.temperature2,
192                       sr.free_fall_protection);
193     }
194 
195     return 0;
196 }
197