]> git.sur5r.net Git - i3/i3status/blob - src/print_cpu_temperature.c
able to print percentage
[i3/i3status] / src / print_cpu_temperature.c
1 // vim:ts=4:sw=4:expandtab
2 #include <stdlib.h>
3 #include <limits.h>
4 #include <glob.h>
5 #include <stdio.h>
6 #include <string.h>
7 #include <yajl/yajl_gen.h>
8 #include <yajl/yajl_version.h>
9
10 #include "i3status.h"
11
12 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
13 #include <err.h>
14 #include <sys/types.h>
15 #include <sys/sysctl.h>
16 #define TZ_ZEROC 2731
17 #define TZ_KELVTOC(x) (((x)-TZ_ZEROC) / 10), abs(((x)-TZ_ZEROC) % 10)
18 #define TZ_AVG(x) ((x)-TZ_ZEROC) / 10
19 #endif
20
21 #if defined(__DragonFly__)
22 #include <sys/sysctl.h>
23 #include <sys/types.h>
24 #include <sys/sensors.h>
25 #define MUKTOC(v) ((v - 273150000) / 1000000.0)
26 #endif
27
28 #if defined(__OpenBSD__)
29 #include <sys/param.h>
30 #include <sys/types.h>
31 #include <sys/sysctl.h>
32 #include <sys/sensors.h>
33 #include <errno.h>
34 #include <err.h>
35
36 #define MUKTOC(v) ((v - 273150000) / 1000000.0)
37 #endif
38
39 #if defined(__NetBSD__)
40 #include <fcntl.h>
41 #include <prop/proplib.h>
42 #include <sys/envsys.h>
43
44 #define MUKTOC(v) ((v - 273150000) / 1000000.0)
45 #endif
46
47 typedef struct temperature_s {
48     double raw_value;
49     char formatted_value[20];
50 } temperature_t;
51
52 #define ERROR_CODE 1
53
54 static int read_temperature(char *thermal_zone, temperature_t *temperature) {
55 #if defined(LINUX)
56     static char buf[16];
57     long int temp;
58
59     if (!slurp(thermal_zone, buf, sizeof(buf)))
60         return ERROR_CODE;
61
62     temp = strtol(buf, NULL, 10);
63     temperature->raw_value = temp / 1000;
64
65     if (temp == LONG_MIN || temp == LONG_MAX || temp <= 0)
66         strcpy(temperature->formatted_value, "?");
67     else
68         sprintf(temperature->formatted_value, "%ld", (temp / 1000));
69
70 #elif defined(__DragonFly__)
71     struct sensor th_sensor;
72     size_t th_sensorlen;
73
74     th_sensorlen = sizeof(th_sensor);
75
76     if (sysctlbyname(thermal_zone, &th_sensor, &th_sensorlen, NULL, 0) == -1) {
77         perror("sysctlbyname");
78         return ERROR_CODE;
79     }
80
81     temperature->raw_value = MUKTOC(th_sensor.value);
82     sprintf(temperature->formatted_value, "%.2f", MUKTOC(th_sensor.value));
83
84 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
85     int sysctl_rslt;
86     size_t sysctl_size = sizeof(sysctl_rslt);
87
88     if (sysctlbyname(thermal_zone, &sysctl_rslt, &sysctl_size, NULL, 0))
89         return ERROR_CODE;
90
91     temperature->raw_value = TZ_AVG(sysctl_rslt);
92     sprintf(temperature->formatted_value, "%d.%d", TZ_KELVTOC(sysctl_rslt));
93
94 #elif defined(__OpenBSD__)
95     struct sensordev sensordev;
96     struct sensor sensor;
97     size_t sdlen, slen;
98     int dev, numt, mib[5] = {CTL_HW, HW_SENSORS, 0, 0, 0};
99
100     sdlen = sizeof(sensordev);
101     slen = sizeof(sensor);
102
103     for (dev = 0;; dev++) {
104         mib[2] = dev;
105         if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
106             if (errno == ENXIO)
107                 continue;
108             if (errno == ENOENT)
109                 break;
110             return ERROR_CODE;
111         }
112         /* 'path' is the node within the full path (defaults to acpitz0). */
113         if (BEGINS_WITH(sensordev.xname, thermal_zone)) {
114             mib[3] = SENSOR_TEMP;
115             /* Limit to temp0, but should retrieve from a full path... */
116             for (numt = 0; numt < 1 /*sensordev.maxnumt[SENSOR_TEMP]*/; numt++) {
117                 mib[4] = numt;
118                 if (sysctl(mib, 5, &sensor, &slen, NULL, 0) == -1) {
119                     if (errno != ENOENT) {
120                         warn("sysctl");
121                         continue;
122                     }
123                 }
124                 temperature->raw_value = MUKTOC(sensor.value);
125                 sprintf(temperature->formatted_value, "%.2f", MUKTOC(sensor.value));
126             }
127         }
128     }
129 #elif defined(__NetBSD__)
130     int fd, rval;
131     bool err = false;
132     prop_dictionary_t dict;
133     prop_array_t array;
134     prop_object_iterator_t iter;
135     prop_object_iterator_t iter2;
136     prop_object_t obj, obj2, obj3;
137
138     fd = open("/dev/sysmon", O_RDONLY);
139     if (fd == -1)
140         return ERROR_CODE;
141
142     rval = prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &dict);
143     if (rval == -1) {
144         err = true;
145         goto error_netbsd1;
146     }
147
148     /* No drivers registered? */
149     if (prop_dictionary_count(dict) == 0) {
150         err = true;
151         goto error_netbsd2;
152     }
153
154     iter = prop_dictionary_iterator(dict);
155     if (iter == NULL) {
156         err = true;
157         goto error_netbsd2;
158     }
159
160     /* iterate over the dictionary returned by the kernel */
161     while ((obj = prop_object_iterator_next(iter)) != NULL) {
162         /* skip this dict if it's not what we're looking for */
163         if ((strlen(prop_dictionary_keysym_cstring_nocopy(obj)) != strlen(thermal_zone)) ||
164             (strncmp(thermal_zone,
165                      prop_dictionary_keysym_cstring_nocopy(obj),
166                      strlen(thermal_zone)) != 0))
167             continue;
168
169         array = prop_dictionary_get_keysym(dict, obj);
170         if (prop_object_type(array) != PROP_TYPE_ARRAY) {
171             err = true;
172             goto error_netbsd3;
173         }
174
175         iter2 = prop_array_iterator(array);
176         if (!iter2) {
177             err = true;
178             goto error_netbsd3;
179         }
180
181         /* iterate over array of dicts specific to target sensor */
182         while ((obj2 = prop_object_iterator_next(iter2)) != NULL) {
183             obj3 = prop_dictionary_get(obj2, "cur-value");
184
185             float temp = MUKTOC(prop_number_integer_value(obj3));
186             temperature->raw_value = temp;
187             sprintf(temperature->formatted_value, "%.2f", temp);
188
189             break;
190         }
191         prop_object_iterator_release(iter2);
192     }
193 error_netbsd3:
194     prop_object_iterator_release(iter);
195 error_netbsd2:
196     prop_object_release(dict);
197 error_netbsd1:
198     close(fd);
199     if (err)
200         return ERROR_CODE;
201
202 #endif
203     return 0;
204 }
205
206 /*
207  * Reads the CPU temperature from /sys/class/thermal/thermal_zone%d/temp (or
208  * the user provided path) and returns the temperature in degree celsius.
209  *
210  */
211 void print_cpu_temperature_info(yajl_gen json_gen, char *buffer, int zone, const char *path, const char *format, const char *format_above_threshold, int max_threshold) {
212     char *outwalk = buffer;
213 #ifdef THERMAL_ZONE
214     const char *selected_format = format;
215     const char *walk;
216     bool colorful_output = false;
217     char *thermal_zone;
218     temperature_t temperature;
219     temperature.raw_value = 0;
220     sprintf(temperature.formatted_value, "%.2f", 0.0);
221
222     if (path == NULL)
223         asprintf(&thermal_zone, THERMAL_ZONE, zone);
224     else {
225         static glob_t globbuf;
226         if (glob(path, GLOB_NOCHECK | GLOB_TILDE, NULL, &globbuf) != 0)
227             die("glob() failed\n");
228         if (globbuf.gl_pathc == 0) {
229             /* No glob matches, the specified path does not contain a wildcard. */
230             asprintf(&thermal_zone, path, zone);
231         } else {
232             /* glob matched, we take the first match and ignore the others */
233             asprintf(&thermal_zone, "%s", globbuf.gl_pathv[0]);
234         }
235         globfree(&globbuf);
236     }
237
238     INSTANCE(thermal_zone);
239
240     if (read_temperature(thermal_zone, &temperature) != 0)
241         goto error;
242
243     if (temperature.raw_value >= max_threshold) {
244         START_COLOR("color_bad");
245         colorful_output = true;
246         if (format_above_threshold != NULL)
247             selected_format = format_above_threshold;
248     }
249
250     for (walk = selected_format; *walk != '\0'; walk++) {
251         if (*walk != '%') {
252             *(outwalk++) = *walk;
253
254         } else if (BEGINS_WITH(walk + 1, "degrees")) {
255             outwalk += sprintf(outwalk, "%s", temperature.formatted_value);
256             walk += strlen("degrees");
257
258         } else {
259             *(outwalk++) = '%';
260         }
261     }
262
263     if (colorful_output) {
264         END_COLOR;
265         colorful_output = false;
266     }
267
268     free(thermal_zone);
269
270     OUTPUT_FULL_TEXT(buffer);
271     return;
272 error:
273     free(thermal_zone);
274 #endif
275
276     OUTPUT_FULL_TEXT("can't read temp");
277     (void)fputs("i3status: Cannot read temperature. Verify that you have a thermal zone in /sys/class/thermal or disable the cpu_temperature module in your i3status config.\n", stderr);
278 }