Fix array bounds problem in hddtemp code.
[monky] / src / hddtemp.c
1 /* Conky, a system monitor, based on torsmo
2  *
3  * Any original torsmo code is licensed under the BSD license
4  *
5  * All code written since the fork of torsmo is licensed under the GPL
6  *
7  * Please see COPYING for details
8  *
9  * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
10  * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
11  *      (see AUTHORS)
12  * All rights reserved.
13  *
14  * This program is free software: you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation, either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * You should have received a copy of the GNU General Public License
24  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
25  *
26  */
27
28 #include "conky.h"
29 #include "logging.h"
30 #include <errno.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <netdb.h>
34 #include <sys/select.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37
38 #define BUFLEN 512
39 #define PORT 7634
40
41 char buf[BUFLEN];
42
43 int scan_hddtemp(const char *arg, char **dev, char **addr, int *port)
44 {
45         char buf1[32], buf2[64];
46         int n, ret;
47
48         if (!arg)
49                 return 1;
50
51         if ((ret = sscanf(arg, "%31s %63s %d", buf1, buf2, &n)) < 1)
52                 return 1;
53
54         if (strncmp(buf1, "/dev/", 5)) {
55                 strncpy(buf1 + 5, buf1, 32 - 5);
56                 strncpy(buf1, "/dev/", 5);
57         }
58         *dev = strndup(buf1, text_buffer_size);
59
60         if (ret >= 2) {
61                 *addr = strndup(buf2, text_buffer_size);
62         } else {
63                 *addr = strndup("127.0.0.1", text_buffer_size);
64         }
65
66         if (ret == 3) {
67                 *port = n;
68         } else {
69                 *port = PORT;
70         }
71
72         return 0;
73 }
74
75 /* this is an iterator:
76  * set line to NULL in consecutive calls to get the next field
77  * returns "<dev><unit><val>" or NULL on error
78  */
79 static char *read_hdd_val(const char *line)
80 {
81         static char line_s[512] = "\0";
82         static char *p = 0;
83         char *dev, *val, unit;
84         char *ret = NULL;
85
86         if (line) {
87                 snprintf(line_s, 512, "%s", line);
88                 p = line_s;
89         }
90         if (!(*line_s))
91                 return ret;
92         /* read the device */
93         dev = ++p;
94         if (!p) return ret;
95         if (!(p = strchr(p, line_s[0])))
96                 return ret;
97         *(p++) = '\0';
98         /* jump over the devname */
99         if (!(p = strchr(p, line_s[0])))
100                 return ret;
101         /* read the value */
102         val = ++p;
103         if (!(p = strchr(p, line_s[0])))
104                 return ret;
105         *(p++) = '\0';
106         unit = *(p++);
107         /* preset p for next call */
108         p = strchr(p + 1, line_s[0]);
109
110         if (dev && *dev && val && *val) {
111                 asprintf(&ret, "%s%c%s", dev, unit, val);
112         }
113         return ret;
114 }
115
116 /* returns <unit><val> or NULL on error or N/A */
117 char *get_hddtemp_info(char *dev, char *hostaddr, int port)
118 {
119         int sockfd = 0;
120         struct hostent he, *he_res = 0;
121         int he_errno;
122         char hostbuff[2048];
123         struct sockaddr_in addr;
124         struct timeval tv;
125         fd_set rfds;
126         int len, i;
127         char *p, *r = NULL;
128
129         if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
130                 perror("socket");
131                 goto GET_OUT;
132         }
133
134 #ifdef HAVE_GETHOSTBYNAME_R
135         if (gethostbyname_r(hostaddr, &he, hostbuff,
136                             sizeof(hostbuff), &he_res, &he_errno)) {
137                 ERR("hddtemp gethostbyname_r: %s", hstrerror(h_errno));
138 #else /* HAVE_GETHOSTBYNAME_R */
139         if (!(he_res = gethostbyname(hostaddr))) {
140                 perror("gethostbyname()");
141 #endif /* HAVE_GETHOSTBYNAME_R */
142                 goto GET_OUT;
143         }
144
145         addr.sin_family = AF_INET;
146         addr.sin_port = htons(port);
147         addr.sin_addr = *((struct in_addr *) he_res->h_addr);
148         memset(&(addr.sin_zero), 0, 8);
149
150         if (connect(sockfd, (struct sockaddr *) &addr,
151                                 sizeof(struct sockaddr)) == -1) {
152                 perror("connect");
153                 goto GET_OUT;
154         }
155
156         FD_ZERO(&rfds);
157         FD_SET(sockfd, &rfds);
158
159         /* We're going to wait up to a half second to see whether there's any
160          * data available. Polling with timeout set to 0 doesn't seem to work
161          * with hddtemp.
162          */
163         tv.tv_sec = 0;
164         tv.tv_usec = 500000;
165
166         i = select(sockfd + 1, &rfds, NULL, NULL, &tv);
167         if (i == -1) { /* select() failed */
168                 if (errno == EINTR) {
169                         /* silently ignore interrupted system call */
170                         goto GET_OUT;
171                 } else {
172                         perror("select");
173                 }
174         } else if (i == 0) { /* select() timeouted */
175                 ERR("hddtemp had nothing for us");
176                 goto GET_OUT;
177         }
178
179         p = buf;
180         len = 0;
181         do {
182                 i = recv(sockfd, p, BUFLEN - (p - buf), 0);
183                 if (i < 0) {
184                         perror("recv");
185                         break;
186                 }
187                 len += i;
188                 p += i;
189         } while (i > 0 && p < buf + BUFLEN - 1);
190
191         if (len < 2) {
192                 ERR("hddtemp returned nada");
193                 goto GET_OUT;
194         }
195
196         buf[len] = 0;
197
198         if ((p = read_hdd_val(buf)) == NULL)
199                 goto GET_OUT;
200         do {
201                 if (p >= buf + BUFLEN) break;
202                 if (!strncmp(dev, p, strlen(dev)))
203                         asprintf(&r, "%s", p + strlen(dev));
204                 free(p);
205         } while(!r && (p = read_hdd_val(NULL)) != NULL);
206
207 GET_OUT:
208         close(sockfd);
209         return r;
210 }