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