Fix for segfault in top_name stuff.
[monky] / src / hddtemp.c
1 /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
2  * vim: ts=4 sw=4 noet ai cindent syntax=c
3  *
4  * Conky, a system monitor, based on torsmo
5  *
6  * Any original torsmo code is licensed under the BSD license
7  *
8  * All code written since the fork of torsmo is licensed under the GPL
9  *
10  * Please see COPYING for details
11  *
12  * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
13  * Copyright (c) 2005-2010 Brenden Matthews, Philip Kovacs, et. al.
14  *      (see AUTHORS)
15  * All rights reserved.
16  *
17  * This program is free software: you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation, either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  * You should have received a copy of the GNU General Public License
27  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
28  *
29  */
30
31 #include "conky.h"
32 #include "logging.h"
33 #include <errno.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <netdb.h>
37 #include <sys/select.h>
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40
41 #define BUFLEN 512
42 #define DEFAULT_PORT "7634"
43 #define DEFAULT_HOST "127.0.0.1"
44
45 static char *hddtemp_host = NULL;
46 static char *hddtemp_port = NULL;
47
48 static struct hdd_info {
49         struct hdd_info *next;
50         char *dev;
51         short temp;
52         char unit;
53 } hdd_info_head = {
54         .next = NULL,
55 };
56
57 void set_hddtemp_host(const char *host)
58 {
59         if (hddtemp_host)
60                 free(hddtemp_host);
61         hddtemp_host = strdup(host);
62 }
63
64 void set_hddtemp_port(const char *port)
65 {
66         if (hddtemp_port)
67                 free(hddtemp_port);
68         hddtemp_port = strdup(port);
69 }
70
71 static void __free_hddtemp_info(struct hdd_info *hdi)
72 {
73         if (hdi->next)
74                 __free_hddtemp_info(hdi->next);
75         free(hdi->dev);
76         free(hdi);
77 }
78
79 static void free_hddtemp_info(void)
80 {
81         DBGP("free_hddtemp_info() called");
82         if (!hdd_info_head.next)
83                 return;
84         __free_hddtemp_info(hdd_info_head.next);
85         hdd_info_head.next = NULL;
86 }
87
88 static void add_hddtemp_info(char *dev, short temp, char unit)
89 {
90         struct hdd_info *hdi = &hdd_info_head;
91
92         DBGP("add_hddtemp_info(%s, %d, %c) being called", dev, temp, unit);
93         while (hdi->next)
94                 hdi = hdi->next;
95
96         hdi->next = malloc(sizeof(struct hdd_info));
97         memset(hdi->next, 0, sizeof(struct hdd_info));
98         hdi->next->dev = strdup(dev);
99         hdi->next->temp = temp;
100         hdi->next->unit = unit;
101 }
102
103 static char *fetch_hddtemp_output(void)
104 {
105         int sockfd;
106         const char *dst_host, *dst_port;
107         char *buf = NULL;
108         int buflen, offset = 0, rlen;
109         struct addrinfo hints, *result, *rp;
110         int i;
111
112         dst_host = hddtemp_host ? hddtemp_host : DEFAULT_HOST;
113         dst_port = hddtemp_port ? hddtemp_port : DEFAULT_PORT;
114
115         memset(&hints, 0, sizeof(hints));
116         hints.ai_family = AF_INET;      /* XXX: hddtemp has no ipv6 support (yet?) */
117         hints.ai_socktype = SOCK_STREAM;
118
119         if ((i = getaddrinfo(dst_host, dst_port, &hints, &result))) {
120                 NORM_ERR("getaddrinfo(): %s", gai_strerror(i));
121                 return NULL;
122         }
123
124         for (rp = result; rp; rp = rp->ai_next) {
125                 sockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
126                 if (sockfd == -1)
127                         continue;
128                 if (connect(sockfd, rp->ai_addr, rp->ai_addrlen) != -1)
129                         break;
130                 close(sockfd);
131         }
132         if (!rp) {
133                 NORM_ERR("could not connect to hddtemp host");
134                 goto GET_OUT;
135         }
136
137         buflen = 1024;
138         buf = malloc(buflen);
139         while ((rlen = recv(sockfd, buf + offset, buflen - offset - 1, 0)) > 0) {
140                 offset += rlen;
141                 if (buflen - offset < 1) {
142                         buflen += 1024;
143                         buf = realloc(buf, buflen);
144                 }
145         }
146         if (rlen < 0)
147                 perror("recv");
148
149         buf[offset] = '\0';
150
151         close(sockfd);
152 GET_OUT:
153         freeaddrinfo(result);
154         return buf;
155 }
156
157 /* this is an iterator:
158  * set line to NULL in consecutive calls to get the next field
159  * note that exhausing iteration is assumed - otherwise *saveptr
160  * is not being freed!
161  */
162 static int read_hdd_val(const char *line, char **dev, short *val, char *unit,
163                 char **saveptr)
164 {
165         char *line_s, *cval, *endptr;
166         static char *p = 0;
167
168         if (line) {
169                 *saveptr = strdup(line);
170                 p = *saveptr;
171         }
172         line_s = *saveptr;
173
174 again:
175         if(!*p)
176                 goto out_fail;
177         /* read the device */
178         *dev = ++p;
179         if (!(p = strchr(p, line_s[0])))
180                 goto out_fail;
181         *(p++) = '\0';
182         /* jump over the devname */
183         if (!(p = strchr(p, line_s[0])))
184                 goto out_fail;
185         /* read the value */
186         cval = ++p;
187         if (!(p = strchr(p, line_s[0])))
188                 goto out_fail;
189         *(p++) = '\0';
190         *unit = *(p++);
191         *val = strtol(cval, &endptr, 10);
192         if (*endptr) {
193                 if (!(p = strchr(p, line_s[0])))
194                         goto out_fail;
195
196                 p++;
197                 goto again;
198         }
199         
200         /* preset p for next call */
201         p++;
202
203         return 0;
204 out_fail:
205         free(*saveptr);
206         return 1;
207 }
208
209 int update_hddtemp(void) {
210         char *data, *dev, unit, *saveptr;
211         short val;
212         static double last_hddtemp_update = 0.0;
213
214         /* limit tcp connection overhead */
215         if (current_update_time - last_hddtemp_update < 5)
216                 return 0;
217         last_hddtemp_update = current_update_time;
218
219         free_hddtemp_info();
220
221         if (!(data = fetch_hddtemp_output()))
222                 return 0;
223
224         if (read_hdd_val(data, &dev, &val, &unit, &saveptr)) {
225                 free(data);
226                 return 0;
227         }
228         do {
229                 add_hddtemp_info(dev, val, unit);
230         } while (!read_hdd_val(NULL, &dev, &val, &unit, &saveptr));
231         free(data);
232         return 0;
233 }
234
235 void free_hddtemp(void)
236 {
237         free_hddtemp_info();
238         if (hddtemp_host) {
239                 free(hddtemp_host);
240                 hddtemp_host = NULL;
241         }
242         if (hddtemp_port) {
243                 free(hddtemp_port);
244                 hddtemp_port = NULL;
245         }
246 }
247
248 int get_hddtemp_info(const char *dev, short *val, char *unit)
249 {
250         struct hdd_info *hdi = hdd_info_head.next;
251
252         /* if no dev is given, just use hdd_info_head->next */
253         while(dev && hdi) {
254                 if (!strcmp(dev, hdi->dev))
255                         break;
256                 hdi = hdi->next;
257         }
258         if (!hdi)
259                 return 1;
260         
261         *val = hdi->temp;
262         *unit = hdi->unit;
263         return 0;
264 }