Imported Upstream version 2.9
Michael Stapelberg
9 years ago
0 | 2015-03-22 i3status 2.9 | |
1 | ||
2 | • meta: i3status is now on GitHub and uses clang-format | |
3 | • allow customization of the module separator | |
4 | • add “align” and “min_width” options for each module | |
5 | • fix CFLAGS and LDFLAGS for DragonFly and FreeBSD | |
6 | • add contrib/net-speed, a more light-weight reimplementation of | |
7 | contrib/measure-net-speed.bash | |
8 | • battery: add hide_seconds option | |
9 | • battery: fix for systems without POWER_SUPPLY_VOLTAGE_NOW | |
10 | • battery: for %percentage, drop leading 0 for values < 10 | |
11 | • battery: add NetBSD support | |
12 | • battery: use absolute values for (dis)charging rates | |
13 | • battery: introduce status_{chr,bat,full} | |
14 | • cpu_temperature: fix displaying multiple sensors | |
15 | • cpu_temperature: fix NetBSD support | |
16 | • cpu_temperature: fix DragonFly support | |
17 | • disk: introduce threshold_type and low_threshold | |
18 | • disk: add format_not_mounted for unmounted paths | |
19 | • ethernet, wireless: support special interface name _first_ | |
20 | • run_watch: check all matching pid files until first valid one | |
21 | • volume: fix wrong color tags with xmobar | |
22 | • wireless: support %frequency (2.4 vs. 5 GHz) | |
23 | ||
0 | 24 | 2014-01-05 i3status 2.8 |
1 | 25 | |
2 | 26 | • Fix build on GNU/Hurd |
18 | 18 | LIBS+=-lconfuse |
19 | 19 | LIBS+=-lyajl |
20 | 20 | |
21 | VERSION=2.8 | |
22 | GIT_VERSION="2.8 (2014-01-05)" | |
21 | VERSION=2.9 | |
22 | GIT_VERSION="2.9 (2015-03-22)" | |
23 | 23 | OS:=$(shell uname) |
24 | 24 | |
25 | 25 | ifeq ($(OS),Linux) |
33 | 33 | LIBS+=-lbsd |
34 | 34 | endif |
35 | 35 | |
36 | ifeq ($(OS),OpenBSD) | |
36 | ifneq (, $(filter $(OS), DragonFly FreeBSD OpenBSD)) | |
37 | 37 | CFLAGS+=-I/usr/local/include/ |
38 | 38 | LDFLAGS+=-L/usr/local/lib/ |
39 | endif | |
40 | ||
41 | ifeq ($(OS),OpenBSD) | |
39 | 42 | LIBS+=-lossaudio |
40 | 43 | endif |
41 | 44 | |
45 | ifeq ($(OS), NetBSD) | |
46 | LIBS+= -lprop | |
47 | endif | |
42 | 48 | |
43 | 49 | # This probably applies for any pkgsrc based system |
44 | 50 | ifneq (, $(filter $(OS), NetBSD DragonFly)) |
45 | 51 | CFLAGS+=-I/usr/pkg/include/ |
46 | 52 | LDFLAGS+=-L/usr/pkg/lib/ |
47 | 53 | endif |
48 | ||
49 | ifeq ($(OS), NetBSD) | |
50 | LIBS+= -lprop | |
51 | endif | |
52 | ||
53 | 54 | |
54 | 55 | V ?= 0 |
55 | 56 | ifeq ($(V),0) |
102 | 103 | release: |
103 | 104 | [ -f i3status-${VERSION} ] || rm -rf i3status-${VERSION} |
104 | 105 | mkdir i3status-${VERSION} |
105 | find . -maxdepth 1 -type f \( -regex ".*\.\(c\|conf\|h\)" -or -name "Makefile" -or -name "LICENSE" -or -name "CHANGELOG" \) -exec cp '{}' i3status-${VERSION} \; | |
106 | find . -maxdepth 1 -type f \( -regex ".*\.\(c\|conf\|h\)" -or -name "README" -or -name "Makefile" -or -name "LICENSE" -or -name "CHANGELOG" \) -exec cp '{}' i3status-${VERSION} \; | |
106 | 107 | mkdir i3status-${VERSION}/src |
107 | 108 | mkdir i3status-${VERSION}/man |
108 | 109 | find src -maxdepth 1 -type f \( -regex ".*\.\(c\|h\)" \) -exec cp '{}' i3status-${VERSION}/src \; |
0 | ┌────────────────────────────┐ | |
1 | │ Description │ | |
2 | └────────────────────────────┘ | |
3 | ||
4 | i3status is a small program (about 1500 SLOC) for generating a status bar for | |
5 | i3bar, dzen2, xmobar or similar programs. It is designed to be very efficient by | |
6 | issuing a very small number of system calls, as one generally wants to update | |
7 | such a status line every second. This ensures that even under high load, your | |
8 | status bar is updated correctly. Also, it saves a bit of energy by not hogging | |
9 | your CPU as much as spawning the corresponding amount of shell commands would. | |
10 | ||
11 | ┌────────────────────────────┐ | |
12 | │ Development │ | |
13 | └────────────────────────────┘ | |
14 | ||
15 | i3status has the following dependencies: | |
16 | • libconfuse-dev | |
17 | • libyajl-dev | |
18 | • libasound2-dev | |
19 | • libiw-dev | |
20 | • libcap2-bin (for getting network status without root permissions) | |
21 | • asciidoc (only for the documentation) | |
22 | ||
23 | On debian-based systems, the following line will install all requirements: | |
24 | apt-get install libconfuse-dev libyajl-dev libasound2-dev libiw-dev asciidoc libcap2-bin | |
25 | ||
26 | ┌────────────────────────────┐ | |
27 | │ Upstream │ | |
28 | └────────────────────────────┘ | |
29 | ||
30 | i3status is developed at | |
31 | https://github.com/i3/i3status | |
32 | ||
33 | ┌────────────────────────────┐ | |
34 | │ Compilation │ | |
35 | └────────────────────────────┘ | |
36 | ||
37 | Compiling is done with the usual make-line | |
38 | make && sudo make install |
0 | #!/bin/sh | |
1 | # Copyright (c) 2014 Zhong Jianxin <azuwis@gmail.com> | |
2 | ||
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
4 | # of this software and associated documentation files (the "Software"), to deal | |
5 | # in the Software without restriction, including without limitation the rights | |
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
7 | # copies of the Software, and to permit persons to whom the Software is | |
8 | # furnished to do so, subject to the following conditions: | |
9 | ||
10 | # The above copyright notice and this permission notice shall be included in | |
11 | # all copies or substantial portions of the Software. | |
12 | ||
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
19 | # THE SOFTWARE. | |
20 | ||
21 | # Thank Stefan Breunig for the original implementation, see | |
22 | # contrib/measure-net-speed.bash. | |
23 | ||
24 | # i3status.conf should contain: | |
25 | # general { | |
26 | # output_format = i3bar | |
27 | # } | |
28 | ||
29 | # i3 config looks like this: | |
30 | # bar { | |
31 | # status_command exec /usr/share/doc/i3status/contrib/net-speed | |
32 | # } | |
33 | ||
34 | # Single interface | |
35 | #ifaces="eth0" | |
36 | ||
37 | # Multiple interfaces | |
38 | #ifaces="eth0 wlan0" | |
39 | ||
40 | # Auto detect | |
41 | ifaces=$(ls /sys/class/net | grep -E '^(eth|wlan)') | |
42 | ||
43 | # Interval must be the same as in i3status.conf | |
44 | #interval=5 | |
45 | ||
46 | # Auto detect | |
47 | if [ -f ~/.i3status.conf ]; then | |
48 | i3status_conf=~/.i3status.conf | |
49 | else | |
50 | i3status_conf="/etc/i3status.conf" | |
51 | fi | |
52 | interval=$(grep -o '^[[:space:]]*interval[[:space:]]*=[[:space:]]*[[:digit:]]\+' $i3status_conf | grep -o '[[:digit:]]\+') | |
53 | if [ x"$interval" = x ]; then | |
54 | interval=5 | |
55 | fi | |
56 | ||
57 | last_rx=0 | |
58 | last_tx=0 | |
59 | rate="" | |
60 | ||
61 | readable() { | |
62 | local byte=$1 | |
63 | local kib=$(( byte >> 10 )) | |
64 | if [ "$kib" -gt 1024 ]; then | |
65 | local mib_int=$(( kib >> 10 )) | |
66 | local mib_dec=$(( kib % 1024 * 976 / 10000 )) | |
67 | if [ "$mib_dec" -lt 10 ]; then | |
68 | mib_dec="0${mib_dec}" | |
69 | fi | |
70 | echo "${mib_int}.${mib_dec}M" | |
71 | else | |
72 | echo "${kib}K" | |
73 | fi | |
74 | } | |
75 | ||
76 | update_rate() { | |
77 | local rx=0 | |
78 | local tx=0 | |
79 | for iface in $ifaces; do | |
80 | local tmp_rx | |
81 | local tmp_tx | |
82 | local stat="/sys/class/net/${iface}/statistics" | |
83 | read tmp_rx < "${stat}/rx_bytes" | |
84 | read tmp_tx < "${stat}/tx_bytes" | |
85 | rx=$(( rx + tmp_rx )) | |
86 | tx=$(( tx + tmp_tx )) | |
87 | done | |
88 | rate="$(readable $(( (rx - last_rx) / interval )))↓ $(readable $(( (tx - last_tx) / interval )))↑" | |
89 | last_rx=$rx | |
90 | last_tx=$tx | |
91 | } | |
92 | ||
93 | # while :; do | |
94 | # update_rate | |
95 | # echo "$rate" | |
96 | # sleep "$interval" | |
97 | # done | |
98 | ||
99 | i3status | (read line && echo "$line" && read line && echo "$line" && read line && echo "$line" && update_rate && while : | |
100 | do | |
101 | read line | |
102 | update_rate | |
103 | echo ",[{\"full_text\":\"${rate}\" },${line#,\[}" || exit 1 | |
104 | done) |
0 | 0 | /* |
1 | * vim:ts=8:expandtab | |
1 | * vim:ts=4:sw=4:expandtab | |
2 | 2 | * |
3 | 3 | * i3status – Generates a status line for dzen2 or xmobar |
4 | 4 | * |
10 | 10 | * See file LICENSE for license information. |
11 | 11 | * |
12 | 12 | */ |
13 | #include <limits.h> | |
13 | 14 | #include <string.h> |
14 | 15 | #include <stdio.h> |
15 | 16 | #include <stdbool.h> |
32 | 33 | |
33 | 34 | #include "i3status.h" |
34 | 35 | |
35 | #define exit_if_null(pointer, ...) { if (pointer == NULL) die(__VA_ARGS__); } | |
36 | ||
37 | #define CFG_COLOR_OPTS(good, degraded, bad) \ | |
38 | CFG_STR("color_good", good, CFGF_NONE), \ | |
39 | CFG_STR("color_degraded", degraded, CFGF_NONE), \ | |
40 | CFG_STR("color_bad", bad, CFGF_NONE) | |
36 | #define exit_if_null(pointer, ...) \ | |
37 | { \ | |
38 | if (pointer == NULL) \ | |
39 | die(__VA_ARGS__); \ | |
40 | } | |
41 | ||
42 | #define CFG_CUSTOM_ALIGN_OPT \ | |
43 | CFG_STR_CB("align", NULL, CFGF_NONE, parse_align) | |
44 | ||
45 | #define CFG_COLOR_OPTS(good, degraded, bad) \ | |
46 | CFG_STR("color_good", good, CFGF_NONE), \ | |
47 | CFG_STR("color_degraded", degraded, CFGF_NONE), \ | |
48 | CFG_STR("color_bad", bad, CFGF_NONE) | |
41 | 49 | |
42 | 50 | #define CFG_CUSTOM_COLOR_OPTS CFG_COLOR_OPTS(NULL, NULL, NULL) |
51 | ||
52 | #define CFG_CUSTOM_MIN_WIDTH_OPT \ | |
53 | CFG_PTR_CB("min_width", NULL, CFGF_NONE, parse_min_width, free) | |
43 | 54 | |
44 | 55 | /* socket file descriptor for general purposes */ |
45 | 56 | int general_socket; |
47 | 58 | static bool exit_upon_signal = false; |
48 | 59 | |
49 | 60 | cfg_t *cfg, *cfg_general, *cfg_section; |
61 | ||
62 | void **cur_instance; | |
50 | 63 | |
51 | 64 | /* |
52 | 65 | * Set the exit_upon_signal flag, because one cannot do anything in a safe |
56 | 69 | * |
57 | 70 | */ |
58 | 71 | void fatalsig(int signum) { |
59 | exit_upon_signal = true; | |
72 | exit_upon_signal = true; | |
60 | 73 | } |
61 | 74 | |
62 | 75 | /* |
72 | 85 | * |
73 | 86 | */ |
74 | 87 | static bool path_exists(const char *path) { |
75 | struct stat buf; | |
76 | return (stat(path, &buf) == 0); | |
88 | struct stat buf; | |
89 | return (stat(path, &buf) == 0); | |
77 | 90 | } |
78 | 91 | |
79 | 92 | static void *scalloc(size_t size) { |
80 | void *result = calloc(size, 1); | |
81 | exit_if_null(result, "Error: out of memory (calloc(%zd))\n", size); | |
82 | return result; | |
93 | void *result = calloc(size, 1); | |
94 | exit_if_null(result, "Error: out of memory (calloc(%zd))\n", size); | |
95 | return result; | |
83 | 96 | } |
84 | 97 | |
85 | 98 | static char *sstrdup(const char *str) { |
86 | char *result = strdup(str); | |
87 | exit_if_null(result, "Error: out of memory (strdup())\n"); | |
88 | return result; | |
89 | } | |
90 | ||
99 | char *result = strdup(str); | |
100 | exit_if_null(result, "Error: out of memory (strdup())\n"); | |
101 | return result; | |
102 | } | |
103 | ||
104 | /* | |
105 | * Parses the "align" module option (to validate input). | |
106 | */ | |
107 | static int parse_align(cfg_t *context, cfg_opt_t *option, const char *value, void *result) { | |
108 | if (strcasecmp(value, "left") != 0 && strcasecmp(value, "right") != 0 && strcasecmp(value, "center") != 0) | |
109 | die("Invalid alignment attribute found in section %s, line %d: \"%s\"\n" | |
110 | "Valid attributes are: left, center, right\n", | |
111 | context->name, context->line, value); | |
112 | ||
113 | const char **cresult = result; | |
114 | *cresult = value; | |
115 | ||
116 | return 0; | |
117 | } | |
118 | ||
119 | /* | |
120 | * Parses the "min_width" module option whose value can either be a string or an integer. | |
121 | */ | |
122 | static int parse_min_width(cfg_t *context, cfg_opt_t *option, const char *value, void *result) { | |
123 | char *end; | |
124 | long num = strtol(value, &end, 10); | |
125 | ||
126 | if (num < 0) | |
127 | die("Invalid min_width attribute found in section %s, line %d: %d\n" | |
128 | "Expected positive integer or string\n", | |
129 | context->name, context->line, num); | |
130 | else if (num == LONG_MIN || num == LONG_MAX || (end && *end != '\0')) | |
131 | num = 0; | |
132 | ||
133 | if (strlen(value) == 0) | |
134 | die("Empty min_width attribute found in section %s, line %d\n" | |
135 | "Expected positive integer or non-empty string\n", | |
136 | context->name, context->line); | |
137 | ||
138 | if (strcmp(value, "0") == 0) | |
139 | die("Invalid min_width attribute found in section %s, line %d: \"%s\"\n" | |
140 | "Expected positive integer or string\n", | |
141 | context->name, context->line, value); | |
142 | ||
143 | struct min_width *parsed = scalloc(sizeof(struct min_width)); | |
144 | parsed->num = num; | |
145 | ||
146 | /* num is preferred, but if it’s 0 (i.e. not valid), store and use | |
147 | * the raw string value */ | |
148 | if (num == 0) | |
149 | parsed->str = sstrdup(value); | |
150 | ||
151 | struct min_width **cresult = result; | |
152 | *cresult = parsed; | |
153 | ||
154 | return 0; | |
155 | } | |
91 | 156 | |
92 | 157 | /* |
93 | 158 | * Validates a color in "#RRGGBB" format |
94 | 159 | * |
95 | 160 | */ |
96 | static int valid_color(const char *value) | |
97 | { | |
98 | if (strlen(value) != 7) return 0; | |
99 | if (value[0] != '#') return 0; | |
100 | for (int i = 1; i < 7; ++i) { | |
101 | if (value[i] >= '0' && value[i] <= '9') continue; | |
102 | if (value[i] >= 'a' && value[i] <= 'f') continue; | |
103 | if (value[i] >= 'A' && value[i] <= 'F') continue; | |
104 | return 0; | |
105 | } | |
106 | return 1; | |
161 | static int valid_color(const char *value) { | |
162 | if (strlen(value) != 7) | |
163 | return 0; | |
164 | if (value[0] != '#') | |
165 | return 0; | |
166 | for (int i = 1; i < 7; ++i) { | |
167 | if (value[i] >= '0' && value[i] <= '9') | |
168 | continue; | |
169 | if (value[i] >= 'a' && value[i] <= 'f') | |
170 | continue; | |
171 | if (value[i] >= 'A' && value[i] <= 'F') | |
172 | continue; | |
173 | return 0; | |
174 | } | |
175 | return 1; | |
107 | 176 | } |
108 | 177 | |
109 | 178 | /* |
113 | 182 | * |
114 | 183 | */ |
115 | 184 | static char *resolve_tilde(const char *path) { |
116 | static glob_t globbuf; | |
117 | char *head, *tail, *result = NULL; | |
118 | ||
119 | tail = strchr(path, '/'); | |
120 | head = strndup(path, tail ? (size_t)(tail - path) : strlen(path)); | |
121 | ||
122 | int res = glob(head, GLOB_TILDE, NULL, &globbuf); | |
123 | free(head); | |
124 | /* no match, or many wildcard matches are bad */ | |
125 | if (res == GLOB_NOMATCH || globbuf.gl_pathc != 1) | |
126 | result = sstrdup(path); | |
127 | else if (res != 0) { | |
128 | die("glob() failed"); | |
185 | static glob_t globbuf; | |
186 | char *head, *tail, *result = NULL; | |
187 | ||
188 | tail = strchr(path, '/'); | |
189 | head = strndup(path, tail ? (size_t)(tail - path) : strlen(path)); | |
190 | ||
191 | int res = glob(head, GLOB_TILDE, NULL, &globbuf); | |
192 | free(head); | |
193 | /* no match, or many wildcard matches are bad */ | |
194 | if (res == GLOB_NOMATCH || globbuf.gl_pathc != 1) | |
195 | result = sstrdup(path); | |
196 | else if (res != 0) { | |
197 | die("glob() failed"); | |
198 | } else { | |
199 | head = globbuf.gl_pathv[0]; | |
200 | result = scalloc(strlen(head) + (tail ? strlen(tail) : 0) + 1); | |
201 | strncpy(result, head, strlen(head)); | |
202 | if (tail) | |
203 | strncat(result, tail, strlen(tail)); | |
204 | } | |
205 | globfree(&globbuf); | |
206 | ||
207 | return result; | |
208 | } | |
209 | ||
210 | static char *get_config_path(void) { | |
211 | char *xdg_config_home, *xdg_config_dirs, *config_path; | |
212 | ||
213 | /* 1: check the traditional path under the home directory */ | |
214 | config_path = resolve_tilde("~/.i3status.conf"); | |
215 | if (path_exists(config_path)) | |
216 | return config_path; | |
217 | ||
218 | /* 2: check for $XDG_CONFIG_HOME/i3status/config */ | |
219 | if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL) | |
220 | xdg_config_home = "~/.config"; | |
221 | ||
222 | xdg_config_home = resolve_tilde(xdg_config_home); | |
223 | if (asprintf(&config_path, "%s/i3status/config", xdg_config_home) == -1) | |
224 | die("asprintf() failed"); | |
225 | free(xdg_config_home); | |
226 | ||
227 | if (path_exists(config_path)) | |
228 | return config_path; | |
229 | free(config_path); | |
230 | ||
231 | /* 3: check the traditional path under /etc */ | |
232 | config_path = SYSCONFDIR "/i3status.conf"; | |
233 | if (path_exists(config_path)) | |
234 | return sstrdup(config_path); | |
235 | ||
236 | /* 4: check for $XDG_CONFIG_DIRS/i3status/config */ | |
237 | if ((xdg_config_dirs = getenv("XDG_CONFIG_DIRS")) == NULL) | |
238 | xdg_config_dirs = "/etc/xdg"; | |
239 | ||
240 | char *buf = strdup(xdg_config_dirs); | |
241 | char *tok = strtok(buf, ":"); | |
242 | while (tok != NULL) { | |
243 | tok = resolve_tilde(tok); | |
244 | if (asprintf(&config_path, "%s/i3status/config", tok) == -1) | |
245 | die("asprintf() failed"); | |
246 | free(tok); | |
247 | if (path_exists(config_path)) { | |
248 | free(buf); | |
249 | return config_path; | |
250 | } | |
251 | free(config_path); | |
252 | tok = strtok(NULL, ":"); | |
253 | } | |
254 | free(buf); | |
255 | ||
256 | die("Unable to find the configuration file (looked at " | |
257 | "~/.i3status.conf, $XDG_CONFIG_HOME/i3status/config, " | |
258 | "/etc/i3status.conf and $XDG_CONFIG_DIRS/i3status/config)"); | |
259 | return NULL; | |
260 | } | |
261 | ||
262 | /* | |
263 | * Returns the default separator to use if no custom separator has been specified. | |
264 | */ | |
265 | static char *get_default_separator() { | |
266 | if (output_format == O_DZEN2) | |
267 | return "^p(5;-2)^ro(2)^p()^p(5)"; | |
268 | if (output_format == O_I3BAR) | |
269 | // anything besides the empty string indicates that the default separator should be used | |
270 | return "default"; | |
271 | return " | "; | |
272 | } | |
273 | ||
274 | int main(int argc, char *argv[]) { | |
275 | unsigned int j; | |
276 | ||
277 | cfg_opt_t general_opts[] = { | |
278 | CFG_STR("output_format", "auto", CFGF_NONE), | |
279 | CFG_BOOL("colors", 1, CFGF_NONE), | |
280 | CFG_STR("separator", "default", CFGF_NONE), | |
281 | CFG_STR("color_separator", "#333333", CFGF_NONE), | |
282 | CFG_INT("interval", 1, CFGF_NONE), | |
283 | CFG_COLOR_OPTS("#00FF00", "#FFFF00", "#FF0000"), | |
284 | CFG_END()}; | |
285 | ||
286 | cfg_opt_t run_watch_opts[] = { | |
287 | CFG_STR("pidfile", NULL, CFGF_NONE), | |
288 | CFG_STR("format", "%title: %status", CFGF_NONE), | |
289 | CFG_CUSTOM_ALIGN_OPT, | |
290 | CFG_CUSTOM_COLOR_OPTS, | |
291 | CFG_CUSTOM_MIN_WIDTH_OPT, | |
292 | CFG_END()}; | |
293 | ||
294 | cfg_opt_t path_exists_opts[] = { | |
295 | CFG_STR("path", NULL, CFGF_NONE), | |
296 | CFG_STR("format", "%title: %status", CFGF_NONE), | |
297 | CFG_CUSTOM_ALIGN_OPT, | |
298 | CFG_CUSTOM_COLOR_OPTS, | |
299 | CFG_CUSTOM_MIN_WIDTH_OPT, | |
300 | CFG_END()}; | |
301 | ||
302 | cfg_opt_t wireless_opts[] = { | |
303 | CFG_STR("format_up", "W: (%quality at %essid, %bitrate) %ip", CFGF_NONE), | |
304 | CFG_STR("format_down", "W: down", CFGF_NONE), | |
305 | CFG_CUSTOM_ALIGN_OPT, | |
306 | CFG_CUSTOM_COLOR_OPTS, | |
307 | CFG_CUSTOM_MIN_WIDTH_OPT, | |
308 | CFG_END()}; | |
309 | ||
310 | cfg_opt_t ethernet_opts[] = { | |
311 | CFG_STR("format_up", "E: %ip (%speed)", CFGF_NONE), | |
312 | CFG_STR("format_down", "E: down", CFGF_NONE), | |
313 | CFG_CUSTOM_ALIGN_OPT, | |
314 | CFG_CUSTOM_COLOR_OPTS, | |
315 | CFG_CUSTOM_MIN_WIDTH_OPT, | |
316 | CFG_END()}; | |
317 | ||
318 | cfg_opt_t ipv6_opts[] = { | |
319 | CFG_STR("format_up", "%ip", CFGF_NONE), | |
320 | CFG_STR("format_down", "no IPv6", CFGF_NONE), | |
321 | CFG_CUSTOM_ALIGN_OPT, | |
322 | CFG_CUSTOM_COLOR_OPTS, | |
323 | CFG_CUSTOM_MIN_WIDTH_OPT, | |
324 | CFG_END()}; | |
325 | ||
326 | cfg_opt_t battery_opts[] = { | |
327 | CFG_STR("format", "%status %percentage %remaining", CFGF_NONE), | |
328 | CFG_STR("format_down", "No battery", CFGF_NONE), | |
329 | CFG_STR("status_chr", "CHR", CFGF_NONE), | |
330 | CFG_STR("status_bat", "BAT", CFGF_NONE), | |
331 | CFG_STR("status_full", "FULL", CFGF_NONE), | |
332 | CFG_STR("path", "/sys/class/power_supply/BAT%d/uevent", CFGF_NONE), | |
333 | CFG_INT("low_threshold", 30, CFGF_NONE), | |
334 | CFG_STR("threshold_type", "time", CFGF_NONE), | |
335 | CFG_BOOL("last_full_capacity", false, CFGF_NONE), | |
336 | CFG_BOOL("integer_battery_capacity", false, CFGF_NONE), | |
337 | CFG_BOOL("hide_seconds", false, CFGF_NONE), | |
338 | CFG_CUSTOM_ALIGN_OPT, | |
339 | CFG_CUSTOM_COLOR_OPTS, | |
340 | CFG_CUSTOM_MIN_WIDTH_OPT, | |
341 | CFG_END()}; | |
342 | ||
343 | cfg_opt_t time_opts[] = { | |
344 | CFG_STR("format", "%Y-%m-%d %H:%M:%S", CFGF_NONE), | |
345 | CFG_CUSTOM_ALIGN_OPT, | |
346 | CFG_CUSTOM_MIN_WIDTH_OPT, | |
347 | CFG_END()}; | |
348 | ||
349 | cfg_opt_t tztime_opts[] = { | |
350 | CFG_STR("format", "%Y-%m-%d %H:%M:%S %Z", CFGF_NONE), | |
351 | CFG_STR("timezone", "", CFGF_NONE), | |
352 | CFG_CUSTOM_ALIGN_OPT, | |
353 | CFG_CUSTOM_MIN_WIDTH_OPT, | |
354 | CFG_END()}; | |
355 | ||
356 | cfg_opt_t ddate_opts[] = { | |
357 | CFG_STR("format", "%{%a, %b %d%}, %Y%N - %H", CFGF_NONE), | |
358 | CFG_CUSTOM_ALIGN_OPT, | |
359 | CFG_CUSTOM_MIN_WIDTH_OPT, | |
360 | CFG_END()}; | |
361 | ||
362 | cfg_opt_t load_opts[] = { | |
363 | CFG_STR("format", "%1min %5min %15min", CFGF_NONE), | |
364 | CFG_FLOAT("max_threshold", 5, CFGF_NONE), | |
365 | CFG_CUSTOM_ALIGN_OPT, | |
366 | CFG_CUSTOM_COLOR_OPTS, | |
367 | CFG_CUSTOM_MIN_WIDTH_OPT, | |
368 | CFG_END()}; | |
369 | ||
370 | cfg_opt_t usage_opts[] = { | |
371 | CFG_STR("format", "%usage", CFGF_NONE), | |
372 | CFG_CUSTOM_ALIGN_OPT, | |
373 | CFG_CUSTOM_MIN_WIDTH_OPT, | |
374 | CFG_END()}; | |
375 | ||
376 | cfg_opt_t temp_opts[] = { | |
377 | CFG_STR("format", "%degrees C", CFGF_NONE), | |
378 | CFG_STR("path", NULL, CFGF_NONE), | |
379 | CFG_INT("max_threshold", 75, CFGF_NONE), | |
380 | CFG_CUSTOM_ALIGN_OPT, | |
381 | CFG_CUSTOM_COLOR_OPTS, | |
382 | CFG_CUSTOM_MIN_WIDTH_OPT, | |
383 | CFG_END()}; | |
384 | ||
385 | cfg_opt_t disk_opts[] = { | |
386 | CFG_STR("format", "%free", CFGF_NONE), | |
387 | CFG_STR("format_not_mounted", NULL, CFGF_NONE), | |
388 | CFG_STR("prefix_type", "binary", CFGF_NONE), | |
389 | CFG_STR("threshold_type", "percentage_avail", CFGF_NONE), | |
390 | CFG_FLOAT("low_threshold", 0, CFGF_NONE), | |
391 | CFG_CUSTOM_ALIGN_OPT, | |
392 | CFG_CUSTOM_COLOR_OPTS, | |
393 | CFG_CUSTOM_MIN_WIDTH_OPT, | |
394 | CFG_END()}; | |
395 | ||
396 | cfg_opt_t volume_opts[] = { | |
397 | CFG_STR("format", "♪: %volume", CFGF_NONE), | |
398 | CFG_STR("format_muted", "♪: 0%%", CFGF_NONE), | |
399 | CFG_STR("device", "default", CFGF_NONE), | |
400 | CFG_STR("mixer", "Master", CFGF_NONE), | |
401 | CFG_INT("mixer_idx", 0, CFGF_NONE), | |
402 | CFG_CUSTOM_ALIGN_OPT, | |
403 | CFG_CUSTOM_COLOR_OPTS, | |
404 | CFG_CUSTOM_MIN_WIDTH_OPT, | |
405 | CFG_END()}; | |
406 | ||
407 | cfg_opt_t opts[] = { | |
408 | CFG_STR_LIST("order", "{}", CFGF_NONE), | |
409 | CFG_SEC("general", general_opts, CFGF_NONE), | |
410 | CFG_SEC("run_watch", run_watch_opts, CFGF_TITLE | CFGF_MULTI), | |
411 | CFG_SEC("path_exists", path_exists_opts, CFGF_TITLE | CFGF_MULTI), | |
412 | CFG_SEC("wireless", wireless_opts, CFGF_TITLE | CFGF_MULTI), | |
413 | CFG_SEC("ethernet", ethernet_opts, CFGF_TITLE | CFGF_MULTI), | |
414 | CFG_SEC("battery", battery_opts, CFGF_TITLE | CFGF_MULTI), | |
415 | CFG_SEC("cpu_temperature", temp_opts, CFGF_TITLE | CFGF_MULTI), | |
416 | CFG_SEC("disk", disk_opts, CFGF_TITLE | CFGF_MULTI), | |
417 | CFG_SEC("volume", volume_opts, CFGF_TITLE | CFGF_MULTI), | |
418 | CFG_SEC("ipv6", ipv6_opts, CFGF_NONE), | |
419 | CFG_SEC("time", time_opts, CFGF_NONE), | |
420 | CFG_SEC("tztime", tztime_opts, CFGF_TITLE | CFGF_MULTI), | |
421 | CFG_SEC("ddate", ddate_opts, CFGF_NONE), | |
422 | CFG_SEC("load", load_opts, CFGF_NONE), | |
423 | CFG_SEC("cpu_usage", usage_opts, CFGF_NONE), | |
424 | CFG_END()}; | |
425 | ||
426 | char *configfile = NULL; | |
427 | int o, option_index = 0; | |
428 | struct option long_options[] = { | |
429 | {"config", required_argument, 0, 'c'}, | |
430 | {"help", no_argument, 0, 'h'}, | |
431 | {"version", no_argument, 0, 'v'}, | |
432 | {0, 0, 0, 0}}; | |
433 | ||
434 | struct sigaction action; | |
435 | memset(&action, 0, sizeof(struct sigaction)); | |
436 | action.sa_handler = fatalsig; | |
437 | ||
438 | /* Exit upon SIGPIPE because when we have nowhere to write to, gathering system | |
439 | * information is pointless. Also exit explicitly on SIGTERM and SIGINT because | |
440 | * only this will trigger a reset of the cursor in the terminal output-format. | |
441 | */ | |
442 | sigaction(SIGPIPE, &action, NULL); | |
443 | sigaction(SIGTERM, &action, NULL); | |
444 | sigaction(SIGINT, &action, NULL); | |
445 | ||
446 | memset(&action, 0, sizeof(struct sigaction)); | |
447 | action.sa_handler = sigusr1; | |
448 | sigaction(SIGUSR1, &action, NULL); | |
449 | ||
450 | if (setlocale(LC_ALL, "") == NULL) | |
451 | die("Could not set locale. Please make sure all your LC_* / LANG settings are correct."); | |
452 | ||
453 | while ((o = getopt_long(argc, argv, "c:hv", long_options, &option_index)) != -1) | |
454 | if ((char)o == 'c') | |
455 | configfile = optarg; | |
456 | else if ((char)o == 'h') { | |
457 | printf("i3status " VERSION " © 2008-2012 Michael Stapelberg and contributors\n" | |
458 | "Syntax: %s [-c <configfile>] [-h] [-v]\n", | |
459 | argv[0]); | |
460 | return 0; | |
461 | } else if ((char)o == 'v') { | |
462 | printf("i3status " VERSION " © 2008-2012 Michael Stapelberg and contributors\n"); | |
463 | return 0; | |
464 | } | |
465 | ||
466 | if (configfile == NULL) | |
467 | configfile = get_config_path(); | |
468 | ||
469 | cfg = cfg_init(opts, CFGF_NOCASE); | |
470 | if (cfg_parse(cfg, configfile) == CFG_PARSE_ERROR) | |
471 | return EXIT_FAILURE; | |
472 | ||
473 | if (cfg_size(cfg, "order") == 0) | |
474 | die("Your 'order' array is empty. Please fix your config.\n"); | |
475 | ||
476 | cfg_general = cfg_getsec(cfg, "general"); | |
477 | if (cfg_general == NULL) | |
478 | die("Could not get section \"general\"\n"); | |
479 | ||
480 | char *output_str = cfg_getstr(cfg_general, "output_format"); | |
481 | if (strcasecmp(output_str, "auto") == 0) { | |
482 | fprintf(stderr, "i3status: trying to auto-detect output_format setting\n"); | |
483 | output_str = auto_detect_format(); | |
484 | if (!output_str) { | |
485 | output_str = "none"; | |
486 | fprintf(stderr, "i3status: falling back to \"none\"\n"); | |
129 | 487 | } else { |
130 | head = globbuf.gl_pathv[0]; | |
131 | result = scalloc(strlen(head) + (tail ? strlen(tail) : 0) + 1); | |
132 | strncpy(result, head, strlen(head)); | |
133 | if (tail) | |
134 | strncat(result, tail, strlen(tail)); | |
488 | fprintf(stderr, "i3status: auto-detected \"%s\"\n", output_str); | |
135 | 489 | } |
136 | globfree(&globbuf); | |
137 | ||
138 | return result; | |
139 | } | |
140 | ||
141 | static char *get_config_path(void) { | |
142 | char *xdg_config_home, *xdg_config_dirs, *config_path; | |
143 | ||
144 | /* 1: check the traditional path under the home directory */ | |
145 | config_path = resolve_tilde("~/.i3status.conf"); | |
146 | if (path_exists(config_path)) | |
147 | return config_path; | |
148 | ||
149 | /* 2: check for $XDG_CONFIG_HOME/i3status/config */ | |
150 | if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL) | |
151 | xdg_config_home = "~/.config"; | |
152 | ||
153 | xdg_config_home = resolve_tilde(xdg_config_home); | |
154 | if (asprintf(&config_path, "%s/i3status/config", xdg_config_home) == -1) | |
155 | die("asprintf() failed"); | |
156 | free(xdg_config_home); | |
157 | ||
158 | if (path_exists(config_path)) | |
159 | return config_path; | |
160 | free(config_path); | |
161 | ||
162 | /* 3: check the traditional path under /etc */ | |
163 | config_path = SYSCONFDIR "/i3status.conf"; | |
164 | if (path_exists(config_path)) | |
165 | return sstrdup(config_path); | |
166 | ||
167 | /* 4: check for $XDG_CONFIG_DIRS/i3status/config */ | |
168 | if ((xdg_config_dirs = getenv("XDG_CONFIG_DIRS")) == NULL) | |
169 | xdg_config_dirs = "/etc/xdg"; | |
170 | ||
171 | char *buf = strdup(xdg_config_dirs); | |
172 | char *tok = strtok(buf, ":"); | |
173 | while (tok != NULL) { | |
174 | tok = resolve_tilde(tok); | |
175 | if (asprintf(&config_path, "%s/i3status/config", tok) == -1) | |
176 | die("asprintf() failed"); | |
177 | free(tok); | |
178 | if (path_exists(config_path)) { | |
179 | free(buf); | |
180 | return config_path; | |
181 | } | |
182 | free(config_path); | |
183 | tok = strtok(NULL, ":"); | |
490 | } | |
491 | ||
492 | if (strcasecmp(output_str, "dzen2") == 0) | |
493 | output_format = O_DZEN2; | |
494 | else if (strcasecmp(output_str, "xmobar") == 0) | |
495 | output_format = O_XMOBAR; | |
496 | else if (strcasecmp(output_str, "i3bar") == 0) | |
497 | output_format = O_I3BAR; | |
498 | else if (strcasecmp(output_str, "term") == 0) | |
499 | output_format = O_TERM; | |
500 | else if (strcasecmp(output_str, "none") == 0) | |
501 | output_format = O_NONE; | |
502 | else | |
503 | die("Unknown output format: \"%s\"\n", output_str); | |
504 | ||
505 | const char *separator = cfg_getstr(cfg_general, "separator"); | |
506 | ||
507 | // if no custom separator has been provided, use the default one | |
508 | if (strcasecmp(separator, "default") == 0) | |
509 | separator = get_default_separator(); | |
510 | ||
511 | if (!valid_color(cfg_getstr(cfg_general, "color_good")) || !valid_color(cfg_getstr(cfg_general, "color_degraded")) || !valid_color(cfg_getstr(cfg_general, "color_bad")) || !valid_color(cfg_getstr(cfg_general, "color_separator"))) | |
512 | die("Bad color format"); | |
513 | ||
514 | #if YAJL_MAJOR >= 2 | |
515 | yajl_gen json_gen = yajl_gen_alloc(NULL); | |
516 | #else | |
517 | yajl_gen json_gen = yajl_gen_alloc(NULL, NULL); | |
518 | #endif | |
519 | ||
520 | if (output_format == O_I3BAR) { | |
521 | /* Initialize the i3bar protocol. See i3/docs/i3bar-protocol | |
522 | * for details. */ | |
523 | printf("{\"version\":1}\n[\n"); | |
524 | fflush(stdout); | |
525 | yajl_gen_array_open(json_gen); | |
526 | yajl_gen_clear(json_gen); | |
527 | } | |
528 | if (output_format == O_TERM) { | |
529 | /* Save the cursor-position and hide the cursor */ | |
530 | printf("\033[s\033[?25l"); | |
531 | /* Undo at exit */ | |
532 | atexit(&reset_cursor); | |
533 | } | |
534 | ||
535 | if ((general_socket = socket(AF_INET, SOCK_DGRAM, 0)) == -1) | |
536 | die("Could not create socket\n"); | |
537 | ||
538 | int interval = cfg_getint(cfg_general, "interval"); | |
539 | ||
540 | /* One memory page which each plugin can use to buffer output. | |
541 | * Even though it’s unclean, we just assume that the user will not | |
542 | * specify a format string which expands to something longer than 4096 | |
543 | * bytes — given that the output of i3status is used to display | |
544 | * information on screen, more than 1024 characters for the full line | |
545 | * (!), not individual plugins, seem very unlikely. */ | |
546 | char buffer[4096]; | |
547 | ||
548 | void **per_instance = calloc(cfg_size(cfg, "order"), sizeof(*per_instance)); | |
549 | ||
550 | while (1) { | |
551 | if (exit_upon_signal) { | |
552 | fprintf(stderr, "Exiting due to signal.\n"); | |
553 | exit(1); | |
184 | 554 | } |
185 | free(buf); | |
186 | ||
187 | die("Unable to find the configuration file (looked at " | |
188 | "~/.i3status.conf, $XDG_CONFIG_HOME/i3status/config, " | |
189 | "/etc/i3status.conf and $XDG_CONFIG_DIRS/i3status/config)"); | |
190 | return NULL; | |
191 | } | |
192 | ||
193 | int main(int argc, char *argv[]) { | |
194 | unsigned int j; | |
195 | ||
196 | cfg_opt_t general_opts[] = { | |
197 | CFG_STR("output_format", "auto", CFGF_NONE), | |
198 | CFG_BOOL("colors", 1, CFGF_NONE), | |
199 | CFG_STR("color_separator", "#333333", CFGF_NONE), | |
200 | CFG_INT("interval", 1, CFGF_NONE), | |
201 | CFG_COLOR_OPTS("#00FF00", "#FFFF00", "#FF0000"), | |
202 | CFG_END() | |
203 | }; | |
204 | ||
205 | cfg_opt_t run_watch_opts[] = { | |
206 | CFG_STR("pidfile", NULL, CFGF_NONE), | |
207 | CFG_STR("format", "%title: %status", CFGF_NONE), | |
208 | CFG_CUSTOM_COLOR_OPTS, | |
209 | CFG_END() | |
210 | }; | |
211 | ||
212 | cfg_opt_t path_exists_opts[] = { | |
213 | CFG_STR("path", NULL, CFGF_NONE), | |
214 | CFG_STR("format", "%title: %status", CFGF_NONE), | |
215 | CFG_CUSTOM_COLOR_OPTS, | |
216 | CFG_END() | |
217 | }; | |
218 | ||
219 | cfg_opt_t wireless_opts[] = { | |
220 | CFG_STR("format_up", "W: (%quality at %essid, %bitrate) %ip", CFGF_NONE), | |
221 | CFG_STR("format_down", "W: down", CFGF_NONE), | |
222 | CFG_CUSTOM_COLOR_OPTS, | |
223 | CFG_END() | |
224 | }; | |
225 | ||
226 | cfg_opt_t ethernet_opts[] = { | |
227 | CFG_STR("format_up", "E: %ip (%speed)", CFGF_NONE), | |
228 | CFG_STR("format_down", "E: down", CFGF_NONE), | |
229 | CFG_CUSTOM_COLOR_OPTS, | |
230 | CFG_END() | |
231 | }; | |
232 | ||
233 | cfg_opt_t ipv6_opts[] = { | |
234 | CFG_STR("format_up", "%ip", CFGF_NONE), | |
235 | CFG_STR("format_down", "no IPv6", CFGF_NONE), | |
236 | CFG_CUSTOM_COLOR_OPTS, | |
237 | CFG_END() | |
238 | }; | |
239 | ||
240 | cfg_opt_t battery_opts[] = { | |
241 | CFG_STR("format", "%status %percentage %remaining", CFGF_NONE), | |
242 | CFG_STR("format_down", "No battery", CFGF_NONE), | |
243 | CFG_STR("path", "/sys/class/power_supply/BAT%d/uevent", CFGF_NONE), | |
244 | CFG_INT("low_threshold", 30, CFGF_NONE), | |
245 | CFG_STR("threshold_type", "time", CFGF_NONE), | |
246 | CFG_BOOL("last_full_capacity", false, CFGF_NONE), | |
247 | CFG_BOOL("integer_battery_capacity", false, CFGF_NONE), | |
248 | CFG_CUSTOM_COLOR_OPTS, | |
249 | CFG_END() | |
250 | }; | |
251 | ||
252 | cfg_opt_t time_opts[] = { | |
253 | CFG_STR("format", "%Y-%m-%d %H:%M:%S", CFGF_NONE), | |
254 | CFG_END() | |
255 | }; | |
256 | ||
257 | cfg_opt_t tztime_opts[] = { | |
258 | CFG_STR("format", "%Y-%m-%d %H:%M:%S %Z", CFGF_NONE), | |
259 | CFG_STR("timezone", "", CFGF_NONE), | |
260 | CFG_END() | |
261 | }; | |
262 | ||
263 | cfg_opt_t ddate_opts[] = { | |
264 | CFG_STR("format", "%{%a, %b %d%}, %Y%N - %H", CFGF_NONE), | |
265 | CFG_END() | |
266 | }; | |
267 | ||
268 | cfg_opt_t load_opts[] = { | |
269 | CFG_STR("format", "%1min %5min %15min", CFGF_NONE), | |
270 | CFG_FLOAT("max_threshold", 5, CFGF_NONE), | |
271 | CFG_CUSTOM_COLOR_OPTS, | |
272 | CFG_END() | |
273 | }; | |
274 | ||
275 | cfg_opt_t usage_opts[] = { | |
276 | CFG_STR("format", "%usage", CFGF_NONE), | |
277 | CFG_END() | |
278 | }; | |
279 | ||
280 | cfg_opt_t temp_opts[] = { | |
281 | CFG_STR("format", "%degrees C", CFGF_NONE), | |
282 | CFG_STR("path", NULL, CFGF_NONE), | |
283 | CFG_INT("max_threshold", 75, CFGF_NONE), | |
284 | CFG_CUSTOM_COLOR_OPTS, | |
285 | CFG_END() | |
286 | }; | |
287 | ||
288 | cfg_opt_t disk_opts[] = { | |
289 | CFG_STR("format", "%free", CFGF_NONE), | |
290 | CFG_STR("prefix_type", "binary", CFGF_NONE), | |
291 | CFG_END() | |
292 | }; | |
293 | ||
294 | cfg_opt_t volume_opts[] = { | |
295 | CFG_STR("format", "♪: %volume", CFGF_NONE), | |
296 | CFG_STR("format_muted", "♪: 0%%", CFGF_NONE), | |
297 | CFG_STR("device", "default", CFGF_NONE), | |
298 | CFG_STR("mixer", "Master", CFGF_NONE), | |
299 | CFG_INT("mixer_idx", 0, CFGF_NONE), | |
300 | CFG_CUSTOM_COLOR_OPTS, | |
301 | CFG_END() | |
302 | }; | |
303 | ||
304 | cfg_opt_t opts[] = { | |
305 | CFG_STR_LIST("order", "{}", CFGF_NONE), | |
306 | CFG_SEC("general", general_opts, CFGF_NONE), | |
307 | CFG_SEC("run_watch", run_watch_opts, CFGF_TITLE | CFGF_MULTI), | |
308 | CFG_SEC("path_exists", path_exists_opts, CFGF_TITLE | CFGF_MULTI), | |
309 | CFG_SEC("wireless", wireless_opts, CFGF_TITLE | CFGF_MULTI), | |
310 | CFG_SEC("ethernet", ethernet_opts, CFGF_TITLE | CFGF_MULTI), | |
311 | CFG_SEC("battery", battery_opts, CFGF_TITLE | CFGF_MULTI), | |
312 | CFG_SEC("cpu_temperature", temp_opts, CFGF_TITLE | CFGF_MULTI), | |
313 | CFG_SEC("disk", disk_opts, CFGF_TITLE | CFGF_MULTI), | |
314 | CFG_SEC("volume", volume_opts, CFGF_TITLE | CFGF_MULTI), | |
315 | CFG_SEC("ipv6", ipv6_opts, CFGF_NONE), | |
316 | CFG_SEC("time", time_opts, CFGF_NONE), | |
317 | CFG_SEC("tztime", tztime_opts, CFGF_TITLE | CFGF_MULTI), | |
318 | CFG_SEC("ddate", ddate_opts, CFGF_NONE), | |
319 | CFG_SEC("load", load_opts, CFGF_NONE), | |
320 | CFG_SEC("cpu_usage", usage_opts, CFGF_NONE), | |
321 | CFG_CUSTOM_COLOR_OPTS, | |
322 | CFG_END() | |
323 | }; | |
324 | ||
325 | char *configfile = NULL; | |
326 | int o, option_index = 0; | |
327 | struct option long_options[] = { | |
328 | {"config", required_argument, 0, 'c'}, | |
329 | {"help", no_argument, 0, 'h'}, | |
330 | {"version", no_argument, 0, 'v'}, | |
331 | {0, 0, 0, 0} | |
332 | }; | |
333 | ||
334 | struct sigaction action; | |
335 | memset(&action, 0, sizeof(struct sigaction)); | |
336 | action.sa_handler = fatalsig; | |
337 | ||
338 | /* Exit upon SIGPIPE because when we have nowhere to write to, gathering system | |
339 | * information is pointless. Also exit explicitly on SIGTERM and SIGINT because | |
340 | * only this will trigger a reset of the cursor in the terminal output-format. | |
341 | */ | |
342 | sigaction(SIGPIPE, &action, NULL); | |
343 | sigaction(SIGTERM, &action, NULL); | |
344 | sigaction(SIGINT, &action, NULL); | |
345 | ||
346 | memset(&action, 0, sizeof(struct sigaction)); | |
347 | action.sa_handler = sigusr1; | |
348 | sigaction(SIGUSR1, &action, NULL); | |
349 | ||
350 | if (setlocale(LC_ALL, "") == NULL) | |
351 | die("Could not set locale. Please make sure all your LC_* / LANG settings are correct."); | |
352 | ||
353 | while ((o = getopt_long(argc, argv, "c:hv", long_options, &option_index)) != -1) | |
354 | if ((char)o == 'c') | |
355 | configfile = optarg; | |
356 | else if ((char)o == 'h') { | |
357 | printf("i3status " VERSION " © 2008-2012 Michael Stapelberg and contributors\n" | |
358 | "Syntax: %s [-c <configfile>] [-h] [-v]\n", argv[0]); | |
359 | return 0; | |
360 | } else if ((char)o == 'v') { | |
361 | printf("i3status " VERSION " © 2008-2012 Michael Stapelberg and contributors\n"); | |
362 | return 0; | |
363 | } | |
364 | ||
365 | ||
366 | if (configfile == NULL) | |
367 | configfile = get_config_path(); | |
368 | ||
369 | cfg = cfg_init(opts, CFGF_NOCASE); | |
370 | if (cfg_parse(cfg, configfile) == CFG_PARSE_ERROR) | |
371 | return EXIT_FAILURE; | |
372 | ||
373 | if (cfg_size(cfg, "order") == 0) | |
374 | die("Your 'order' array is empty. Please fix your config.\n"); | |
375 | ||
376 | cfg_general = cfg_getsec(cfg, "general"); | |
377 | if (cfg_general == NULL) | |
378 | die("Could not get section \"general\"\n"); | |
379 | ||
380 | char *output_str = cfg_getstr(cfg_general, "output_format"); | |
381 | if (strcasecmp(output_str, "auto") == 0) { | |
382 | fprintf(stderr, "i3status: trying to auto-detect output_format setting\n"); | |
383 | output_str = auto_detect_format(); | |
384 | if (!output_str) { | |
385 | output_str = "none"; | |
386 | fprintf(stderr, "i3status: falling back to \"none\"\n"); | |
387 | } else { | |
388 | fprintf(stderr, "i3status: auto-detected \"%s\"\n", output_str); | |
389 | } | |
555 | struct timeval tv; | |
556 | gettimeofday(&tv, NULL); | |
557 | if (output_format == O_I3BAR) | |
558 | yajl_gen_array_open(json_gen); | |
559 | else if (output_format == O_TERM) | |
560 | /* Restore the cursor-position, clear line */ | |
561 | printf("\033[u\033[K"); | |
562 | for (j = 0; j < cfg_size(cfg, "order"); j++) { | |
563 | cur_instance = per_instance + j; | |
564 | if (j > 0) | |
565 | print_separator(separator); | |
566 | ||
567 | const char *current = cfg_getnstr(cfg, "order", j); | |
568 | ||
569 | CASE_SEC("ipv6") { | |
570 | SEC_OPEN_MAP("ipv6"); | |
571 | print_ipv6_info(json_gen, buffer, cfg_getstr(sec, "format_up"), cfg_getstr(sec, "format_down")); | |
572 | SEC_CLOSE_MAP; | |
573 | } | |
574 | ||
575 | CASE_SEC_TITLE("wireless") { | |
576 | SEC_OPEN_MAP("wireless"); | |
577 | const char *interface = NULL; | |
578 | if (strcasecmp(title, "_first_") == 0) | |
579 | interface = first_eth_interface(NET_TYPE_WIRELESS); | |
580 | if (interface == NULL) | |
581 | interface = title; | |
582 | print_wireless_info(json_gen, buffer, interface, cfg_getstr(sec, "format_up"), cfg_getstr(sec, "format_down")); | |
583 | SEC_CLOSE_MAP; | |
584 | } | |
585 | ||
586 | CASE_SEC_TITLE("ethernet") { | |
587 | SEC_OPEN_MAP("ethernet"); | |
588 | const char *interface = NULL; | |
589 | if (strcasecmp(title, "_first_") == 0) | |
590 | interface = first_eth_interface(NET_TYPE_ETHERNET); | |
591 | if (interface == NULL) | |
592 | interface = title; | |
593 | print_eth_info(json_gen, buffer, interface, cfg_getstr(sec, "format_up"), cfg_getstr(sec, "format_down")); | |
594 | SEC_CLOSE_MAP; | |
595 | } | |
596 | ||
597 | CASE_SEC_TITLE("battery") { | |
598 | SEC_OPEN_MAP("battery"); | |
599 | print_battery_info(json_gen, buffer, atoi(title), cfg_getstr(sec, "path"), cfg_getstr(sec, "format"), cfg_getstr(sec, "format_down"), cfg_getstr(sec, "status_chr"), cfg_getstr(sec, "status_bat"), cfg_getstr(sec, "status_full"), cfg_getint(sec, "low_threshold"), cfg_getstr(sec, "threshold_type"), cfg_getbool(sec, "last_full_capacity"), cfg_getbool(sec, "integer_battery_capacity"), cfg_getbool(sec, "hide_seconds")); | |
600 | SEC_CLOSE_MAP; | |
601 | } | |
602 | ||
603 | CASE_SEC_TITLE("run_watch") { | |
604 | SEC_OPEN_MAP("run_watch"); | |
605 | print_run_watch(json_gen, buffer, title, cfg_getstr(sec, "pidfile"), cfg_getstr(sec, "format")); | |
606 | SEC_CLOSE_MAP; | |
607 | } | |
608 | ||
609 | CASE_SEC_TITLE("path_exists") { | |
610 | SEC_OPEN_MAP("path_exists"); | |
611 | print_path_exists(json_gen, buffer, title, cfg_getstr(sec, "path"), cfg_getstr(sec, "format")); | |
612 | SEC_CLOSE_MAP; | |
613 | } | |
614 | ||
615 | CASE_SEC_TITLE("disk") { | |
616 | SEC_OPEN_MAP("disk_info"); | |
617 | print_disk_info(json_gen, buffer, title, cfg_getstr(sec, "format"), cfg_getstr(sec, "format_not_mounted"), cfg_getstr(sec, "prefix_type"), cfg_getstr(sec, "threshold_type"), cfg_getfloat(sec, "low_threshold")); | |
618 | SEC_CLOSE_MAP; | |
619 | } | |
620 | ||
621 | CASE_SEC("load") { | |
622 | SEC_OPEN_MAP("load"); | |
623 | print_load(json_gen, buffer, cfg_getstr(sec, "format"), cfg_getfloat(sec, "max_threshold")); | |
624 | SEC_CLOSE_MAP; | |
625 | } | |
626 | ||
627 | CASE_SEC("time") { | |
628 | SEC_OPEN_MAP("time"); | |
629 | print_time(json_gen, buffer, cfg_getstr(sec, "format"), NULL, tv.tv_sec); | |
630 | SEC_CLOSE_MAP; | |
631 | } | |
632 | ||
633 | CASE_SEC_TITLE("tztime") { | |
634 | SEC_OPEN_MAP("tztime"); | |
635 | print_time(json_gen, buffer, cfg_getstr(sec, "format"), cfg_getstr(sec, "timezone"), tv.tv_sec); | |
636 | SEC_CLOSE_MAP; | |
637 | } | |
638 | ||
639 | CASE_SEC("ddate") { | |
640 | SEC_OPEN_MAP("ddate"); | |
641 | print_ddate(json_gen, buffer, cfg_getstr(sec, "format"), tv.tv_sec); | |
642 | SEC_CLOSE_MAP; | |
643 | } | |
644 | ||
645 | CASE_SEC_TITLE("volume") { | |
646 | SEC_OPEN_MAP("volume"); | |
647 | print_volume(json_gen, buffer, cfg_getstr(sec, "format"), | |
648 | cfg_getstr(sec, "format_muted"), | |
649 | cfg_getstr(sec, "device"), | |
650 | cfg_getstr(sec, "mixer"), | |
651 | cfg_getint(sec, "mixer_idx")); | |
652 | SEC_CLOSE_MAP; | |
653 | } | |
654 | ||
655 | CASE_SEC_TITLE("cpu_temperature") { | |
656 | SEC_OPEN_MAP("cpu_temperature"); | |
657 | print_cpu_temperature_info(json_gen, buffer, atoi(title), cfg_getstr(sec, "path"), cfg_getstr(sec, "format"), cfg_getint(sec, "max_threshold")); | |
658 | SEC_CLOSE_MAP; | |
659 | } | |
660 | ||
661 | CASE_SEC("cpu_usage") { | |
662 | SEC_OPEN_MAP("cpu_usage"); | |
663 | print_cpu_usage(json_gen, buffer, cfg_getstr(sec, "format")); | |
664 | SEC_CLOSE_MAP; | |
665 | } | |
390 | 666 | } |
391 | ||
392 | if (strcasecmp(output_str, "dzen2") == 0) | |
393 | output_format = O_DZEN2; | |
394 | else if (strcasecmp(output_str, "xmobar") == 0) | |
395 | output_format = O_XMOBAR; | |
396 | else if (strcasecmp(output_str, "i3bar") == 0) | |
397 | output_format = O_I3BAR; | |
398 | else if (strcasecmp(output_str, "term") == 0) | |
399 | output_format = O_TERM; | |
400 | else if (strcasecmp(output_str, "none") == 0) | |
401 | output_format = O_NONE; | |
402 | else die("Unknown output format: \"%s\"\n", output_str); | |
403 | ||
404 | if (!valid_color(cfg_getstr(cfg_general, "color_good")) | |
405 | || !valid_color(cfg_getstr(cfg_general, "color_degraded")) | |
406 | || !valid_color(cfg_getstr(cfg_general, "color_bad")) | |
407 | || !valid_color(cfg_getstr(cfg_general, "color_separator"))) | |
408 | die("Bad color format"); | |
409 | ||
667 | if (output_format == O_I3BAR) { | |
668 | yajl_gen_array_close(json_gen); | |
669 | const unsigned char *buf; | |
410 | 670 | #if YAJL_MAJOR >= 2 |
411 | yajl_gen json_gen = yajl_gen_alloc(NULL); | |
671 | size_t len; | |
412 | 672 | #else |
413 | yajl_gen json_gen = yajl_gen_alloc(NULL, NULL); | |
673 | unsigned int len; | |
414 | 674 | #endif |
415 | ||
416 | if (output_format == O_I3BAR) { | |
417 | /* Initialize the i3bar protocol. See i3/docs/i3bar-protocol | |
418 | * for details. */ | |
419 | printf("{\"version\":1}\n[\n"); | |
420 | fflush(stdout); | |
421 | yajl_gen_array_open(json_gen); | |
422 | yajl_gen_clear(json_gen); | |
675 | yajl_gen_get_buf(json_gen, &buf, &len); | |
676 | write(STDOUT_FILENO, buf, len); | |
677 | yajl_gen_clear(json_gen); | |
423 | 678 | } |
424 | if (output_format == O_TERM) { | |
425 | /* Save the cursor-position and hide the cursor */ | |
426 | printf("\033[s\033[?25l"); | |
427 | /* Undo at exit */ | |
428 | atexit(&reset_cursor); | |
429 | } | |
430 | ||
431 | if ((general_socket = socket(AF_INET, SOCK_DGRAM, 0)) == -1) | |
432 | die("Could not create socket\n"); | |
433 | ||
434 | int interval = cfg_getint(cfg_general, "interval"); | |
435 | ||
436 | /* One memory page which each plugin can use to buffer output. | |
437 | * Even though it’s unclean, we just assume that the user will not | |
438 | * specify a format string which expands to something longer than 4096 | |
439 | * bytes — given that the output of i3status is used to display | |
440 | * information on screen, more than 1024 characters for the full line | |
441 | * (!), not individual plugins, seem very unlikely. */ | |
442 | char buffer[4096]; | |
443 | ||
444 | while (1) { | |
445 | if (exit_upon_signal) { | |
446 | fprintf(stderr, "Exiting due to signal.\n"); | |
447 | exit(1); | |
448 | } | |
449 | struct timeval tv; | |
450 | gettimeofday(&tv, NULL); | |
451 | if (output_format == O_I3BAR) | |
452 | yajl_gen_array_open(json_gen); | |
453 | else if (output_format == O_TERM) | |
454 | /* Restore the cursor-position, clear line */ | |
455 | printf("\033[u\033[K"); | |
456 | for (j = 0; j < cfg_size(cfg, "order"); j++) { | |
457 | if (j > 0) | |
458 | print_seperator(); | |
459 | ||
460 | const char *current = cfg_getnstr(cfg, "order", j); | |
461 | ||
462 | CASE_SEC("ipv6") { | |
463 | SEC_OPEN_MAP("ipv6"); | |
464 | print_ipv6_info(json_gen, buffer, cfg_getstr(sec, "format_up"), cfg_getstr(sec, "format_down")); | |
465 | SEC_CLOSE_MAP; | |
466 | } | |
467 | ||
468 | CASE_SEC_TITLE("wireless") { | |
469 | SEC_OPEN_MAP("wireless"); | |
470 | print_wireless_info(json_gen, buffer, title, cfg_getstr(sec, "format_up"), cfg_getstr(sec, "format_down")); | |
471 | SEC_CLOSE_MAP; | |
472 | } | |
473 | ||
474 | CASE_SEC_TITLE("ethernet") { | |
475 | SEC_OPEN_MAP("ethernet"); | |
476 | print_eth_info(json_gen, buffer, title, cfg_getstr(sec, "format_up"), cfg_getstr(sec, "format_down")); | |
477 | SEC_CLOSE_MAP; | |
478 | } | |
479 | ||
480 | CASE_SEC_TITLE("battery") { | |
481 | SEC_OPEN_MAP("battery"); | |
482 | print_battery_info(json_gen, buffer, atoi(title), cfg_getstr(sec, "path"), cfg_getstr(sec, "format"), cfg_getstr(sec, "format_down"), cfg_getint(sec, "low_threshold"), cfg_getstr(sec, "threshold_type"), cfg_getbool(sec, "last_full_capacity"), cfg_getbool(sec, "integer_battery_capacity")); | |
483 | SEC_CLOSE_MAP; | |
484 | } | |
485 | ||
486 | CASE_SEC_TITLE("run_watch") { | |
487 | SEC_OPEN_MAP("run_watch"); | |
488 | print_run_watch(json_gen, buffer, title, cfg_getstr(sec, "pidfile"), cfg_getstr(sec, "format")); | |
489 | SEC_CLOSE_MAP; | |
490 | } | |
491 | ||
492 | CASE_SEC_TITLE("path_exists") { | |
493 | SEC_OPEN_MAP("path_exists"); | |
494 | print_path_exists(json_gen, buffer, title, cfg_getstr(sec, "path"), cfg_getstr(sec, "format")); | |
495 | SEC_CLOSE_MAP; | |
496 | } | |
497 | ||
498 | CASE_SEC_TITLE("disk") { | |
499 | SEC_OPEN_MAP("disk_info"); | |
500 | print_disk_info(json_gen, buffer, title, cfg_getstr(sec, "format"), cfg_getstr(sec, "prefix_type")); | |
501 | SEC_CLOSE_MAP; | |
502 | } | |
503 | ||
504 | CASE_SEC("load") { | |
505 | SEC_OPEN_MAP("load"); | |
506 | print_load(json_gen, buffer, cfg_getstr(sec, "format"), cfg_getfloat(sec, "max_threshold")); | |
507 | SEC_CLOSE_MAP; | |
508 | } | |
509 | ||
510 | CASE_SEC("time") { | |
511 | SEC_OPEN_MAP("time"); | |
512 | print_time(json_gen, buffer, cfg_getstr(sec, "format"), NULL, tv.tv_sec); | |
513 | SEC_CLOSE_MAP; | |
514 | } | |
515 | ||
516 | CASE_SEC_TITLE("tztime") { | |
517 | SEC_OPEN_MAP("tztime"); | |
518 | print_time(json_gen, buffer, cfg_getstr(sec, "format"), cfg_getstr(sec, "timezone"), tv.tv_sec); | |
519 | SEC_CLOSE_MAP; | |
520 | } | |
521 | ||
522 | CASE_SEC("ddate") { | |
523 | SEC_OPEN_MAP("ddate"); | |
524 | print_ddate(json_gen, buffer, cfg_getstr(sec, "format"), tv.tv_sec); | |
525 | SEC_CLOSE_MAP; | |
526 | } | |
527 | ||
528 | CASE_SEC_TITLE("volume") { | |
529 | SEC_OPEN_MAP("volume"); | |
530 | print_volume(json_gen, buffer, cfg_getstr(sec, "format"), | |
531 | cfg_getstr(sec, "format_muted"), | |
532 | cfg_getstr(sec, "device"), | |
533 | cfg_getstr(sec, "mixer"), | |
534 | cfg_getint(sec, "mixer_idx")); | |
535 | SEC_CLOSE_MAP; | |
536 | } | |
537 | ||
538 | CASE_SEC_TITLE("cpu_temperature") { | |
539 | SEC_OPEN_MAP("cpu_temperature"); | |
540 | print_cpu_temperature_info(json_gen, buffer, atoi(title), cfg_getstr(sec, "path"), cfg_getstr(sec, "format"), cfg_getint(sec, "max_threshold")); | |
541 | SEC_CLOSE_MAP; | |
542 | } | |
543 | ||
544 | CASE_SEC("cpu_usage") { | |
545 | SEC_OPEN_MAP("cpu_usage"); | |
546 | print_cpu_usage(json_gen, buffer, cfg_getstr(sec, "format")); | |
547 | SEC_CLOSE_MAP; | |
548 | } | |
549 | } | |
550 | if (output_format == O_I3BAR) { | |
551 | yajl_gen_array_close(json_gen); | |
552 | const unsigned char *buf; | |
553 | #if YAJL_MAJOR >= 2 | |
554 | size_t len; | |
555 | #else | |
556 | unsigned int len; | |
557 | #endif | |
558 | yajl_gen_get_buf(json_gen, &buf, &len); | |
559 | write(STDOUT_FILENO, buf, len); | |
560 | yajl_gen_clear(json_gen); | |
561 | } | |
562 | ||
563 | printf("\n"); | |
564 | fflush(stdout); | |
565 | ||
566 | /* To provide updates on every full second (as good as possible) | |
567 | * we don’t use sleep(interval) but we sleep until the next | |
568 | * second (with microsecond precision) plus (interval-1) | |
569 | * seconds. We also align to 60 seconds modulo interval such | |
570 | * that we start with :00 on every new minute. */ | |
571 | struct timeval current_timeval; | |
572 | gettimeofday(¤t_timeval, NULL); | |
573 | struct timespec ts = {interval - 1 - (current_timeval.tv_sec % interval), (10e5 - current_timeval.tv_usec) * 1000}; | |
574 | nanosleep(&ts, NULL); | |
575 | } | |
576 | } | |
679 | ||
680 | printf("\n"); | |
681 | fflush(stdout); | |
682 | ||
683 | /* To provide updates on every full second (as good as possible) | |
684 | * we don’t use sleep(interval) but we sleep until the next | |
685 | * second (with microsecond precision) plus (interval-1) | |
686 | * seconds. We also align to 60 seconds modulo interval such | |
687 | * that we start with :00 on every new minute. */ | |
688 | struct timeval current_timeval; | |
689 | gettimeofday(¤t_timeval, NULL); | |
690 | struct timespec ts = {interval - 1 - (current_timeval.tv_sec % interval), (10e5 - current_timeval.tv_usec) * 1000}; | |
691 | nanosleep(&ts, NULL); | |
692 | } | |
693 | } |
14 | 14 | order += "disk /" |
15 | 15 | order += "run_watch DHCP" |
16 | 16 | order += "run_watch VPN" |
17 | order += "wireless wlan0" | |
18 | order += "ethernet eth0" | |
17 | order += "wireless _first_" | |
18 | order += "ethernet _first_" | |
19 | 19 | order += "battery 0" |
20 | 20 | order += "load" |
21 | 21 | order += "tztime local" |
22 | 22 | |
23 | wireless wlan0 { | |
23 | wireless _first_ { | |
24 | 24 | format_up = "W: (%quality at %essid) %ip" |
25 | 25 | format_down = "W: down" |
26 | 26 | } |
27 | 27 | |
28 | ethernet eth0 { | |
28 | ethernet _first_ { | |
29 | 29 | # if you use %speed, i3status requires root privileges |
30 | 30 | format_up = "E: %ip (%speed)" |
31 | 31 | format_down = "E: down" |
0 | 0 | #ifndef _I3STATUS_H |
1 | 1 | #define _I3STATUS_H |
2 | 2 | |
3 | enum { O_DZEN2, O_XMOBAR, O_I3BAR, O_TERM, O_NONE } output_format; | |
3 | enum { O_DZEN2, | |
4 | O_XMOBAR, | |
5 | O_I3BAR, | |
6 | O_TERM, | |
7 | O_NONE } output_format; | |
4 | 8 | |
5 | 9 | #include <stdbool.h> |
6 | 10 | #include <confuse.h> |
29 | 33 | #define BATT_TIME "hw.acpi.battery.time" |
30 | 34 | #define BATT_STATE "hw.acpi.battery.state" |
31 | 35 | |
32 | #elif defined(__OpenBSD__) | |
36 | #elif defined(__OpenBSD__) || defined(__NetBSD__) | |
33 | 37 | /* Default to acpitz(4) if no path is set. */ |
34 | 38 | #define THERMAL_ZONE "acpitz%d" |
35 | #elif defined(__NetBSD__) | |
36 | /* Rely on envsys(4). The key of the sensor is generally cpu%d temperature */ | |
37 | #define THERMAL_ZONE "cpu%d temperature" | |
38 | 39 | #endif |
39 | 40 | |
40 | 41 | #if defined(__FreeBSD_kernel__) && defined(__GLIBC__) |
47 | 48 | /* Allows for the definition of a variable without opening a new scope, thus |
48 | 49 | * suited for usage in a macro. Idea from wmii. */ |
49 | 50 | #define with(type, var, init) \ |
50 | for (type var = (type)-1; (var == (type)-1) && ((var=(init)) || 1); ) | |
51 | ||
52 | #define CASE_SEC(name) \ | |
53 | if (BEGINS_WITH(current, name)) \ | |
54 | with(cfg_t *, sec, cfg_section = cfg_getsec(cfg, name)) \ | |
55 | if (sec != NULL) | |
56 | ||
57 | #define CASE_SEC_TITLE(name) \ | |
58 | if (BEGINS_WITH(current, name)) \ | |
59 | with(const char *, title, current + strlen(name) + 1) \ | |
60 | with(cfg_t *, sec, cfg_section = cfg_gettsec(cfg, name, title)) \ | |
61 | if (sec != NULL) | |
51 | for (type var = (type)-1; (var == (type)-1) && ((var = (init)) || 1);) | |
52 | ||
53 | #define CASE_SEC(name) \ | |
54 | if (BEGINS_WITH(current, name)) \ | |
55 | with(cfg_t *, sec, cfg_section = cfg_getsec(cfg, name)) if (sec != NULL) | |
56 | ||
57 | #define CASE_SEC_TITLE(name) \ | |
58 | if (BEGINS_WITH(current, name)) \ | |
59 | with(const char *, title, current + strlen(name) + 1) \ | |
60 | with(cfg_t *, sec, cfg_section = cfg_gettsec(cfg, name, title)) if (sec != NULL) | |
62 | 61 | |
63 | 62 | /* Macro which any plugin can use to output the full_text part (when the output |
64 | 63 | * format is JSON) or just output to stdout (any other output format). */ |
65 | #define OUTPUT_FULL_TEXT(text) \ | |
66 | do { \ | |
67 | /* Terminate the output buffer here in any case, so that it’s \ | |
68 | * not forgotten in the module */ \ | |
69 | *outwalk = '\0'; \ | |
70 | if (output_format == O_I3BAR) { \ | |
71 | yajl_gen_string(json_gen, (const unsigned char *)"full_text", strlen("full_text")); \ | |
72 | yajl_gen_string(json_gen, (const unsigned char *)text, strlen(text)); \ | |
73 | } else { \ | |
74 | printf("%s", text); \ | |
75 | } \ | |
76 | } while (0) | |
77 | ||
78 | #define SEC_OPEN_MAP(name) \ | |
79 | do { \ | |
80 | if (output_format == O_I3BAR) { \ | |
81 | yajl_gen_map_open(json_gen); \ | |
82 | yajl_gen_string(json_gen, (const unsigned char *)"name", strlen("name")); \ | |
83 | yajl_gen_string(json_gen, (const unsigned char *)name, strlen(name)); \ | |
84 | } \ | |
85 | } while (0) | |
86 | ||
87 | #define SEC_CLOSE_MAP \ | |
88 | do { \ | |
89 | if (output_format == O_I3BAR) { \ | |
90 | yajl_gen_map_close(json_gen); \ | |
91 | } \ | |
92 | } while (0) | |
93 | ||
94 | #define START_COLOR(colorstr) \ | |
95 | do { \ | |
96 | if (cfg_getbool(cfg_general, "colors")) { \ | |
97 | const char *_val = NULL; \ | |
98 | if (cfg_section) \ | |
99 | _val = cfg_getstr(cfg_section, colorstr); \ | |
100 | if (!_val) \ | |
101 | _val = cfg_getstr(cfg_general, colorstr); \ | |
102 | if (output_format == O_I3BAR) { \ | |
103 | yajl_gen_string(json_gen, (const unsigned char *)"color", strlen("color")); \ | |
104 | yajl_gen_string(json_gen, (const unsigned char *)_val, strlen(_val)); \ | |
105 | } else { \ | |
106 | outwalk += sprintf(outwalk, "%s", color(colorstr)); \ | |
107 | } \ | |
108 | } \ | |
109 | } while (0) | |
110 | ||
111 | #define END_COLOR \ | |
112 | do { \ | |
113 | if (cfg_getbool(cfg_general, "colors") && output_format != O_I3BAR) { \ | |
114 | outwalk += sprintf(outwalk, "%s", endcolor()); \ | |
115 | } \ | |
116 | } while (0) | |
117 | ||
118 | #define INSTANCE(instance) \ | |
119 | do { \ | |
120 | if (output_format == O_I3BAR) { \ | |
121 | yajl_gen_string(json_gen, (const unsigned char *)"instance", strlen("instance")); \ | |
122 | yajl_gen_string(json_gen, (const unsigned char *)instance, strlen(instance)); \ | |
123 | } \ | |
124 | } while (0) | |
125 | ||
126 | ||
127 | typedef enum { CS_DISCHARGING, CS_CHARGING, CS_FULL } charging_status_t; | |
64 | #define OUTPUT_FULL_TEXT(text) \ | |
65 | do { \ | |
66 | /* Terminate the output buffer here in any case, so that it’s \ | |
67 | * not forgotten in the module */ \ | |
68 | *outwalk = '\0'; \ | |
69 | if (output_format == O_I3BAR) { \ | |
70 | yajl_gen_string(json_gen, (const unsigned char *) "full_text", strlen("full_text")); \ | |
71 | yajl_gen_string(json_gen, (const unsigned char *)text, strlen(text)); \ | |
72 | } else { \ | |
73 | printf("%s", text); \ | |
74 | } \ | |
75 | } while (0) | |
76 | ||
77 | #define SEC_OPEN_MAP(name) \ | |
78 | do { \ | |
79 | if (output_format == O_I3BAR) { \ | |
80 | yajl_gen_map_open(json_gen); \ | |
81 | yajl_gen_string(json_gen, (const unsigned char *) "name", strlen("name")); \ | |
82 | yajl_gen_string(json_gen, (const unsigned char *)name, strlen(name)); \ | |
83 | } \ | |
84 | } while (0) | |
85 | ||
86 | #define SEC_CLOSE_MAP \ | |
87 | do { \ | |
88 | if (output_format == O_I3BAR) { \ | |
89 | char *_align = cfg_getstr(sec, "align"); \ | |
90 | if (_align) { \ | |
91 | yajl_gen_string(json_gen, (const unsigned char *) "align", strlen("align")); \ | |
92 | yajl_gen_string(json_gen, (const unsigned char *)_align, strlen(_align)); \ | |
93 | } \ | |
94 | struct min_width *_width = cfg_getptr(sec, "min_width"); \ | |
95 | if (_width) { \ | |
96 | /* if the value can be parsed as a number, we use the numerical value */ \ | |
97 | if (_width->num > 0) { \ | |
98 | yajl_gen_string(json_gen, (const unsigned char *) "min_width", strlen("min_width")); \ | |
99 | yajl_gen_integer(json_gen, _width->num); \ | |
100 | } else { \ | |
101 | yajl_gen_string(json_gen, (const unsigned char *) "min_width", strlen("min_width")); \ | |
102 | yajl_gen_string(json_gen, (const unsigned char *)_width->str, strlen(_width->str)); \ | |
103 | } \ | |
104 | } \ | |
105 | const char *_sep = cfg_getstr(cfg_general, "separator"); \ | |
106 | if (strlen(_sep) == 0) { \ | |
107 | yajl_gen_string(json_gen, (const unsigned char *) "separator", strlen("separator")); \ | |
108 | yajl_gen_bool(json_gen, false); \ | |
109 | } \ | |
110 | yajl_gen_map_close(json_gen); \ | |
111 | } \ | |
112 | } while (0) | |
113 | ||
114 | #define START_COLOR(colorstr) \ | |
115 | do { \ | |
116 | if (cfg_getbool(cfg_general, "colors")) { \ | |
117 | const char *_val = NULL; \ | |
118 | if (cfg_section) \ | |
119 | _val = cfg_getstr(cfg_section, colorstr); \ | |
120 | if (!_val) \ | |
121 | _val = cfg_getstr(cfg_general, colorstr); \ | |
122 | if (output_format == O_I3BAR) { \ | |
123 | yajl_gen_string(json_gen, (const unsigned char *) "color", strlen("color")); \ | |
124 | yajl_gen_string(json_gen, (const unsigned char *)_val, strlen(_val)); \ | |
125 | } else { \ | |
126 | outwalk += sprintf(outwalk, "%s", color(colorstr)); \ | |
127 | } \ | |
128 | } \ | |
129 | } while (0) | |
130 | ||
131 | #define END_COLOR \ | |
132 | do { \ | |
133 | if (cfg_getbool(cfg_general, "colors") && output_format != O_I3BAR) { \ | |
134 | outwalk += sprintf(outwalk, "%s", endcolor()); \ | |
135 | } \ | |
136 | } while (0) | |
137 | ||
138 | #define INSTANCE(instance) \ | |
139 | do { \ | |
140 | if (output_format == O_I3BAR) { \ | |
141 | yajl_gen_string(json_gen, (const unsigned char *) "instance", strlen("instance")); \ | |
142 | yajl_gen_string(json_gen, (const unsigned char *)instance, strlen(instance)); \ | |
143 | } \ | |
144 | } while (0) | |
145 | ||
146 | typedef enum { CS_DISCHARGING, | |
147 | CS_CHARGING, | |
148 | CS_FULL } charging_status_t; | |
149 | ||
150 | /* | |
151 | * The "min_width" module option may either be defined as a string or a number. | |
152 | */ | |
153 | struct min_width { | |
154 | long num; | |
155 | const char *str; | |
156 | }; | |
128 | 157 | |
129 | 158 | /* src/general.c */ |
130 | 159 | char *skip_character(char *input, char character, int amount); |
132 | 161 | bool slurp(const char *filename, char *destination, int size); |
133 | 162 | |
134 | 163 | /* src/output.c */ |
135 | void print_seperator(); | |
164 | void print_separator(const char *separator); | |
136 | 165 | char *color(const char *colorstr); |
137 | char *endcolor() __attribute__ ((pure)); | |
166 | char *endcolor() __attribute__((pure)); | |
138 | 167 | void reset_cursor(void); |
139 | 168 | |
140 | 169 | /* src/auto_detect_format.c */ |
143 | 172 | /* src/print_time.c */ |
144 | 173 | void set_timezone(const char *tz); |
145 | 174 | |
175 | /* src/first_network_device.c */ | |
176 | typedef enum { | |
177 | NET_TYPE_WIRELESS = 0, | |
178 | NET_TYPE_ETHERNET = 1 | |
179 | } net_type_t; | |
180 | const char *first_eth_interface(const net_type_t type); | |
181 | ||
146 | 182 | void print_ipv6_info(yajl_gen json_gen, char *buffer, const char *format_up, const char *format_down); |
147 | void print_disk_info(yajl_gen json_gen, char *buffer, const char *path, const char *format, const char *prefix_type); | |
148 | void print_battery_info(yajl_gen json_gen, char *buffer, int number, const char *path, const char *format, const char *format_down, int low_threshold, char *threshold_type, bool last_full_capacity, bool integer_battery_capacity); | |
183 | void print_disk_info(yajl_gen json_gen, char *buffer, const char *path, const char *format, const char *format_not_mounted, const char *prefix_type, const char *threshold_type, const double low_threshold); | |
184 | void print_battery_info(yajl_gen json_gen, char *buffer, int number, const char *path, const char *format, const char *format_down, const char *status_chr, const char *status_bat, const char *status_full, int low_threshold, char *threshold_type, bool last_full_capacity, bool integer_battery_capacity, bool hide_seconds); | |
149 | 185 | void print_time(yajl_gen json_gen, char *buffer, const char *format, const char *tz, time_t t); |
150 | 186 | void print_ddate(yajl_gen json_gen, char *buffer, const char *format, time_t t); |
151 | 187 | const char *get_ip_addr(); |
164 | 200 | |
165 | 201 | extern cfg_t *cfg, *cfg_general, *cfg_section; |
166 | 202 | |
167 | #endif | |
203 | extern void **cur_instance; | |
204 | ||
205 | #endif |
31 | 31 | * @(#)queue.h 8.5 (Berkeley) 8/20/94 |
32 | 32 | */ |
33 | 33 | |
34 | #ifndef _SYS_QUEUE_H_ | |
35 | #define _SYS_QUEUE_H_ | |
34 | #ifndef _SYS_QUEUE_H_ | |
35 | #define _SYS_QUEUE_H_ | |
36 | 36 | |
37 | 37 | /* |
38 | 38 | * This file defines five types of data structures: singly-linked lists, |
90 | 90 | /* |
91 | 91 | * Singly-linked List definitions. |
92 | 92 | */ |
93 | #define SLIST_HEAD(name, type) \ | |
94 | struct name { \ | |
95 | struct type *slh_first; /* first element */ \ | |
96 | } | |
97 | ||
98 | #define SLIST_HEAD_INITIALIZER(head) \ | |
99 | { NULL } | |
100 | ||
101 | #define SLIST_ENTRY(type) \ | |
102 | struct { \ | |
103 | struct type *sle_next; /* next element */ \ | |
104 | } | |
93 | #define SLIST_HEAD(name, type) \ | |
94 | struct name { \ | |
95 | struct type *slh_first; /* first element */ \ | |
96 | } | |
97 | ||
98 | #define SLIST_HEAD_INITIALIZER(head) \ | |
99 | { NULL } | |
100 | ||
101 | #define SLIST_ENTRY(type) \ | |
102 | struct { \ | |
103 | struct type *sle_next; /* next element */ \ | |
104 | } | |
105 | 105 | |
106 | 106 | /* |
107 | 107 | * Singly-linked List access methods. |
108 | 108 | */ |
109 | #define SLIST_FIRST(head) ((head)->slh_first) | |
110 | #define SLIST_END(head) NULL | |
111 | #define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) | |
112 | #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) | |
113 | ||
114 | #define SLIST_FOREACH(var, head, field) \ | |
115 | for((var) = SLIST_FIRST(head); \ | |
116 | (var) != SLIST_END(head); \ | |
117 | (var) = SLIST_NEXT(var, field)) | |
118 | ||
119 | #define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ | |
120 | for ((varp) = &SLIST_FIRST((head)); \ | |
121 | ((var) = *(varp)) != SLIST_END(head); \ | |
122 | (varp) = &SLIST_NEXT((var), field)) | |
109 | #define SLIST_FIRST(head) ((head)->slh_first) | |
110 | #define SLIST_END(head) NULL | |
111 | #define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) | |
112 | #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) | |
113 | ||
114 | #define SLIST_FOREACH(var, head, field) \ | |
115 | for ((var) = SLIST_FIRST(head); \ | |
116 | (var) != SLIST_END(head); \ | |
117 | (var) = SLIST_NEXT(var, field)) | |
118 | ||
119 | #define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ | |
120 | for ((varp) = &SLIST_FIRST((head)); \ | |
121 | ((var) = *(varp)) != SLIST_END(head); \ | |
122 | (varp) = &SLIST_NEXT((var), field)) | |
123 | 123 | |
124 | 124 | /* |
125 | 125 | * Singly-linked List functions. |
126 | 126 | */ |
127 | #define SLIST_INIT(head) { \ | |
128 | SLIST_FIRST(head) = SLIST_END(head); \ | |
129 | } | |
130 | ||
131 | #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ | |
132 | (elm)->field.sle_next = (slistelm)->field.sle_next; \ | |
133 | (slistelm)->field.sle_next = (elm); \ | |
134 | } while (0) | |
135 | ||
136 | #define SLIST_INSERT_HEAD(head, elm, field) do { \ | |
137 | (elm)->field.sle_next = (head)->slh_first; \ | |
138 | (head)->slh_first = (elm); \ | |
139 | } while (0) | |
140 | ||
141 | #define SLIST_REMOVE_NEXT(head, elm, field) do { \ | |
142 | (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ | |
143 | } while (0) | |
144 | ||
145 | #define SLIST_REMOVE_HEAD(head, field) do { \ | |
146 | (head)->slh_first = (head)->slh_first->field.sle_next; \ | |
147 | } while (0) | |
148 | ||
149 | #define SLIST_REMOVE(head, elm, type, field) do { \ | |
150 | if ((head)->slh_first == (elm)) { \ | |
151 | SLIST_REMOVE_HEAD((head), field); \ | |
152 | } else { \ | |
153 | struct type *curelm = (head)->slh_first; \ | |
154 | \ | |
155 | while (curelm->field.sle_next != (elm)) \ | |
156 | curelm = curelm->field.sle_next; \ | |
157 | curelm->field.sle_next = \ | |
158 | curelm->field.sle_next->field.sle_next; \ | |
159 | _Q_INVALIDATE((elm)->field.sle_next); \ | |
160 | } \ | |
161 | } while (0) | |
127 | #define SLIST_INIT(head) \ | |
128 | { \ | |
129 | SLIST_FIRST(head) = SLIST_END(head); \ | |
130 | } | |
131 | ||
132 | #define SLIST_INSERT_AFTER(slistelm, elm, field) \ | |
133 | do { \ | |
134 | (elm)->field.sle_next = (slistelm)->field.sle_next; \ | |
135 | (slistelm)->field.sle_next = (elm); \ | |
136 | } while (0) | |
137 | ||
138 | #define SLIST_INSERT_HEAD(head, elm, field) \ | |
139 | do { \ | |
140 | (elm)->field.sle_next = (head)->slh_first; \ | |
141 | (head)->slh_first = (elm); \ | |
142 | } while (0) | |
143 | ||
144 | #define SLIST_REMOVE_NEXT(head, elm, field) \ | |
145 | do { \ | |
146 | (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ | |
147 | } while (0) | |
148 | ||
149 | #define SLIST_REMOVE_HEAD(head, field) \ | |
150 | do { \ | |
151 | (head)->slh_first = (head)->slh_first->field.sle_next; \ | |
152 | } while (0) | |
153 | ||
154 | #define SLIST_REMOVE(head, elm, type, field) \ | |
155 | do { \ | |
156 | if ((head)->slh_first == (elm)) { \ | |
157 | SLIST_REMOVE_HEAD((head), field); \ | |
158 | } else { \ | |
159 | struct type *curelm = (head)->slh_first; \ | |
160 | \ | |
161 | while (curelm->field.sle_next != (elm)) \ | |
162 | curelm = curelm->field.sle_next; \ | |
163 | curelm->field.sle_next = curelm->field.sle_next->field.sle_next; \ | |
164 | _Q_INVALIDATE((elm)->field.sle_next); \ | |
165 | } \ | |
166 | } while (0) | |
162 | 167 | |
163 | 168 | /* |
164 | 169 | * List definitions. |
165 | 170 | */ |
166 | #define LIST_HEAD(name, type) \ | |
167 | struct name { \ | |
168 | struct type *lh_first; /* first element */ \ | |
169 | } | |
170 | ||
171 | #define LIST_HEAD_INITIALIZER(head) \ | |
172 | { NULL } | |
173 | ||
174 | #define LIST_ENTRY(type) \ | |
175 | struct { \ | |
176 | struct type *le_next; /* next element */ \ | |
177 | struct type **le_prev; /* address of previous next element */ \ | |
178 | } | |
171 | #define LIST_HEAD(name, type) \ | |
172 | struct name { \ | |
173 | struct type *lh_first; /* first element */ \ | |
174 | } | |
175 | ||
176 | #define LIST_HEAD_INITIALIZER(head) \ | |
177 | { NULL } | |
178 | ||
179 | #define LIST_ENTRY(type) \ | |
180 | struct { \ | |
181 | struct type *le_next; /* next element */ \ | |
182 | struct type **le_prev; /* address of previous next element */ \ | |
183 | } | |
179 | 184 | |
180 | 185 | /* |
181 | 186 | * List access methods |
182 | 187 | */ |
183 | #define LIST_FIRST(head) ((head)->lh_first) | |
184 | #define LIST_END(head) NULL | |
185 | #define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) | |
186 | #define LIST_NEXT(elm, field) ((elm)->field.le_next) | |
187 | ||
188 | #define LIST_FOREACH(var, head, field) \ | |
189 | for((var) = LIST_FIRST(head); \ | |
190 | (var)!= LIST_END(head); \ | |
191 | (var) = LIST_NEXT(var, field)) | |
188 | #define LIST_FIRST(head) ((head)->lh_first) | |
189 | #define LIST_END(head) NULL | |
190 | #define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) | |
191 | #define LIST_NEXT(elm, field) ((elm)->field.le_next) | |
192 | ||
193 | #define LIST_FOREACH(var, head, field) \ | |
194 | for ((var) = LIST_FIRST(head); \ | |
195 | (var) != LIST_END(head); \ | |
196 | (var) = LIST_NEXT(var, field)) | |
192 | 197 | |
193 | 198 | /* |
194 | 199 | * List functions. |
195 | 200 | */ |
196 | #define LIST_INIT(head) do { \ | |
197 | LIST_FIRST(head) = LIST_END(head); \ | |
198 | } while (0) | |
199 | ||
200 | #define LIST_INSERT_AFTER(listelm, elm, field) do { \ | |
201 | if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ | |
202 | (listelm)->field.le_next->field.le_prev = \ | |
203 | &(elm)->field.le_next; \ | |
204 | (listelm)->field.le_next = (elm); \ | |
205 | (elm)->field.le_prev = &(listelm)->field.le_next; \ | |
206 | } while (0) | |
207 | ||
208 | #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ | |
209 | (elm)->field.le_prev = (listelm)->field.le_prev; \ | |
210 | (elm)->field.le_next = (listelm); \ | |
211 | *(listelm)->field.le_prev = (elm); \ | |
212 | (listelm)->field.le_prev = &(elm)->field.le_next; \ | |
213 | } while (0) | |
214 | ||
215 | #define LIST_INSERT_HEAD(head, elm, field) do { \ | |
216 | if (((elm)->field.le_next = (head)->lh_first) != NULL) \ | |
217 | (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ | |
218 | (head)->lh_first = (elm); \ | |
219 | (elm)->field.le_prev = &(head)->lh_first; \ | |
220 | } while (0) | |
221 | ||
222 | #define LIST_REMOVE(elm, field) do { \ | |
223 | if ((elm)->field.le_next != NULL) \ | |
224 | (elm)->field.le_next->field.le_prev = \ | |
225 | (elm)->field.le_prev; \ | |
226 | *(elm)->field.le_prev = (elm)->field.le_next; \ | |
227 | _Q_INVALIDATE((elm)->field.le_prev); \ | |
228 | _Q_INVALIDATE((elm)->field.le_next); \ | |
229 | } while (0) | |
230 | ||
231 | #define LIST_REPLACE(elm, elm2, field) do { \ | |
232 | if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ | |
233 | (elm2)->field.le_next->field.le_prev = \ | |
234 | &(elm2)->field.le_next; \ | |
235 | (elm2)->field.le_prev = (elm)->field.le_prev; \ | |
236 | *(elm2)->field.le_prev = (elm2); \ | |
237 | _Q_INVALIDATE((elm)->field.le_prev); \ | |
238 | _Q_INVALIDATE((elm)->field.le_next); \ | |
239 | } while (0) | |
201 | #define LIST_INIT(head) \ | |
202 | do { \ | |
203 | LIST_FIRST(head) = LIST_END(head); \ | |
204 | } while (0) | |
205 | ||
206 | #define LIST_INSERT_AFTER(listelm, elm, field) \ | |
207 | do { \ | |
208 | if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ | |
209 | (listelm)->field.le_next->field.le_prev = &(elm)->field.le_next; \ | |
210 | (listelm)->field.le_next = (elm); \ | |
211 | (elm)->field.le_prev = &(listelm)->field.le_next; \ | |
212 | } while (0) | |
213 | ||
214 | #define LIST_INSERT_BEFORE(listelm, elm, field) \ | |
215 | do { \ | |
216 | (elm)->field.le_prev = (listelm)->field.le_prev; \ | |
217 | (elm)->field.le_next = (listelm); \ | |
218 | *(listelm)->field.le_prev = (elm); \ | |
219 | (listelm)->field.le_prev = &(elm)->field.le_next; \ | |
220 | } while (0) | |
221 | ||
222 | #define LIST_INSERT_HEAD(head, elm, field) \ | |
223 | do { \ | |
224 | if (((elm)->field.le_next = (head)->lh_first) != NULL) \ | |
225 | (head)->lh_first->field.le_prev = &(elm)->field.le_next; \ | |
226 | (head)->lh_first = (elm); \ | |
227 | (elm)->field.le_prev = &(head)->lh_first; \ | |
228 | } while (0) | |
229 | ||
230 | #define LIST_REMOVE(elm, field) \ | |
231 | do { \ | |
232 | if ((elm)->field.le_next != NULL) \ | |
233 | (elm)->field.le_next->field.le_prev = (elm)->field.le_prev; \ | |
234 | *(elm)->field.le_prev = (elm)->field.le_next; \ | |
235 | _Q_INVALIDATE((elm)->field.le_prev); \ | |
236 | _Q_INVALIDATE((elm)->field.le_next); \ | |
237 | } while (0) | |
238 | ||
239 | #define LIST_REPLACE(elm, elm2, field) \ | |
240 | do { \ | |
241 | if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ | |
242 | (elm2)->field.le_next->field.le_prev = &(elm2)->field.le_next; \ | |
243 | (elm2)->field.le_prev = (elm)->field.le_prev; \ | |
244 | *(elm2)->field.le_prev = (elm2); \ | |
245 | _Q_INVALIDATE((elm)->field.le_prev); \ | |
246 | _Q_INVALIDATE((elm)->field.le_next); \ | |
247 | } while (0) | |
240 | 248 | |
241 | 249 | /* |
242 | 250 | * Simple queue definitions. |
243 | 251 | */ |
244 | #define SIMPLEQ_HEAD(name, type) \ | |
245 | struct name { \ | |
246 | struct type *sqh_first; /* first element */ \ | |
247 | struct type **sqh_last; /* addr of last next element */ \ | |
248 | } | |
249 | ||
250 | #define SIMPLEQ_HEAD_INITIALIZER(head) \ | |
251 | { NULL, &(head).sqh_first } | |
252 | ||
253 | #define SIMPLEQ_ENTRY(type) \ | |
254 | struct { \ | |
255 | struct type *sqe_next; /* next element */ \ | |
256 | } | |
252 | #define SIMPLEQ_HEAD(name, type) \ | |
253 | struct name { \ | |
254 | struct type *sqh_first; /* first element */ \ | |
255 | struct type **sqh_last; /* addr of last next element */ \ | |
256 | } | |
257 | ||
258 | #define SIMPLEQ_HEAD_INITIALIZER(head) \ | |
259 | { NULL, &(head).sqh_first } | |
260 | ||
261 | #define SIMPLEQ_ENTRY(type) \ | |
262 | struct { \ | |
263 | struct type *sqe_next; /* next element */ \ | |
264 | } | |
257 | 265 | |
258 | 266 | /* |
259 | 267 | * Simple queue access methods. |
260 | 268 | */ |
261 | #define SIMPLEQ_FIRST(head) ((head)->sqh_first) | |
262 | #define SIMPLEQ_END(head) NULL | |
263 | #define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) | |
264 | #define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) | |
265 | ||
266 | #define SIMPLEQ_FOREACH(var, head, field) \ | |
267 | for((var) = SIMPLEQ_FIRST(head); \ | |
268 | (var) != SIMPLEQ_END(head); \ | |
269 | (var) = SIMPLEQ_NEXT(var, field)) | |
269 | #define SIMPLEQ_FIRST(head) ((head)->sqh_first) | |
270 | #define SIMPLEQ_END(head) NULL | |
271 | #define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) | |
272 | #define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) | |
273 | ||
274 | #define SIMPLEQ_FOREACH(var, head, field) \ | |
275 | for ((var) = SIMPLEQ_FIRST(head); \ | |
276 | (var) != SIMPLEQ_END(head); \ | |
277 | (var) = SIMPLEQ_NEXT(var, field)) | |
270 | 278 | |
271 | 279 | /* |
272 | 280 | * Simple queue functions. |
273 | 281 | */ |
274 | #define SIMPLEQ_INIT(head) do { \ | |
275 | (head)->sqh_first = NULL; \ | |
276 | (head)->sqh_last = &(head)->sqh_first; \ | |
277 | } while (0) | |
278 | ||
279 | #define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ | |
280 | if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ | |
281 | (head)->sqh_last = &(elm)->field.sqe_next; \ | |
282 | (head)->sqh_first = (elm); \ | |
283 | } while (0) | |
284 | ||
285 | #define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ | |
286 | (elm)->field.sqe_next = NULL; \ | |
287 | *(head)->sqh_last = (elm); \ | |
288 | (head)->sqh_last = &(elm)->field.sqe_next; \ | |
289 | } while (0) | |
290 | ||
291 | #define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ | |
292 | if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ | |
293 | (head)->sqh_last = &(elm)->field.sqe_next; \ | |
294 | (listelm)->field.sqe_next = (elm); \ | |
295 | } while (0) | |
296 | ||
297 | #define SIMPLEQ_REMOVE_HEAD(head, field) do { \ | |
298 | if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ | |
299 | (head)->sqh_last = &(head)->sqh_first; \ | |
300 | } while (0) | |
282 | #define SIMPLEQ_INIT(head) \ | |
283 | do { \ | |
284 | (head)->sqh_first = NULL; \ | |
285 | (head)->sqh_last = &(head)->sqh_first; \ | |
286 | } while (0) | |
287 | ||
288 | #define SIMPLEQ_INSERT_HEAD(head, elm, field) \ | |
289 | do { \ | |
290 | if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ | |
291 | (head)->sqh_last = &(elm)->field.sqe_next; \ | |
292 | (head)->sqh_first = (elm); \ | |
293 | } while (0) | |
294 | ||
295 | #define SIMPLEQ_INSERT_TAIL(head, elm, field) \ | |
296 | do { \ | |
297 | (elm)->field.sqe_next = NULL; \ | |
298 | *(head)->sqh_last = (elm); \ | |
299 | (head)->sqh_last = &(elm)->field.sqe_next; \ | |
300 | } while (0) | |
301 | ||
302 | #define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) \ | |
303 | do { \ | |
304 | if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL) \ | |
305 | (head)->sqh_last = &(elm)->field.sqe_next; \ | |
306 | (listelm)->field.sqe_next = (elm); \ | |
307 | } while (0) | |
308 | ||
309 | #define SIMPLEQ_REMOVE_HEAD(head, field) \ | |
310 | do { \ | |
311 | if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ | |
312 | (head)->sqh_last = &(head)->sqh_first; \ | |
313 | } while (0) | |
301 | 314 | |
302 | 315 | /* |
303 | 316 | * Tail queue definitions. |
304 | 317 | */ |
305 | #define TAILQ_HEAD(name, type) \ | |
306 | struct name { \ | |
307 | struct type *tqh_first; /* first element */ \ | |
308 | struct type **tqh_last; /* addr of last next element */ \ | |
309 | } | |
310 | ||
311 | #define TAILQ_HEAD_INITIALIZER(head) \ | |
312 | { NULL, &(head).tqh_first } | |
313 | ||
314 | #define TAILQ_ENTRY(type) \ | |
315 | struct { \ | |
316 | struct type *tqe_next; /* next element */ \ | |
317 | struct type **tqe_prev; /* address of previous next element */ \ | |
318 | } | |
318 | #define TAILQ_HEAD(name, type) \ | |
319 | struct name { \ | |
320 | struct type *tqh_first; /* first element */ \ | |
321 | struct type **tqh_last; /* addr of last next element */ \ | |
322 | } | |
323 | ||
324 | #define TAILQ_HEAD_INITIALIZER(head) \ | |
325 | { NULL, &(head).tqh_first } | |
326 | ||
327 | #define TAILQ_ENTRY(type) \ | |
328 | struct { \ | |
329 | struct type *tqe_next; /* next element */ \ | |
330 | struct type **tqe_prev; /* address of previous next element */ \ | |
331 | } | |
319 | 332 | |
320 | 333 | /* |
321 | 334 | * tail queue access methods |
322 | 335 | */ |
323 | #define TAILQ_FIRST(head) ((head)->tqh_first) | |
324 | #define TAILQ_END(head) NULL | |
325 | #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) | |
326 | #define TAILQ_LAST(head, headname) \ | |
327 | (*(((struct headname *)((head)->tqh_last))->tqh_last)) | |
336 | #define TAILQ_FIRST(head) ((head)->tqh_first) | |
337 | #define TAILQ_END(head) NULL | |
338 | #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) | |
339 | #define TAILQ_LAST(head, headname) \ | |
340 | (*(((struct headname *)((head)->tqh_last))->tqh_last)) | |
328 | 341 | /* XXX */ |
329 | #define TAILQ_PREV(elm, headname, field) \ | |
330 | (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) | |
331 | #define TAILQ_EMPTY(head) \ | |
332 | (TAILQ_FIRST(head) == TAILQ_END(head)) | |
333 | ||
334 | #define TAILQ_FOREACH(var, head, field) \ | |
335 | for((var) = TAILQ_FIRST(head); \ | |
336 | (var) != TAILQ_END(head); \ | |
337 | (var) = TAILQ_NEXT(var, field)) | |
338 | ||
339 | #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ | |
340 | for((var) = TAILQ_LAST(head, headname); \ | |
341 | (var) != TAILQ_END(head); \ | |
342 | (var) = TAILQ_PREV(var, headname, field)) | |
342 | #define TAILQ_PREV(elm, headname, field) \ | |
343 | (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) | |
344 | #define TAILQ_EMPTY(head) \ | |
345 | (TAILQ_FIRST(head) == TAILQ_END(head)) | |
346 | ||
347 | #define TAILQ_FOREACH(var, head, field) \ | |
348 | for ((var) = TAILQ_FIRST(head); \ | |
349 | (var) != TAILQ_END(head); \ | |
350 | (var) = TAILQ_NEXT(var, field)) | |
351 | ||
352 | #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ | |
353 | for ((var) = TAILQ_LAST(head, headname); \ | |
354 | (var) != TAILQ_END(head); \ | |
355 | (var) = TAILQ_PREV(var, headname, field)) | |
343 | 356 | |
344 | 357 | /* |
345 | 358 | * Tail queue functions. |
346 | 359 | */ |
347 | #define TAILQ_INIT(head) do { \ | |
348 | (head)->tqh_first = NULL; \ | |
349 | (head)->tqh_last = &(head)->tqh_first; \ | |
350 | } while (0) | |
351 | ||
352 | #define TAILQ_INSERT_HEAD(head, elm, field) do { \ | |
353 | if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ | |
354 | (head)->tqh_first->field.tqe_prev = \ | |
355 | &(elm)->field.tqe_next; \ | |
356 | else \ | |
357 | (head)->tqh_last = &(elm)->field.tqe_next; \ | |
358 | (head)->tqh_first = (elm); \ | |
359 | (elm)->field.tqe_prev = &(head)->tqh_first; \ | |
360 | } while (0) | |
361 | ||
362 | #define TAILQ_INSERT_TAIL(head, elm, field) do { \ | |
363 | (elm)->field.tqe_next = NULL; \ | |
364 | (elm)->field.tqe_prev = (head)->tqh_last; \ | |
365 | *(head)->tqh_last = (elm); \ | |
366 | (head)->tqh_last = &(elm)->field.tqe_next; \ | |
367 | } while (0) | |
368 | ||
369 | #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ | |
370 | if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ | |
371 | (elm)->field.tqe_next->field.tqe_prev = \ | |
372 | &(elm)->field.tqe_next; \ | |
373 | else \ | |
374 | (head)->tqh_last = &(elm)->field.tqe_next; \ | |
375 | (listelm)->field.tqe_next = (elm); \ | |
376 | (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ | |
377 | } while (0) | |
378 | ||
379 | #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ | |
380 | (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ | |
381 | (elm)->field.tqe_next = (listelm); \ | |
382 | *(listelm)->field.tqe_prev = (elm); \ | |
383 | (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ | |
384 | } while (0) | |
385 | ||
386 | #define TAILQ_REMOVE(head, elm, field) do { \ | |
387 | if (((elm)->field.tqe_next) != NULL) \ | |
388 | (elm)->field.tqe_next->field.tqe_prev = \ | |
389 | (elm)->field.tqe_prev; \ | |
390 | else \ | |
391 | (head)->tqh_last = (elm)->field.tqe_prev; \ | |
392 | *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ | |
393 | _Q_INVALIDATE((elm)->field.tqe_prev); \ | |
394 | _Q_INVALIDATE((elm)->field.tqe_next); \ | |
395 | } while (0) | |
396 | ||
397 | #define TAILQ_REPLACE(head, elm, elm2, field) do { \ | |
398 | if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ | |
399 | (elm2)->field.tqe_next->field.tqe_prev = \ | |
400 | &(elm2)->field.tqe_next; \ | |
401 | else \ | |
402 | (head)->tqh_last = &(elm2)->field.tqe_next; \ | |
403 | (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ | |
404 | *(elm2)->field.tqe_prev = (elm2); \ | |
405 | _Q_INVALIDATE((elm)->field.tqe_prev); \ | |
406 | _Q_INVALIDATE((elm)->field.tqe_next); \ | |
407 | } while (0) | |
360 | #define TAILQ_INIT(head) \ | |
361 | do { \ | |
362 | (head)->tqh_first = NULL; \ | |
363 | (head)->tqh_last = &(head)->tqh_first; \ | |
364 | } while (0) | |
365 | ||
366 | #define TAILQ_INSERT_HEAD(head, elm, field) \ | |
367 | do { \ | |
368 | if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ | |
369 | (head)->tqh_first->field.tqe_prev = &(elm)->field.tqe_next; \ | |
370 | else \ | |
371 | (head)->tqh_last = &(elm)->field.tqe_next; \ | |
372 | (head)->tqh_first = (elm); \ | |
373 | (elm)->field.tqe_prev = &(head)->tqh_first; \ | |
374 | } while (0) | |
375 | ||
376 | #define TAILQ_INSERT_TAIL(head, elm, field) \ | |
377 | do { \ | |
378 | (elm)->field.tqe_next = NULL; \ | |
379 | (elm)->field.tqe_prev = (head)->tqh_last; \ | |
380 | *(head)->tqh_last = (elm); \ | |
381 | (head)->tqh_last = &(elm)->field.tqe_next; \ | |
382 | } while (0) | |
383 | ||
384 | #define TAILQ_INSERT_AFTER(head, listelm, elm, field) \ | |
385 | do { \ | |
386 | if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL) \ | |
387 | (elm)->field.tqe_next->field.tqe_prev = &(elm)->field.tqe_next; \ | |
388 | else \ | |
389 | (head)->tqh_last = &(elm)->field.tqe_next; \ | |
390 | (listelm)->field.tqe_next = (elm); \ | |
391 | (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ | |
392 | } while (0) | |
393 | ||
394 | #define TAILQ_INSERT_BEFORE(listelm, elm, field) \ | |
395 | do { \ | |
396 | (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ | |
397 | (elm)->field.tqe_next = (listelm); \ | |
398 | *(listelm)->field.tqe_prev = (elm); \ | |
399 | (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ | |
400 | } while (0) | |
401 | ||
402 | #define TAILQ_REMOVE(head, elm, field) \ | |
403 | do { \ | |
404 | if (((elm)->field.tqe_next) != NULL) \ | |
405 | (elm)->field.tqe_next->field.tqe_prev = (elm)->field.tqe_prev; \ | |
406 | else \ | |
407 | (head)->tqh_last = (elm)->field.tqe_prev; \ | |
408 | *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ | |
409 | _Q_INVALIDATE((elm)->field.tqe_prev); \ | |
410 | _Q_INVALIDATE((elm)->field.tqe_next); \ | |
411 | } while (0) | |
412 | ||
413 | #define TAILQ_REPLACE(head, elm, elm2, field) \ | |
414 | do { \ | |
415 | if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ | |
416 | (elm2)->field.tqe_next->field.tqe_prev = &(elm2)->field.tqe_next; \ | |
417 | else \ | |
418 | (head)->tqh_last = &(elm2)->field.tqe_next; \ | |
419 | (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ | |
420 | *(elm2)->field.tqe_prev = (elm2); \ | |
421 | _Q_INVALIDATE((elm)->field.tqe_prev); \ | |
422 | _Q_INVALIDATE((elm)->field.tqe_next); \ | |
423 | } while (0) | |
408 | 424 | |
409 | 425 | /* |
410 | 426 | * Circular queue definitions. |
411 | 427 | */ |
412 | #define CIRCLEQ_HEAD(name, type) \ | |
413 | struct name { \ | |
414 | struct type *cqh_first; /* first element */ \ | |
415 | struct type *cqh_last; /* last element */ \ | |
416 | } | |
417 | ||
418 | #define CIRCLEQ_HEAD_INITIALIZER(head) \ | |
419 | { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } | |
420 | ||
421 | #define CIRCLEQ_ENTRY(type) \ | |
422 | struct { \ | |
423 | struct type *cqe_next; /* next element */ \ | |
424 | struct type *cqe_prev; /* previous element */ \ | |
425 | } | |
428 | #define CIRCLEQ_HEAD(name, type) \ | |
429 | struct name { \ | |
430 | struct type *cqh_first; /* first element */ \ | |
431 | struct type *cqh_last; /* last element */ \ | |
432 | } | |
433 | ||
434 | #define CIRCLEQ_HEAD_INITIALIZER(head) \ | |
435 | { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } | |
436 | ||
437 | #define CIRCLEQ_ENTRY(type) \ | |
438 | struct { \ | |
439 | struct type *cqe_next; /* next element */ \ | |
440 | struct type *cqe_prev; /* previous element */ \ | |
441 | } | |
426 | 442 | |
427 | 443 | /* |
428 | 444 | * Circular queue access methods |
429 | 445 | */ |
430 | #define CIRCLEQ_FIRST(head) ((head)->cqh_first) | |
431 | #define CIRCLEQ_LAST(head) ((head)->cqh_last) | |
432 | #define CIRCLEQ_END(head) ((void *)(head)) | |
433 | #define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) | |
434 | #define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) | |
435 | #define CIRCLEQ_EMPTY(head) \ | |
436 | (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) | |
437 | ||
438 | #define CIRCLEQ_FOREACH(var, head, field) \ | |
439 | for((var) = CIRCLEQ_FIRST(head); \ | |
440 | (var) != CIRCLEQ_END(head); \ | |
441 | (var) = CIRCLEQ_NEXT(var, field)) | |
442 | ||
443 | #define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ | |
444 | for((var) = CIRCLEQ_LAST(head); \ | |
445 | (var) != CIRCLEQ_END(head); \ | |
446 | (var) = CIRCLEQ_PREV(var, field)) | |
446 | #define CIRCLEQ_FIRST(head) ((head)->cqh_first) | |
447 | #define CIRCLEQ_LAST(head) ((head)->cqh_last) | |
448 | #define CIRCLEQ_END(head) ((void *)(head)) | |
449 | #define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) | |
450 | #define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) | |
451 | #define CIRCLEQ_EMPTY(head) \ | |
452 | (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) | |
453 | ||
454 | #define CIRCLEQ_FOREACH(var, head, field) \ | |
455 | for ((var) = CIRCLEQ_FIRST(head); \ | |
456 | (var) != CIRCLEQ_END(head); \ | |
457 | (var) = CIRCLEQ_NEXT(var, field)) | |
458 | ||
459 | #define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ | |
460 | for ((var) = CIRCLEQ_LAST(head); \ | |
461 | (var) != CIRCLEQ_END(head); \ | |
462 | (var) = CIRCLEQ_PREV(var, field)) | |
447 | 463 | |
448 | 464 | /* |
449 | 465 | * Circular queue functions. |
450 | 466 | */ |
451 | #define CIRCLEQ_INIT(head) do { \ | |
452 | (head)->cqh_first = CIRCLEQ_END(head); \ | |
453 | (head)->cqh_last = CIRCLEQ_END(head); \ | |
454 | } while (0) | |
455 | ||
456 | #define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ | |
457 | (elm)->field.cqe_next = (listelm)->field.cqe_next; \ | |
458 | (elm)->field.cqe_prev = (listelm); \ | |
459 | if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ | |
460 | (head)->cqh_last = (elm); \ | |
461 | else \ | |
462 | (listelm)->field.cqe_next->field.cqe_prev = (elm); \ | |
463 | (listelm)->field.cqe_next = (elm); \ | |
464 | } while (0) | |
465 | ||
466 | #define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ | |
467 | (elm)->field.cqe_next = (listelm); \ | |
468 | (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ | |
469 | if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ | |
470 | (head)->cqh_first = (elm); \ | |
471 | else \ | |
472 | (listelm)->field.cqe_prev->field.cqe_next = (elm); \ | |
473 | (listelm)->field.cqe_prev = (elm); \ | |
474 | } while (0) | |
475 | ||
476 | #define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ | |
477 | (elm)->field.cqe_next = (head)->cqh_first; \ | |
478 | (elm)->field.cqe_prev = CIRCLEQ_END(head); \ | |
479 | if ((head)->cqh_last == CIRCLEQ_END(head)) \ | |
480 | (head)->cqh_last = (elm); \ | |
481 | else \ | |
482 | (head)->cqh_first->field.cqe_prev = (elm); \ | |
483 | (head)->cqh_first = (elm); \ | |
484 | } while (0) | |
485 | ||
486 | #define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ | |
487 | (elm)->field.cqe_next = CIRCLEQ_END(head); \ | |
488 | (elm)->field.cqe_prev = (head)->cqh_last; \ | |
489 | if ((head)->cqh_first == CIRCLEQ_END(head)) \ | |
490 | (head)->cqh_first = (elm); \ | |
491 | else \ | |
492 | (head)->cqh_last->field.cqe_next = (elm); \ | |
493 | (head)->cqh_last = (elm); \ | |
494 | } while (0) | |
495 | ||
496 | #define CIRCLEQ_REMOVE(head, elm, field) do { \ | |
497 | if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ | |
498 | (head)->cqh_last = (elm)->field.cqe_prev; \ | |
499 | else \ | |
500 | (elm)->field.cqe_next->field.cqe_prev = \ | |
501 | (elm)->field.cqe_prev; \ | |
502 | if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ | |
503 | (head)->cqh_first = (elm)->field.cqe_next; \ | |
504 | else \ | |
505 | (elm)->field.cqe_prev->field.cqe_next = \ | |
506 | (elm)->field.cqe_next; \ | |
507 | _Q_INVALIDATE((elm)->field.cqe_prev); \ | |
508 | _Q_INVALIDATE((elm)->field.cqe_next); \ | |
509 | } while (0) | |
510 | ||
511 | #define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ | |
512 | if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ | |
513 | CIRCLEQ_END(head)) \ | |
514 | (head)->cqh_last = (elm2); \ | |
515 | else \ | |
516 | (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ | |
517 | if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ | |
518 | CIRCLEQ_END(head)) \ | |
519 | (head)->cqh_first = (elm2); \ | |
520 | else \ | |
521 | (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ | |
522 | _Q_INVALIDATE((elm)->field.cqe_prev); \ | |
523 | _Q_INVALIDATE((elm)->field.cqe_next); \ | |
524 | } while (0) | |
525 | ||
526 | #endif /* !_SYS_QUEUE_H_ */ | |
467 | #define CIRCLEQ_INIT(head) \ | |
468 | do { \ | |
469 | (head)->cqh_first = CIRCLEQ_END(head); \ | |
470 | (head)->cqh_last = CIRCLEQ_END(head); \ | |
471 | } while (0) | |
472 | ||
473 | #define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) \ | |
474 | do { \ | |
475 | (elm)->field.cqe_next = (listelm)->field.cqe_next; \ | |
476 | (elm)->field.cqe_prev = (listelm); \ | |
477 | if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ | |
478 | (head)->cqh_last = (elm); \ | |
479 | else \ | |
480 | (listelm)->field.cqe_next->field.cqe_prev = (elm); \ | |
481 | (listelm)->field.cqe_next = (elm); \ | |
482 | } while (0) | |
483 | ||
484 | #define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) \ | |
485 | do { \ | |
486 | (elm)->field.cqe_next = (listelm); \ | |
487 | (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ | |
488 | if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ | |
489 | (head)->cqh_first = (elm); \ | |
490 | else \ | |
491 | (listelm)->field.cqe_prev->field.cqe_next = (elm); \ | |
492 | (listelm)->field.cqe_prev = (elm); \ | |
493 | } while (0) | |
494 | ||
495 | #define CIRCLEQ_INSERT_HEAD(head, elm, field) \ | |
496 | do { \ | |
497 | (elm)->field.cqe_next = (head)->cqh_first; \ | |
498 | (elm)->field.cqe_prev = CIRCLEQ_END(head); \ | |
499 | if ((head)->cqh_last == CIRCLEQ_END(head)) \ | |
500 | (head)->cqh_last = (elm); \ | |
501 | else \ | |
502 | (head)->cqh_first->field.cqe_prev = (elm); \ | |
503 | (head)->cqh_first = (elm); \ | |
504 | } while (0) | |
505 | ||
506 | #define CIRCLEQ_INSERT_TAIL(head, elm, field) \ | |
507 | do { \ | |
508 | (elm)->field.cqe_next = CIRCLEQ_END(head); \ | |
509 | (elm)->field.cqe_prev = (head)->cqh_last; \ | |
510 | if ((head)->cqh_first == CIRCLEQ_END(head)) \ | |
511 | (head)->cqh_first = (elm); \ | |
512 | else \ | |
513 | (head)->cqh_last->field.cqe_next = (elm); \ | |
514 | (head)->cqh_last = (elm); \ | |
515 | } while (0) | |
516 | ||
517 | #define CIRCLEQ_REMOVE(head, elm, field) \ | |
518 | do { \ | |
519 | if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ | |
520 | (head)->cqh_last = (elm)->field.cqe_prev; \ | |
521 | else \ | |
522 | (elm)->field.cqe_next->field.cqe_prev = (elm)->field.cqe_prev; \ | |
523 | if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ | |
524 | (head)->cqh_first = (elm)->field.cqe_next; \ | |
525 | else \ | |
526 | (elm)->field.cqe_prev->field.cqe_next = (elm)->field.cqe_next; \ | |
527 | _Q_INVALIDATE((elm)->field.cqe_prev); \ | |
528 | _Q_INVALIDATE((elm)->field.cqe_next); \ | |
529 | } while (0) | |
530 | ||
531 | #define CIRCLEQ_REPLACE(head, elm, elm2, field) \ | |
532 | do { \ | |
533 | if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == CIRCLEQ_END(head)) \ | |
534 | (head)->cqh_last = (elm2); \ | |
535 | else \ | |
536 | (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ | |
537 | if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == CIRCLEQ_END(head)) \ | |
538 | (head)->cqh_first = (elm2); \ | |
539 | else \ | |
540 | (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ | |
541 | _Q_INVALIDATE((elm)->field.cqe_prev); \ | |
542 | _Q_INVALIDATE((elm)->field.cqe_next); \ | |
543 | } while (0) | |
544 | ||
545 | #endif /* !_SYS_QUEUE_H_ */ |
6 | 6 | <refentrytitle>{mantitle}</refentrytitle> |
7 | 7 | <manvolnum>{manvolnum}</manvolnum> |
8 | 8 | <refmiscinfo class="source">i3status</refmiscinfo> |
9 | <refmiscinfo class="version">v2.8</refmiscinfo> | |
9 | <refmiscinfo class="version">v2.9</refmiscinfo> | |
10 | 10 | <refmiscinfo class="manual">i3 Manual</refmiscinfo> |
11 | 11 | </refmeta> |
12 | 12 | <refnamediv> |
0 | 0 | '\" t |
1 | 1 | .\" Title: i3status |
2 | 2 | .\" Author: [see the "AUTHORS" section] |
3 | .\" Generator: DocBook XSL Stylesheets v1.76.1 <http://docbook.sf.net/> | |
4 | .\" Date: 01/05/2014 | |
3 | .\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/> | |
4 | .\" Date: 03/21/2015 | |
5 | 5 | .\" Manual: i3 Manual |
6 | 6 | .\" Source: i3status v2.8 |
7 | 7 | .\" Language: English |
8 | 8 | .\" |
9 | .TH "I3STATUS" "1" "01/05/2014" "i3status v2\&.8" "i3 Manual" | |
9 | .TH "I3STATUS" "1" "03/21/2015" "i3status v2\&.8" "i3 Manual" | |
10 | 10 | .\" ----------------------------------------------------------------- |
11 | 11 | .\" * Define some portability stuff |
12 | 12 | .\" ----------------------------------------------------------------- |
127 | 127 | battery 0 { |
128 | 128 | format = "%status %percentage %remaining %emptytime" |
129 | 129 | format_down = "No battery" |
130 | status_chr = "⚇ CHR"" | |
131 | status_bat = "⚡ BAT" | |
132 | status_full = "☻ FULL" | |
130 | 133 | path = "/sys/class/power_supply/BAT%d/uevent" |
131 | 134 | low_threshold = 10 |
132 | 135 | } |
187 | 190 | .RE |
188 | 191 | .\} |
189 | 192 | .sp |
190 | Likewise, you can use the color_separator directive to specify the color that will be used to paint the separator bar\&. The separator is always output in color, even when colors are disabled by the colors directive\&. | |
193 | Likewise, you can use the color_separator directive to specify the color that will be used to paint the separator bar\&. The separator is always output in color, even when colors are disabled by the colors directive\&. This option has no effect when output_format is set to i3bar or none\&. | |
191 | 194 | .sp |
192 | 195 | The interval directive specifies the time in seconds for which i3status will sleep before printing the next status line\&. |
193 | 196 | .sp |
215 | 218 | .PP |
216 | 219 | none |
217 | 220 | .RS 4 |
218 | Does not use any color codes\&. Separates values by the pipe symbol\&. This should be used with i3bar and can be used for custom scripts\&. | |
221 | Does not use any color codes\&. Separates values by the pipe symbol by default\&. This should be used with i3bar and can be used for custom scripts\&. | |
219 | 222 | .RE |
220 | 223 | .sp |
221 | 224 | It\(cqs also possible to use the color_good, color_degraded, color_bad directives to define specific colors per module\&. If one of these directives is defined in a module section its value will override the value defined in the general section just for this module\&. |
225 | .sp | |
226 | If you don\(cqt fancy the vertical separators between modules i3status/i3bar uses by default, you can employ the separator directive to configure how modules are separated\&. You can either disable the default separator altogether setting it to the empty string\&. You might then define separation as part of a module\(cqs format string\&. This is your only option when using the i3bar output format as the separator is drawn by i3bar directly otherwise\&. For the other output formats, the provided non\-empty string will be automatically enclosed with the necessary coloring bits if color support is enabled\&. | |
227 | .sp | |
228 | \fBExample configuration\fR: | |
229 | .sp | |
230 | .if n \{\ | |
231 | .RS 4 | |
232 | .\} | |
233 | .nf | |
234 | general { | |
235 | output_format = "xmobar" | |
236 | separator = " " | |
237 | } | |
238 | ||
239 | order += "load" | |
240 | order += "disk /" | |
241 | ||
242 | load { | |
243 | format = "[ load: %1min, %5min, %15min ]" | |
244 | } | |
245 | disk "/" { | |
246 | format = "%avail" | |
247 | } | |
248 | .fi | |
249 | .if n \{\ | |
250 | .RE | |
251 | .\} | |
222 | 252 | .SS "IPv6" |
223 | 253 | .sp |
224 | 254 | This module gets the IPv6 address used for outgoing connections (that is, the best available public IPv6 address on your computer)\&. |
225 | 255 | .sp |
226 | 256 | \fBExample format_up\fR: %ip |
227 | 257 | .sp |
228 | \fBExample format_down\fR no IPv6 | |
258 | \fBExample format_down\fR: no IPv6 | |
229 | 259 | .SS "Disk" |
230 | 260 | .sp |
231 | 261 | Gets used, free, available and total amount of bytes on the given mounted filesystem\&. |
249 | 279 | The custom prefixes (K, M, G, T) represent multiples of powers of 1024\&. |
250 | 280 | .RE |
251 | 281 | .sp |
282 | It is possible to define a low_threshold that causes the disk text to be displayed using color_bad\&. The low_threshold type can be of threshold_type "bytes_free", "bytes_avail", "percentage_free", or "percentage_avail", where the former two can be prepended by a generic prefix (k, m, g, t) having prefix_type\&. So, if you configure low_threshold to 2, threshold_type to "gbytes_avail", and prefix_type to "binary", and the remaining available disk space is below 2 GiB, it will be colored bad\&. If not specified, threshold_type is assumed to be "percentage_avail" and low_threshold to be set to 0, which implies no coloring at all\&. | |
283 | .sp | |
284 | You can define a different format with the option "format_not_mounted" which is used if the path is not a mount point\&. So you can just empty the output for the given path with adding \(Fcformat_not_mounted=""\(Fo to the config section\&. | |
285 | .sp | |
252 | 286 | \fBExample order\fR: disk /mnt/usbstick |
253 | 287 | .sp |
254 | 288 | \fBExample format\fR: %free (%avail)/ %total |
256 | 290 | \fBExample format\fR: %percentage_used used, %percentage_free free, %percentage_avail avail |
257 | 291 | .sp |
258 | 292 | \fBExample prefix_type\fR: custom |
293 | .sp | |
294 | \fBExample low_threshold\fR: 5 | |
295 | .sp | |
296 | \fBExample threshold_type\fR: percentage_free | |
259 | 297 | .SS "Run\-watch" |
260 | 298 | .sp |
261 | 299 | Expands the given path to a pidfile and checks if the process ID found inside is valid (that is, if the process is running)\&. You can use this to check if a specific application, such as a VPN client or your DHCP client is running\&. |
272 | 310 | \fBExample format\fR: %title: %status |
273 | 311 | .SS "Wireless" |
274 | 312 | .sp |
275 | Gets the link quality and ESSID of the given wireless network interface\&. You can specify different format strings for the network being connected or not connected\&. | |
313 | Gets the link quality, frequency and ESSID of the given wireless network interface\&. You can specify different format strings for the network being connected or not connected\&. | |
314 | .sp | |
315 | The special interface name _first_ will be replaced by the first wireless network interface found on the system (excluding devices starting with "lo")\&. | |
276 | 316 | .sp |
277 | 317 | \fBExample order\fR: wireless wlan0 |
278 | 318 | .sp |
279 | \fBExample format\fR: W: (%quality at %essid, %bitrate) %ip | |
319 | \fBExample format\fR: W: (%quality at %essid, %bitrate / %frequency) %ip | |
280 | 320 | .SS "Ethernet" |
281 | 321 | .sp |
282 | 322 | Gets the IP address and (if possible) the link speed of the given ethernet interface\&. Getting the link speed requires the cap_net_admin capability\&. Set it using setcap cap_net_admin=ep $(which i3status)\&. |
323 | .sp | |
324 | The special interface name _first_ will be replaced by the first non\-wireless network interface found on the system (excluding devices starting with "lo")\&. | |
283 | 325 | .sp |
284 | 326 | \fBExample order\fR: ethernet eth0 |
285 | 327 | .sp |
286 | 328 | \fBExample format\fR: E: %ip (%speed) |
287 | 329 | .SS "Battery" |
288 | 330 | .sp |
289 | Gets the status (charging, discharging, running), percentage, remaining time and power consumption (in Watts) of the given battery and when it\(cqs estimated to be empty\&. If you want to use the last full capacity instead of the design capacity (when using the design capacity, it may happen that your battery is at 23% when fully charged because it\(cqs old\&. In general, I want to see it this way, because it tells me how worn off my battery is\&.), just specify last_full_capacity = true\&. | |
331 | Gets the status (charging, discharging, running), percentage, remaining time and power consumption (in Watts) of the given battery and when it\(cqs estimated to be empty\&. If you want to use the last full capacity instead of the design capacity (when using the design capacity, it may happen that your battery is at 23% when fully charged because it\(cqs old\&. In general, I want to see it this way, because it tells me how worn off my battery is\&.), just specify last_full_capacity = true\&. You can hide seconds in the remaining time and empty time estimations by setting hide_seconds = true\&. | |
290 | 332 | .sp |
291 | 333 | If you want the battery percentage to be shown without decimals, add integer_battery_capacity = true\&. |
292 | 334 | .sp |
294 | 336 | .sp |
295 | 337 | It is possible to define a low_threshold that causes the battery text to be colored red\&. The low_threshold type can be of threshold_type "time" or "percentage"\&. So, if you configure low_threshold to 10 and threshold_type to "time", and your battery lasts another 9 minutes, it will be colored red\&. |
296 | 338 | .sp |
339 | Optionally custom strings including any UTF\-8 symbols can be used for different battery states\&. This makes it possible to display individual symbols for each state (charging, discharging, full) Of course it will also work with special iconic fonts, such as FontAwesome\&. If any of this special status strings is omitted, the default (CHR, BAT, FULL) is used\&. | |
340 | .sp | |
297 | 341 | \fBExample order\fR: battery 0 |
298 | 342 | .sp |
299 | 343 | \fBExample format\fR: %status %remaining (%emptytime %consumption) |
344 | .sp | |
345 | \fBExample format_down\fR: No battery | |
346 | .sp | |
347 | \fBExample status_chr\fR: ⚇ CHR | |
348 | .sp | |
349 | \fBExample status_bat\fR: ⚡ BAT | |
350 | .sp | |
351 | \fBExample status_full\fR: ☻ FULL | |
300 | 352 | .sp |
301 | 353 | \fBExample low_threshold\fR: 30 |
302 | 354 | .sp |
355 | 407 | \fBExample format\fR: %{%a, %b %d%}, %Y%N \- %H |
356 | 408 | .SS "Volume" |
357 | 409 | .sp |
358 | Outputs the volume of the specified mixer on the specified device\&. Works only on Linux because it uses ALSA\&. A simplified configuration can be used on FreeBSD and OpenBSD due to the lack of ALSA, the device and mixer options can be ignored on these systems\&. On these systems the OSS API is used instead to query /dev/mixer directly if mixer_dix is \-1, otherwise /dev/mixer+mixer_idx+\&. | |
410 | Outputs the volume of the specified mixer on the specified device\&. If compiled with USE_PULSEAUDIO, may work on any system with PulseAudio\&. Otherwise only ALSA is available and therefore will only work on Linux\&. In this case a simplified configuration can be used on FreeBSD and OpenBSD due to the lack of ALSA, the device and mixer options can be ignored on these systems\&. On these systems the OSS API is used instead to query /dev/mixer directly if mixer_idx is \-1, otherwise /dev/mixer+mixer_idx+\&. | |
411 | .sp | |
412 | To get PulseAudio volume information, one must use the following format in the device line: | |
413 | .sp | |
414 | .if n \{\ | |
415 | .RS 4 | |
416 | .\} | |
417 | .nf | |
418 | device = "pulse" | |
419 | .fi | |
420 | .if n \{\ | |
421 | .RE | |
422 | .\} | |
423 | .sp | |
424 | or | |
425 | .sp | |
426 | .if n \{\ | |
427 | .RS 4 | |
428 | .\} | |
429 | .nf | |
430 | device = "pulse:N" | |
431 | .fi | |
432 | .if n \{\ | |
433 | .RE | |
434 | .\} | |
435 | .sp | |
436 | where N is the index of the PulseAudio sink\&. If no sink is specified the default is used\&. If the device string is missing or is set to "default", PulseAudio will be tried if detected and will fallback to ALSA (Linux) or OSS (FreeBSD/OpenBSD)\&. | |
359 | 437 | .sp |
360 | 438 | \fBExample order\fR: volume master |
361 | 439 | .sp |
378 | 456 | .if n \{\ |
379 | 457 | .RE |
380 | 458 | .\} |
459 | .sp | |
460 | \fBExample configuration (PulseAudio)\fR: | |
461 | .sp | |
462 | .if n \{\ | |
463 | .RS 4 | |
464 | .\} | |
465 | .nf | |
466 | volume master { | |
467 | format = "♪: %volume" | |
468 | format_muted = "♪: muted (%volume)" | |
469 | device = "pulse:1" | |
470 | } | |
471 | .fi | |
472 | .if n \{\ | |
473 | .RE | |
474 | .\} | |
475 | .SH "UNIVERSAL MODULE OPTIONS" | |
476 | .sp | |
477 | When using the i3bar output format, there are a few additional options that can be used with all modules to customize their appearance: | |
478 | .PP | |
479 | align | |
480 | .RS 4 | |
481 | The alignment policy to use when the minimum width (see below) is not reached\&. Either | |
482 | center | |
483 | (default), | |
484 | right | |
485 | or | |
486 | left\&. | |
487 | .RE | |
488 | .PP | |
489 | min_width | |
490 | .RS 4 | |
491 | The minimum width (in pixels) the module should occupy\&. If the module takes less space than the specified size, the block will be padded to the left and/or the right side, according to the defined alignment policy\&. This is useful when you want to prevent the whole status line from shifting when values take more or less space between each iteration\&. The option can also be a string\&. In this case, the width of the given text determines the minimum width of the block\&. This is useful when you want to set a sensible minimum width regardless of which font you are using, and at what particular size\&. Please note that a number enclosed with quotes will still be treated as a number\&. | |
492 | .RE | |
493 | .sp | |
494 | \fBExample configuration\fR: | |
495 | .sp | |
496 | .if n \{\ | |
497 | .RS 4 | |
498 | .\} | |
499 | .nf | |
500 | disk "/" { | |
501 | format = "%avail" | |
502 | align = "left" | |
503 | min_width = 100 | |
504 | } | |
505 | .fi | |
506 | .if n \{\ | |
507 | .RE | |
508 | .\} | |
381 | 509 | .SH "USING I3STATUS WITH DZEN2" |
382 | 510 | .sp |
383 | 511 | After installing dzen2, you can directly use it with i3status\&. Just ensure that output_format is set to dzen2\&. |
0 | 0 | i3status(1) |
1 | 1 | =========== |
2 | 2 | Michael Stapelberg <michael@i3wm.org> |
3 | v2.8, January 2014 | |
3 | v2.9, March 2015 | |
4 | 4 | |
5 | 5 | == NAME |
6 | 6 | |
73 | 73 | battery 0 { |
74 | 74 | format = "%status %percentage %remaining %emptytime" |
75 | 75 | format_down = "No battery" |
76 | status_chr = "⚇ CHR"" | |
77 | status_bat = "⚡ BAT" | |
78 | status_full = "☻ FULL" | |
76 | 79 | path = "/sys/class/power_supply/BAT%d/uevent" |
77 | 80 | low_threshold = 10 |
78 | 81 | } |
131 | 134 | |
132 | 135 | Likewise, you can use the +color_separator+ directive to specify the color that |
133 | 136 | will be used to paint the separator bar. The separator is always output in |
134 | color, even when colors are disabled by the +colors+ directive. | |
137 | color, even when colors are disabled by the +colors+ directive. This option has | |
138 | no effect when +output_format+ is set to +i3bar+ or +none+. | |
135 | 139 | |
136 | 140 | The +interval+ directive specifies the time in seconds for which i3status will |
137 | 141 | sleep before printing the next status line. |
159 | 163 | should only used for such quick glances, because it will only support very |
160 | 164 | basic output-features (for example you only get 3 bits of color depth). |
161 | 165 | none:: |
162 | Does not use any color codes. Separates values by the pipe symbol. This should | |
163 | be used with i3bar and can be used for custom scripts. | |
166 | Does not use any color codes. Separates values by the pipe symbol by default. | |
167 | This should be used with i3bar and can be used for custom scripts. | |
164 | 168 | |
165 | 169 | It's also possible to use the color_good, color_degraded, color_bad directives |
166 | 170 | to define specific colors per module. If one of these directives is defined |
167 | 171 | in a module section its value will override the value defined in the general |
168 | 172 | section just for this module. |
169 | 173 | |
174 | If you don't fancy the vertical separators between modules i3status/i3bar | |
175 | uses by default, you can employ the +separator+ directive to configure how | |
176 | modules are separated. You can either disable the default separator altogether | |
177 | setting it to the empty string. You might then define separation as part of a | |
178 | module's format string. This is your only option when using the i3bar output | |
179 | format as the separator is drawn by i3bar directly otherwise. For the other | |
180 | output formats, the provided non-empty string will be automatically enclosed | |
181 | with the necessary coloring bits if color support is enabled. | |
182 | ||
183 | *Example configuration*: | |
184 | ------------------------------------------------------------- | |
185 | general { | |
186 | output_format = "xmobar" | |
187 | separator = " " | |
188 | } | |
189 | ||
190 | order += "load" | |
191 | order += "disk /" | |
192 | ||
193 | load { | |
194 | format = "[ load: %1min, %5min, %15min ]" | |
195 | } | |
196 | disk "/" { | |
197 | format = "%avail" | |
198 | } | |
199 | ------------------------------------------------------------- | |
200 | ||
170 | 201 | === IPv6 |
171 | 202 | |
172 | 203 | This module gets the IPv6 address used for outgoing connections (that is, the |
174 | 205 | |
175 | 206 | *Example format_up*: +%ip+ |
176 | 207 | |
177 | *Example format_down* +no IPv6+ | |
208 | *Example format_down*: +no IPv6+ | |
178 | 209 | |
179 | 210 | === Disk |
180 | 211 | |
195 | 226 | custom:: |
196 | 227 | The custom prefixes (K, M, G, T) represent multiples of powers of 1024. |
197 | 228 | |
229 | It is possible to define a low_threshold that causes the disk text to be | |
230 | displayed using color_bad. The low_threshold type can be of threshold_type | |
231 | "bytes_free", "bytes_avail", "percentage_free", or "percentage_avail", where | |
232 | the former two can be prepended by a generic prefix (k, m, g, t) having | |
233 | prefix_type. So, if you configure low_threshold to 2, threshold_type to | |
234 | "gbytes_avail", and prefix_type to "binary", and the remaining available disk | |
235 | space is below 2 GiB, it will be colored bad. If not specified, threshold_type | |
236 | is assumed to be "percentage_avail" and low_threshold to be set to 0, which | |
237 | implies no coloring at all. | |
238 | ||
239 | You can define a different format with the option "format_not_mounted" | |
240 | which is used if the path is not a mount point. So you can just empty | |
241 | the output for the given path with adding »format_not_mounted=""« | |
242 | to the config section. | |
243 | ||
198 | 244 | *Example order*: +disk /mnt/usbstick+ |
199 | 245 | |
200 | 246 | *Example format*: +%free (%avail)/ %total+ |
202 | 248 | *Example format*: +%percentage_used used, %percentage_free free, %percentage_avail avail+ |
203 | 249 | |
204 | 250 | *Example prefix_type*: +custom+ |
251 | ||
252 | *Example low_threshold*: +5+ | |
253 | ||
254 | *Example threshold_type*: +percentage_free+ | |
205 | 255 | |
206 | 256 | === Run-watch |
207 | 257 | |
224 | 274 | |
225 | 275 | === Wireless |
226 | 276 | |
227 | Gets the link quality and ESSID of the given wireless network interface. You | |
228 | can specify different format strings for the network being connected or not | |
229 | connected. | |
277 | Gets the link quality, frequency and ESSID of the given wireless network | |
278 | interface. You can specify different format strings for the network being | |
279 | connected or not connected. | |
280 | ||
281 | The special interface name `_first_` will be replaced by the first wireless | |
282 | network interface found on the system (excluding devices starting with "lo"). | |
230 | 283 | |
231 | 284 | *Example order*: +wireless wlan0+ |
232 | 285 | |
233 | *Example format*: +W: (%quality at %essid, %bitrate) %ip+ | |
286 | *Example format*: +W: (%quality at %essid, %bitrate / %frequency) %ip+ | |
234 | 287 | |
235 | 288 | === Ethernet |
236 | 289 | |
237 | 290 | Gets the IP address and (if possible) the link speed of the given ethernet |
238 | 291 | interface. Getting the link speed requires the cap_net_admin capability. Set |
239 | 292 | it using +setcap cap_net_admin=ep $(which i3status)+. |
293 | ||
294 | The special interface name `_first_` will be replaced by the first non-wireless | |
295 | network interface found on the system (excluding devices starting with "lo"). | |
240 | 296 | |
241 | 297 | *Example order*: +ethernet eth0+ |
242 | 298 | |
250 | 306 | design capacity (when using the design capacity, it may happen that your |
251 | 307 | battery is at 23% when fully charged because it’s old. In general, I want to |
252 | 308 | see it this way, because it tells me how worn off my battery is.), just specify |
253 | +last_full_capacity = true+. | |
309 | +last_full_capacity = true+. You can hide seconds in the remaining time and | |
310 | empty time estimations by setting +hide_seconds = true+. | |
254 | 311 | |
255 | 312 | If you want the battery percentage to be shown without decimals, add |
256 | 313 | +integer_battery_capacity = true+. |
265 | 322 | "percentage". So, if you configure low_threshold to 10 and threshold_type to |
266 | 323 | "time", and your battery lasts another 9 minutes, it will be colored red. |
267 | 324 | |
325 | Optionally custom strings including any UTF-8 symbols can be used for different | |
326 | battery states. This makes it possible to display individual symbols | |
327 | for each state (charging, discharging, full) | |
328 | Of course it will also work with special iconic fonts, such as FontAwesome. | |
329 | If any of this special status strings is omitted, the default (CHR, BAT, FULL) | |
330 | is used. | |
331 | ||
268 | 332 | *Example order*: +battery 0+ |
269 | 333 | |
270 | 334 | *Example format*: +%status %remaining (%emptytime %consumption)+ |
335 | ||
336 | *Example format_down*: +No battery+ | |
337 | ||
338 | *Example status_chr*: +⚇ CHR+ | |
339 | ||
340 | *Example status_bat*: +⚡ BAT+ | |
341 | ||
342 | *Example status_full*: +☻ FULL+ | |
271 | 343 | |
272 | 344 | *Example low_threshold*: +30+ |
273 | 345 | |
370 | 442 | device = "default" |
371 | 443 | mixer = "Master" |
372 | 444 | mixer_idx = 0 |
445 | } | |
446 | ------------------------------------------------------------- | |
447 | ||
448 | == Universal module options | |
449 | ||
450 | When using the i3bar output format, there are a few additional options that | |
451 | can be used with all modules to customize their appearance: | |
452 | ||
453 | align:: | |
454 | The alignment policy to use when the minimum width (see below) is not | |
455 | reached. Either +center+ (default), +right+ or +left+. | |
456 | min_width:: | |
457 | The minimum width (in pixels) the module should occupy. If the module takes | |
458 | less space than the specified size, the block will be padded to the left | |
459 | and/or the right side, according to the defined alignment policy. This is | |
460 | useful when you want to prevent the whole status line from shifting when | |
461 | values take more or less space between each iteration. | |
462 | The option can also be a string. In this case, the width of the given text | |
463 | determines the minimum width of the block. This is useful when you want to | |
464 | set a sensible minimum width regardless of which font you are using, and at | |
465 | what particular size. Please note that a number enclosed with quotes will | |
466 | still be treated as a number. | |
467 | ||
468 | *Example configuration*: | |
469 | ------------------------------------------------------------- | |
470 | disk "/" { | |
471 | format = "%avail" | |
472 | align = "left" | |
473 | min_width = 100 | |
373 | 474 | } |
374 | 475 | ------------------------------------------------------------- |
375 | 476 |
0 | // vim:ts=4:sw=4:expandtab | |
1 | #include <sys/stat.h> | |
2 | #include <stdlib.h> | |
3 | #include <ifaddrs.h> | |
4 | ||
5 | #include "i3status.h" | |
6 | ||
7 | const char *first_eth_interface(const net_type_t type) { | |
8 | static char *interface = NULL; | |
9 | struct ifaddrs *ifaddr, *addrp; | |
10 | struct stat stbuf; | |
11 | static char path[1024]; | |
12 | ||
13 | getifaddrs(&ifaddr); | |
14 | ||
15 | if (ifaddr == NULL) | |
16 | return NULL; | |
17 | ||
18 | free(interface); | |
19 | interface = NULL; | |
20 | for (addrp = ifaddr; | |
21 | addrp != NULL; | |
22 | addrp = addrp->ifa_next) { | |
23 | if (strncasecmp("lo", addrp->ifa_name, strlen("lo")) == 0) | |
24 | continue; | |
25 | // Skip this interface if it is a wireless interface. | |
26 | snprintf(path, sizeof(path), "/sys/class/net/%s/wireless", addrp->ifa_name); | |
27 | const bool is_wireless = (stat(path, &stbuf) == 0); | |
28 | if ((is_wireless && type == NET_TYPE_ETHERNET) || | |
29 | (!is_wireless && type == NET_TYPE_WIRELESS)) | |
30 | continue; | |
31 | interface = strdup(addrp->ifa_name); | |
32 | break; | |
33 | } | |
34 | ||
35 | freeifaddrs(ifaddr); | |
36 | return interface; | |
37 | } |
0 | // vim:ts=8:expandtab | |
0 | // vim:ts=4:sw=4:expandtab | |
1 | 1 | #include <sys/types.h> |
2 | 2 | #include <string.h> |
3 | 3 | #include <stdarg.h> |
14 | 14 | * |
15 | 15 | */ |
16 | 16 | bool slurp(const char *filename, char *destination, int size) { |
17 | int fd; | |
17 | int fd; | |
18 | 18 | |
19 | if ((fd = open(filename, O_RDONLY)) == -1) | |
20 | return false; | |
19 | if ((fd = open(filename, O_RDONLY)) == -1) | |
20 | return false; | |
21 | 21 | |
22 | /* We need one byte for the trailing 0 byte */ | |
23 | int n = read(fd, destination, size-1); | |
24 | if (n != -1) | |
25 | destination[n] = '\0'; | |
26 | (void)close(fd); | |
22 | /* We need one byte for the trailing 0 byte */ | |
23 | int n = read(fd, destination, size - 1); | |
24 | if (n != -1) | |
25 | destination[n] = '\0'; | |
26 | (void)close(fd); | |
27 | 27 | |
28 | return true; | |
28 | return true; | |
29 | 29 | } |
30 | 30 | |
31 | 31 | /* |
34 | 34 | * |
35 | 35 | */ |
36 | 36 | char *skip_character(char *input, char character, int amount) { |
37 | char *walk; | |
38 | size_t len = strlen(input); | |
39 | int blanks = 0; | |
37 | char *walk; | |
38 | size_t len = strlen(input); | |
39 | int blanks = 0; | |
40 | 40 | |
41 | for (walk = input; ((size_t)(walk - input) < len) && (blanks < amount); walk++) | |
42 | if (*walk == character) | |
43 | blanks++; | |
41 | for (walk = input; ((size_t)(walk - input) < len) && (blanks < amount); walk++) | |
42 | if (*walk == character) | |
43 | blanks++; | |
44 | 44 | |
45 | return (walk == input ? walk : walk-1); | |
45 | return (walk == input ? walk : walk - 1); | |
46 | 46 | } |
47 | 47 | |
48 | 48 | /* |
50 | 50 | * |
51 | 51 | */ |
52 | 52 | void die(const char *fmt, ...) { |
53 | char buffer[512]; | |
54 | va_list ap; | |
55 | va_start(ap, fmt); | |
56 | (void)vsnprintf(buffer, sizeof(buffer), fmt, ap); | |
57 | va_end(ap); | |
53 | char buffer[512]; | |
54 | va_list ap; | |
55 | va_start(ap, fmt); | |
56 | (void)vsnprintf(buffer, sizeof(buffer), fmt, ap); | |
57 | va_end(ap); | |
58 | 58 | |
59 | fprintf(stderr, "%s", buffer); | |
60 | exit(EXIT_FAILURE); | |
59 | fprintf(stderr, "%s", buffer); | |
60 | exit(EXIT_FAILURE); | |
61 | 61 | } |
0 | // vim:ts=8:expandtab | |
0 | // vim:ts=4:sw=4:expandtab | |
1 | 1 | #include <stdbool.h> |
2 | 2 | #include <string.h> |
3 | 3 | #include <stdio.h> |
14 | 14 | * |
15 | 15 | */ |
16 | 16 | char *color(const char *colorstr) { |
17 | static char colorbuf[32]; | |
18 | if (!cfg_getbool(cfg_general, "colors")) { | |
19 | colorbuf[0] = '\0'; | |
20 | return colorbuf; | |
21 | } | |
22 | if (output_format == O_DZEN2) | |
23 | (void)snprintf(colorbuf, sizeof(colorbuf), "^fg(%s)", cfg_getstr(cfg_general, colorstr)); | |
24 | else if (output_format == O_XMOBAR) | |
25 | (void)snprintf(colorbuf, sizeof(colorbuf), "<fc=%s>", cfg_getstr(cfg_general, colorstr)); | |
26 | else if (output_format == O_TERM) { | |
27 | /* The escape-sequence for color is <CSI><col>;1m (bright/bold | |
28 | * output), where col is a 3-bit rgb-value with b in the | |
29 | * least-significant bit. We round the given color to the | |
30 | * nearist 3-bit-depth color and output the escape-sequence */ | |
31 | char *str = cfg_getstr(cfg_general, colorstr); | |
32 | int col = strtol(str + 1, NULL, 16); | |
33 | int r = (col & (0xFF << 0)) / 0x80; | |
34 | int g = (col & (0xFF << 8)) / 0x8000; | |
35 | int b = (col & (0xFF << 16)) / 0x800000; | |
36 | col = (r << 2) | (g << 1) | b; | |
37 | (void)snprintf(colorbuf, sizeof(colorbuf), "\033[3%d;1m", col); | |
38 | } | |
17 | static char colorbuf[32]; | |
18 | if (!cfg_getbool(cfg_general, "colors")) { | |
19 | colorbuf[0] = '\0'; | |
39 | 20 | return colorbuf; |
21 | } | |
22 | if (output_format == O_DZEN2) | |
23 | (void)snprintf(colorbuf, sizeof(colorbuf), "^fg(%s)", cfg_getstr(cfg_general, colorstr)); | |
24 | else if (output_format == O_XMOBAR) | |
25 | (void)snprintf(colorbuf, sizeof(colorbuf), "<fc=%s>", cfg_getstr(cfg_general, colorstr)); | |
26 | else if (output_format == O_TERM) { | |
27 | /* The escape-sequence for color is <CSI><col>;1m (bright/bold | |
28 | * output), where col is a 3-bit rgb-value with b in the | |
29 | * least-significant bit. We round the given color to the | |
30 | * nearist 3-bit-depth color and output the escape-sequence */ | |
31 | char *str = cfg_getstr(cfg_general, colorstr); | |
32 | int col = strtol(str + 1, NULL, 16); | |
33 | int r = (col & (0xFF << 0)) / 0x80; | |
34 | int g = (col & (0xFF << 8)) / 0x8000; | |
35 | int b = (col & (0xFF << 16)) / 0x800000; | |
36 | col = (r << 2) | (g << 1) | b; | |
37 | (void)snprintf(colorbuf, sizeof(colorbuf), "\033[3%d;1m", col); | |
38 | } | |
39 | return colorbuf; | |
40 | 40 | } |
41 | 41 | |
42 | 42 | /* |
44 | 44 | * |
45 | 45 | */ |
46 | 46 | char *endcolor(void) { |
47 | if (output_format == O_XMOBAR) | |
48 | return "</fc>"; | |
49 | else if (output_format == O_TERM) | |
50 | return "\033[0m"; | |
51 | else return ""; | |
47 | if (output_format == O_XMOBAR) | |
48 | return "</fc>"; | |
49 | else if (output_format == O_TERM) | |
50 | return "\033[0m"; | |
51 | else | |
52 | return ""; | |
52 | 53 | } |
53 | 54 | |
54 | void print_seperator(void) { | |
55 | if (output_format == O_DZEN2) | |
56 | printf("^fg(%s)^p(5;-2)^ro(2)^p()^fg()^p(5)", cfg_getstr(cfg_general, "color_separator")); | |
57 | else if (output_format == O_XMOBAR) | |
58 | printf("<fc=%s> | </fc>", cfg_getstr(cfg_general, "color_separator")); | |
59 | else if (output_format == O_TERM) | |
60 | printf(" %s|%s ", color("color_separator"), endcolor()); | |
61 | else if (output_format == O_NONE) | |
62 | printf(" | "); | |
55 | void print_separator(const char *separator) { | |
56 | if (output_format == O_I3BAR || strlen(separator) == 0) | |
57 | return; | |
58 | ||
59 | if (output_format == O_DZEN2) | |
60 | printf("^fg(%s)%s^fg()", cfg_getstr(cfg_general, "color_separator"), separator); | |
61 | else if (output_format == O_XMOBAR) | |
62 | printf("<fc=%s>%s</fc>", cfg_getstr(cfg_general, "color_separator"), separator); | |
63 | else if (output_format == O_TERM) | |
64 | printf("%s%s%s", color("color_separator"), separator, endcolor()); | |
65 | else if (output_format == O_NONE) | |
66 | printf("%s", separator); | |
63 | 67 | } |
64 | 68 | |
65 | 69 | /* |
66 | 70 | * The term-output hides the cursor. We call this on exit to reset that. |
67 | 71 | */ |
68 | 72 | void reset_cursor(void) { |
69 | printf("\033[?25h"); | |
73 | printf("\033[?25h"); | |
70 | 74 | } |
0 | // vim:ts=8:expandtab | |
0 | // vim:ts=4:sw=4:expandtab | |
1 | 1 | #include <ctype.h> |
2 | 2 | #include <time.h> |
3 | 3 | #include <string.h> |
20 | 20 | #include <machine/apmvar.h> |
21 | 21 | #endif |
22 | 22 | |
23 | #define BATT_STATUS_NAME(status) \ | |
24 | (status == CS_CHARGING ? "CHR" : \ | |
25 | (status == CS_DISCHARGING ? "BAT" : "FULL")) | |
23 | #if defined(__NetBSD__) | |
24 | #include <fcntl.h> | |
25 | #include <prop/proplib.h> | |
26 | #include <sys/envsys.h> | |
27 | #endif | |
28 | ||
26 | 29 | /* |
27 | 30 | * Get battery information from /sys. Note that it uses the design capacity to |
28 | 31 | * calculate the percentage, not the last full capacity, so you can see how |
29 | 32 | * worn off your battery is. |
30 | 33 | * |
31 | 34 | */ |
32 | void print_battery_info(yajl_gen json_gen, char *buffer, int number, const char *path, const char *format, const char *format_down, int low_threshold, char *threshold_type, bool last_full_capacity, bool integer_battery_capacity) { | |
33 | time_t empty_time; | |
34 | struct tm *empty_tm; | |
35 | char buf[1024]; | |
36 | char statusbuf[16]; | |
37 | char percentagebuf[16]; | |
38 | char remainingbuf[256]; | |
39 | char emptytimebuf[256]; | |
40 | char consumptionbuf[256]; | |
41 | const char *walk, *last; | |
42 | char *outwalk = buffer; | |
43 | bool watt_as_unit; | |
44 | bool colorful_output = false; | |
45 | int full_design = -1, | |
46 | remaining = -1, | |
47 | present_rate = -1, | |
48 | voltage = -1; | |
49 | charging_status_t status = CS_DISCHARGING; | |
50 | ||
51 | memset(statusbuf, '\0', sizeof(statusbuf)); | |
52 | memset(percentagebuf, '\0', sizeof(percentagebuf)); | |
53 | memset(remainingbuf, '\0', sizeof(remainingbuf)); | |
54 | memset(emptytimebuf, '\0', sizeof(emptytimebuf)); | |
55 | memset(consumptionbuf, '\0', sizeof(consumptionbuf)); | |
56 | ||
57 | static char batpath[512]; | |
58 | sprintf(batpath, path, number); | |
59 | INSTANCE(batpath); | |
35 | void print_battery_info(yajl_gen json_gen, char *buffer, int number, const char *path, const char *format, const char *format_down, const char *status_chr, const char *status_bat, const char *status_full, int low_threshold, char *threshold_type, bool last_full_capacity, bool integer_battery_capacity, bool hide_seconds) { | |
36 | time_t empty_time; | |
37 | struct tm *empty_tm; | |
38 | char buf[1024]; | |
39 | char statusbuf[16]; | |
40 | char percentagebuf[16]; | |
41 | char remainingbuf[256]; | |
42 | char emptytimebuf[256]; | |
43 | char consumptionbuf[256]; | |
44 | const char *walk, *last; | |
45 | char *outwalk = buffer; | |
46 | bool watt_as_unit = false; | |
47 | bool colorful_output = false; | |
48 | int full_design = -1, | |
49 | remaining = -1, | |
50 | present_rate = -1, | |
51 | voltage = -1; | |
52 | charging_status_t status = CS_DISCHARGING; | |
53 | ||
54 | memset(statusbuf, '\0', sizeof(statusbuf)); | |
55 | memset(percentagebuf, '\0', sizeof(percentagebuf)); | |
56 | memset(remainingbuf, '\0', sizeof(remainingbuf)); | |
57 | memset(emptytimebuf, '\0', sizeof(emptytimebuf)); | |
58 | memset(consumptionbuf, '\0', sizeof(consumptionbuf)); | |
59 | ||
60 | static char batpath[512]; | |
61 | sprintf(batpath, path, number); | |
62 | INSTANCE(batpath); | |
63 | ||
64 | #define BATT_STATUS_NAME(status) \ | |
65 | (status == CS_CHARGING ? status_chr : (status == CS_DISCHARGING ? status_bat : status_full)) | |
60 | 66 | |
61 | 67 | #if defined(LINUX) |
62 | if (!slurp(batpath, buf, sizeof(buf))) { | |
63 | OUTPUT_FULL_TEXT(format_down); | |
64 | return; | |
65 | } | |
66 | ||
67 | for (walk = buf, last = buf; (walk-buf) < 1024; walk++) { | |
68 | if (*walk == '\n') { | |
69 | last = walk+1; | |
70 | continue; | |
71 | } | |
72 | ||
73 | if (*walk != '=') | |
74 | continue; | |
75 | ||
76 | if (BEGINS_WITH(last, "POWER_SUPPLY_ENERGY_NOW")) { | |
77 | watt_as_unit = true; | |
78 | remaining = atoi(walk+1); | |
79 | } | |
80 | else if (BEGINS_WITH(last, "POWER_SUPPLY_CHARGE_NOW")) { | |
81 | watt_as_unit = false; | |
82 | remaining = atoi(walk+1); | |
83 | } | |
84 | else if (BEGINS_WITH(last, "POWER_SUPPLY_CURRENT_NOW")) | |
85 | present_rate = atoi(walk+1); | |
86 | else if (BEGINS_WITH(last, "POWER_SUPPLY_VOLTAGE_NOW")) | |
87 | voltage = atoi(walk+1); | |
88 | /* on some systems POWER_SUPPLY_POWER_NOW does not exist, but actually | |
89 | * it is the same as POWER_SUPPLY_CURRENT_NOW but with μWh as | |
90 | * unit instead of μAh. We will calculate it as we need it | |
91 | * later. */ | |
92 | else if (BEGINS_WITH(last, "POWER_SUPPLY_POWER_NOW")) | |
93 | present_rate = atoi(walk+1); | |
94 | else if (BEGINS_WITH(last, "POWER_SUPPLY_STATUS=Charging")) | |
95 | status = CS_CHARGING; | |
96 | else if (BEGINS_WITH(last, "POWER_SUPPLY_STATUS=Full")) | |
97 | status = CS_FULL; | |
98 | else { | |
99 | /* The only thing left is the full capacity */ | |
100 | if (last_full_capacity) { | |
101 | if (!BEGINS_WITH(last, "POWER_SUPPLY_ENERGY_FULL") && | |
102 | !BEGINS_WITH(last, "POWER_SUPPLY_CHARGE_FULL")) | |
103 | continue; | |
104 | } else { | |
105 | if (!BEGINS_WITH(last, "POWER_SUPPLY_CHARGE_FULL_DESIGN") && | |
106 | !BEGINS_WITH(last, "POWER_SUPPLY_ENERGY_FULL_DESIGN")) | |
107 | continue; | |
108 | } | |
109 | ||
110 | full_design = atoi(walk+1); | |
111 | } | |
112 | } | |
113 | ||
114 | /* the difference between POWER_SUPPLY_ENERGY_NOW and | |
115 | * POWER_SUPPLY_CHARGE_NOW is the unit of measurement. The energy is | |
116 | * given in mWh, the charge in mAh. So calculate every value given in | |
117 | * ampere to watt */ | |
118 | if (!watt_as_unit) { | |
119 | present_rate = (((float)voltage / 1000.0) * ((float)present_rate / 1000.0)); | |
68 | if (!slurp(batpath, buf, sizeof(buf))) { | |
69 | OUTPUT_FULL_TEXT(format_down); | |
70 | return; | |
71 | } | |
72 | ||
73 | for (walk = buf, last = buf; (walk - buf) < 1024; walk++) { | |
74 | if (*walk == '\n') { | |
75 | last = walk + 1; | |
76 | continue; | |
77 | } | |
78 | ||
79 | if (*walk != '=') | |
80 | continue; | |
81 | ||
82 | if (BEGINS_WITH(last, "POWER_SUPPLY_ENERGY_NOW")) { | |
83 | watt_as_unit = true; | |
84 | remaining = atoi(walk + 1); | |
85 | } else if (BEGINS_WITH(last, "POWER_SUPPLY_CHARGE_NOW")) { | |
86 | watt_as_unit = false; | |
87 | remaining = atoi(walk + 1); | |
88 | } else if (BEGINS_WITH(last, "POWER_SUPPLY_CURRENT_NOW")) | |
89 | present_rate = abs(atoi(walk + 1)); | |
90 | else if (BEGINS_WITH(last, "POWER_SUPPLY_VOLTAGE_NOW")) | |
91 | voltage = abs(atoi(walk + 1)); | |
92 | /* on some systems POWER_SUPPLY_POWER_NOW does not exist, but actually | |
93 | * it is the same as POWER_SUPPLY_CURRENT_NOW but with μWh as | |
94 | * unit instead of μAh. We will calculate it as we need it | |
95 | * later. */ | |
96 | else if (BEGINS_WITH(last, "POWER_SUPPLY_POWER_NOW")) | |
97 | present_rate = abs(atoi(walk + 1)); | |
98 | else if (BEGINS_WITH(last, "POWER_SUPPLY_STATUS=Charging")) | |
99 | status = CS_CHARGING; | |
100 | else if (BEGINS_WITH(last, "POWER_SUPPLY_STATUS=Full")) | |
101 | status = CS_FULL; | |
102 | else { | |
103 | /* The only thing left is the full capacity */ | |
104 | if (last_full_capacity) { | |
105 | if (!BEGINS_WITH(last, "POWER_SUPPLY_ENERGY_FULL") && | |
106 | !BEGINS_WITH(last, "POWER_SUPPLY_CHARGE_FULL")) | |
107 | continue; | |
108 | } else { | |
109 | if (!BEGINS_WITH(last, "POWER_SUPPLY_CHARGE_FULL_DESIGN") && | |
110 | !BEGINS_WITH(last, "POWER_SUPPLY_ENERGY_FULL_DESIGN")) | |
111 | continue; | |
112 | } | |
113 | ||
114 | full_design = atoi(walk + 1); | |
115 | } | |
116 | } | |
117 | ||
118 | /* the difference between POWER_SUPPLY_ENERGY_NOW and | |
119 | * POWER_SUPPLY_CHARGE_NOW is the unit of measurement. The energy is | |
120 | * given in mWh, the charge in mAh. So calculate every value given in | |
121 | * ampere to watt */ | |
122 | if (!watt_as_unit) { | |
123 | present_rate = (((float)voltage / 1000.0) * ((float)present_rate / 1000.0)); | |
124 | ||
125 | if (voltage != -1) { | |
120 | 126 | remaining = (((float)voltage / 1000.0) * ((float)remaining / 1000.0)); |
121 | 127 | full_design = (((float)voltage / 1000.0) * ((float)full_design / 1000.0)); |
122 | 128 | } |
123 | ||
124 | if ((full_design == -1) || (remaining == -1)) { | |
125 | OUTPUT_FULL_TEXT(format_down); | |
126 | return; | |
127 | } | |
128 | ||
129 | (void)snprintf(statusbuf, sizeof(statusbuf), "%s", BATT_STATUS_NAME(status)); | |
130 | ||
131 | float percentage_remaining = (((float)remaining / (float)full_design) * 100); | |
132 | if (integer_battery_capacity) { | |
133 | (void)snprintf(percentagebuf, sizeof(percentagebuf), "%.00f%%", percentage_remaining); | |
134 | } else { | |
135 | (void)snprintf(percentagebuf, sizeof(percentagebuf), "%.02f%%", percentage_remaining); | |
136 | } | |
137 | ||
138 | if (present_rate > 0) { | |
139 | float remaining_time; | |
140 | int seconds, hours, minutes, seconds_remaining; | |
141 | if (status == CS_CHARGING) | |
142 | remaining_time = ((float)full_design - (float)remaining) / (float)present_rate; | |
143 | else if (status == CS_DISCHARGING) | |
144 | remaining_time = ((float)remaining / (float)present_rate); | |
145 | else remaining_time = 0; | |
146 | ||
147 | seconds_remaining = (int)(remaining_time * 3600.0); | |
148 | ||
149 | hours = seconds_remaining / 3600; | |
150 | seconds = seconds_remaining - (hours * 3600); | |
151 | minutes = seconds / 60; | |
152 | seconds -= (minutes * 60); | |
153 | ||
154 | if (status == CS_DISCHARGING && low_threshold > 0) { | |
155 | if (strncmp(threshold_type, "percentage", strlen(threshold_type)) == 0 | |
156 | && percentage_remaining < low_threshold) { | |
157 | START_COLOR("color_bad"); | |
158 | colorful_output = true; | |
159 | } else if (strncmp(threshold_type, "time", strlen(threshold_type)) == 0 | |
160 | && seconds_remaining < 60 * low_threshold) { | |
161 | START_COLOR("color_bad"); | |
162 | colorful_output = true; | |
163 | } else { | |
164 | colorful_output = false; | |
165 | } | |
166 | } | |
167 | ||
168 | (void)snprintf(remainingbuf, sizeof(remainingbuf), "%02d:%02d:%02d", | |
169 | max(hours, 0), max(minutes, 0), max(seconds, 0)); | |
170 | ||
171 | empty_time = time(NULL); | |
172 | empty_time += seconds_remaining; | |
173 | empty_tm = localtime(&empty_time); | |
174 | ||
175 | (void)snprintf(emptytimebuf, sizeof(emptytimebuf), "%02d:%02d:%02d", | |
176 | max(empty_tm->tm_hour, 0), max(empty_tm->tm_min, 0), max(empty_tm->tm_sec, 0)); | |
177 | ||
178 | (void)snprintf(consumptionbuf, sizeof(consumptionbuf), "%1.2fW", | |
179 | ((float)present_rate / 1000.0 / 1000.0)); | |
180 | } else { | |
181 | /* On some systems, present_rate may not exist. Still, make sure | |
182 | * we colorize the output if threshold_type is set to percentage | |
183 | * (since we don't have any information on remaining time). */ | |
184 | if (status == CS_DISCHARGING && low_threshold > 0) { | |
185 | if (strncmp(threshold_type, "percentage", strlen(threshold_type)) == 0 | |
186 | && percentage_remaining < low_threshold) { | |
187 | START_COLOR("color_bad"); | |
188 | colorful_output = true; | |
189 | } | |
190 | } | |
191 | } | |
129 | } | |
130 | ||
131 | if ((full_design == -1) || (remaining == -1)) { | |
132 | OUTPUT_FULL_TEXT(format_down); | |
133 | return; | |
134 | } | |
135 | ||
136 | (void)snprintf(statusbuf, sizeof(statusbuf), "%s", BATT_STATUS_NAME(status)); | |
137 | ||
138 | float percentage_remaining = (((float)remaining / (float)full_design) * 100); | |
139 | if (integer_battery_capacity) { | |
140 | (void)snprintf(percentagebuf, sizeof(percentagebuf), "%.00f%%", percentage_remaining); | |
141 | } else { | |
142 | (void)snprintf(percentagebuf, sizeof(percentagebuf), "%.02f%%", percentage_remaining); | |
143 | } | |
144 | ||
145 | if (present_rate > 0) { | |
146 | float remaining_time; | |
147 | int seconds, hours, minutes, seconds_remaining; | |
148 | if (status == CS_CHARGING) | |
149 | remaining_time = ((float)full_design - (float)remaining) / (float)present_rate; | |
150 | else if (status == CS_DISCHARGING) | |
151 | remaining_time = ((float)remaining / (float)present_rate); | |
152 | else | |
153 | remaining_time = 0; | |
154 | ||
155 | seconds_remaining = (int)(remaining_time * 3600.0); | |
156 | ||
157 | hours = seconds_remaining / 3600; | |
158 | seconds = seconds_remaining - (hours * 3600); | |
159 | minutes = seconds / 60; | |
160 | seconds -= (minutes * 60); | |
161 | ||
162 | if (status == CS_DISCHARGING && low_threshold > 0) { | |
163 | if (strcasecmp(threshold_type, "percentage") == 0 && percentage_remaining < low_threshold) { | |
164 | START_COLOR("color_bad"); | |
165 | colorful_output = true; | |
166 | } else if (strcasecmp(threshold_type, "time") == 0 && seconds_remaining < 60 * low_threshold) { | |
167 | START_COLOR("color_bad"); | |
168 | colorful_output = true; | |
169 | } else { | |
170 | colorful_output = false; | |
171 | } | |
172 | } | |
173 | ||
174 | if (hide_seconds) | |
175 | (void)snprintf(remainingbuf, sizeof(remainingbuf), "%02d:%02d", | |
176 | max(hours, 0), max(minutes, 0)); | |
177 | else | |
178 | (void)snprintf(remainingbuf, sizeof(remainingbuf), "%02d:%02d:%02d", | |
179 | max(hours, 0), max(minutes, 0), max(seconds, 0)); | |
180 | ||
181 | empty_time = time(NULL); | |
182 | empty_time += seconds_remaining; | |
183 | empty_tm = localtime(&empty_time); | |
184 | ||
185 | if (hide_seconds) | |
186 | (void)snprintf(emptytimebuf, sizeof(emptytimebuf), "%02d:%02d", | |
187 | max(empty_tm->tm_hour, 0), max(empty_tm->tm_min, 0)); | |
188 | else | |
189 | (void)snprintf(emptytimebuf, sizeof(emptytimebuf), "%02d:%02d:%02d", | |
190 | max(empty_tm->tm_hour, 0), max(empty_tm->tm_min, 0), max(empty_tm->tm_sec, 0)); | |
191 | ||
192 | (void)snprintf(consumptionbuf, sizeof(consumptionbuf), "%1.2fW", | |
193 | ((float)present_rate / 1000.0 / 1000.0)); | |
194 | } else { | |
195 | /* On some systems, present_rate may not exist. Still, make sure | |
196 | * we colorize the output if threshold_type is set to percentage | |
197 | * (since we don't have any information on remaining time). */ | |
198 | if (status == CS_DISCHARGING && low_threshold > 0) { | |
199 | if (strcasecmp(threshold_type, "percentage") == 0 && percentage_remaining < low_threshold) { | |
200 | START_COLOR("color_bad"); | |
201 | colorful_output = true; | |
202 | } | |
203 | } | |
204 | } | |
192 | 205 | #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) |
193 | int state; | |
194 | int sysctl_rslt; | |
195 | size_t sysctl_size = sizeof(sysctl_rslt); | |
196 | ||
197 | if (sysctlbyname(BATT_LIFE, &sysctl_rslt, &sysctl_size, NULL, 0) != 0) { | |
198 | OUTPUT_FULL_TEXT(format_down); | |
199 | return; | |
200 | } | |
201 | ||
202 | present_rate = sysctl_rslt; | |
203 | if (sysctlbyname(BATT_TIME, &sysctl_rslt, &sysctl_size, NULL, 0) != 0) { | |
204 | OUTPUT_FULL_TEXT(format_down); | |
205 | return; | |
206 | } | |
207 | ||
208 | remaining = sysctl_rslt; | |
209 | if (sysctlbyname(BATT_STATE, &sysctl_rslt, &sysctl_size, NULL,0) != 0) { | |
210 | OUTPUT_FULL_TEXT(format_down); | |
211 | return; | |
212 | } | |
213 | ||
214 | state = sysctl_rslt; | |
215 | if (state == 0 && present_rate == 100) | |
216 | status = CS_FULL; | |
217 | else if (state == 0 && present_rate < 100) | |
218 | status = CS_CHARGING; | |
219 | else | |
220 | status = CS_DISCHARGING; | |
221 | ||
222 | full_design = sysctl_rslt; | |
223 | ||
224 | (void)snprintf(statusbuf, sizeof(statusbuf), "%s", BATT_STATUS_NAME(status)); | |
225 | ||
226 | (void)snprintf(percentagebuf, sizeof(percentagebuf), "%02d%%", | |
227 | present_rate); | |
228 | ||
229 | if (state == 1) { | |
230 | int hours, minutes; | |
231 | minutes = remaining; | |
232 | hours = minutes / 60; | |
233 | minutes -= (hours * 60); | |
234 | (void)snprintf(remainingbuf, sizeof(remainingbuf), "%02dh%02d", | |
235 | max(hours, 0), max(minutes, 0)); | |
236 | if (strncmp(threshold_type, "percentage", strlen(threshold_type)) == 0 | |
237 | && present_rate < low_threshold) { | |
238 | START_COLOR("color_bad"); | |
239 | colorful_output = true; | |
240 | } else if (strncmp(threshold_type, "time", strlen(threshold_type)) == 0 | |
241 | && remaining < (u_int) low_threshold) { | |
242 | START_COLOR("color_bad"); | |
243 | colorful_output = true; | |
244 | } | |
245 | } | |
206 | int state; | |
207 | int sysctl_rslt; | |
208 | size_t sysctl_size = sizeof(sysctl_rslt); | |
209 | ||
210 | if (sysctlbyname(BATT_LIFE, &sysctl_rslt, &sysctl_size, NULL, 0) != 0) { | |
211 | OUTPUT_FULL_TEXT(format_down); | |
212 | return; | |
213 | } | |
214 | ||
215 | present_rate = sysctl_rslt; | |
216 | if (sysctlbyname(BATT_TIME, &sysctl_rslt, &sysctl_size, NULL, 0) != 0) { | |
217 | OUTPUT_FULL_TEXT(format_down); | |
218 | return; | |
219 | } | |
220 | ||
221 | remaining = sysctl_rslt; | |
222 | if (sysctlbyname(BATT_STATE, &sysctl_rslt, &sysctl_size, NULL, 0) != 0) { | |
223 | OUTPUT_FULL_TEXT(format_down); | |
224 | return; | |
225 | } | |
226 | ||
227 | state = sysctl_rslt; | |
228 | if (state == 0 && present_rate == 100) | |
229 | status = CS_FULL; | |
230 | else if (state == 0 && present_rate < 100) | |
231 | status = CS_CHARGING; | |
232 | else | |
233 | status = CS_DISCHARGING; | |
234 | ||
235 | full_design = sysctl_rslt; | |
236 | ||
237 | (void)snprintf(statusbuf, sizeof(statusbuf), "%s", BATT_STATUS_NAME(status)); | |
238 | ||
239 | (void)snprintf(percentagebuf, sizeof(percentagebuf), "%02d%%", | |
240 | present_rate); | |
241 | ||
242 | if (state == 1) { | |
243 | int hours, minutes; | |
244 | minutes = remaining; | |
245 | hours = minutes / 60; | |
246 | minutes -= (hours * 60); | |
247 | (void)snprintf(remainingbuf, sizeof(remainingbuf), "%02dh%02d", | |
248 | max(hours, 0), max(minutes, 0)); | |
249 | if (strcasecmp(threshold_type, "percentage") == 0 && present_rate < low_threshold) { | |
250 | START_COLOR("color_bad"); | |
251 | colorful_output = true; | |
252 | } else if (strcasecmp(threshold_type, "time") == 0 && remaining < (u_int)low_threshold) { | |
253 | START_COLOR("color_bad"); | |
254 | colorful_output = true; | |
255 | } | |
256 | } | |
246 | 257 | #elif defined(__OpenBSD__) |
247 | /* | |
258 | /* | |
248 | 259 | * We're using apm(4) here, which is the interface to acpi(4) on amd64/i386 and |
249 | 260 | * the generic interface on macppc/sparc64/zaurus, instead of using sysctl(3) and |
250 | 261 | * probing acpi(4) devices. |
251 | 262 | */ |
252 | struct apm_power_info apm_info; | |
253 | int apm_fd; | |
254 | ||
255 | apm_fd = open("/dev/apm", O_RDONLY); | |
256 | if (apm_fd < 0) { | |
257 | OUTPUT_FULL_TEXT("can't open /dev/apm"); | |
258 | return; | |
259 | } | |
260 | if (ioctl(apm_fd, APM_IOC_GETPOWER, &apm_info) < 0) | |
261 | OUTPUT_FULL_TEXT("can't read power info"); | |
262 | ||
263 | close(apm_fd); | |
264 | ||
265 | /* Don't bother to go further if there's no battery present. */ | |
266 | if ((apm_info.battery_state == APM_BATTERY_ABSENT) || | |
267 | (apm_info.battery_state == APM_BATT_UNKNOWN)) { | |
268 | OUTPUT_FULL_TEXT(format_down); | |
269 | return; | |
270 | } | |
271 | ||
272 | switch(apm_info.ac_state) { | |
273 | case APM_AC_OFF: | |
274 | status = CS_DISCHARGING; | |
275 | break; | |
276 | case APM_AC_ON: | |
277 | status = CS_CHARGING; | |
278 | break; | |
279 | default: | |
280 | /* If we don't know what's going on, just assume we're discharging. */ | |
281 | status = CS_DISCHARGING; | |
282 | break; | |
283 | } | |
284 | ||
285 | (void)snprintf(statusbuf, sizeof(statusbuf), "%s", BATT_STATUS_NAME(status)); | |
286 | (void)snprintf(percentagebuf, sizeof(percentagebuf), "%02d%%", apm_info.battery_life); | |
287 | ||
288 | if (status == CS_DISCHARGING && low_threshold > 0) { | |
289 | if (strncmp(threshold_type, "percentage", strlen(threshold_type)) == 0 | |
290 | && apm_info.battery_life < low_threshold) { | |
291 | START_COLOR("color_bad"); | |
292 | colorful_output = true; | |
293 | } else if (strncmp(threshold_type, "time", strlen(threshold_type)) == 0 | |
294 | && apm_info.minutes_left < (u_int) low_threshold) { | |
295 | START_COLOR("color_bad"); | |
296 | colorful_output = true; | |
297 | } | |
298 | } | |
299 | ||
300 | /* Can't give a meaningful value for remaining minutes if we're charging. */ | |
301 | if (status != CS_CHARGING) { | |
302 | (void)snprintf(remainingbuf, sizeof(remainingbuf), "%d", apm_info.minutes_left); | |
303 | } else { | |
304 | (void)snprintf(remainingbuf, sizeof(remainingbuf), "%s", "(CHR)"); | |
305 | } | |
306 | ||
307 | if (colorful_output) | |
308 | END_COLOR; | |
263 | struct apm_power_info apm_info; | |
264 | int apm_fd; | |
265 | ||
266 | apm_fd = open("/dev/apm", O_RDONLY); | |
267 | if (apm_fd < 0) { | |
268 | OUTPUT_FULL_TEXT("can't open /dev/apm"); | |
269 | return; | |
270 | } | |
271 | if (ioctl(apm_fd, APM_IOC_GETPOWER, &apm_info) < 0) | |
272 | OUTPUT_FULL_TEXT("can't read power info"); | |
273 | ||
274 | close(apm_fd); | |
275 | ||
276 | /* Don't bother to go further if there's no battery present. */ | |
277 | if ((apm_info.battery_state == APM_BATTERY_ABSENT) || | |
278 | (apm_info.battery_state == APM_BATT_UNKNOWN)) { | |
279 | OUTPUT_FULL_TEXT(format_down); | |
280 | return; | |
281 | } | |
282 | ||
283 | switch (apm_info.ac_state) { | |
284 | case APM_AC_OFF: | |
285 | status = CS_DISCHARGING; | |
286 | break; | |
287 | case APM_AC_ON: | |
288 | status = CS_CHARGING; | |
289 | break; | |
290 | default: | |
291 | /* If we don't know what's going on, just assume we're discharging. */ | |
292 | status = CS_DISCHARGING; | |
293 | break; | |
294 | } | |
295 | ||
296 | (void)snprintf(statusbuf, sizeof(statusbuf), "%s", BATT_STATUS_NAME(status)); | |
297 | /* integer_battery_capacity is implied as battery_life is already in whole numbers. */ | |
298 | (void)snprintf(percentagebuf, sizeof(percentagebuf), "%.00d%%", apm_info.battery_life); | |
299 | ||
300 | if (status == CS_DISCHARGING && low_threshold > 0) { | |
301 | if (strcasecmp(threshold_type, "percentage") == 0 && apm_info.battery_life < low_threshold) { | |
302 | START_COLOR("color_bad"); | |
303 | colorful_output = true; | |
304 | } else if (strcasecmp(threshold_type, "time") == 0 && apm_info.minutes_left < (u_int)low_threshold) { | |
305 | START_COLOR("color_bad"); | |
306 | colorful_output = true; | |
307 | } | |
308 | } | |
309 | ||
310 | /* Can't give a meaningful value for remaining minutes if we're charging. */ | |
311 | if (status != CS_CHARGING) { | |
312 | (void)snprintf(remainingbuf, sizeof(remainingbuf), "%d", apm_info.minutes_left); | |
313 | } else { | |
314 | (void)snprintf(remainingbuf, sizeof(remainingbuf), "%s", "(CHR)"); | |
315 | } | |
316 | ||
317 | if (colorful_output) | |
318 | END_COLOR; | |
319 | #elif defined(__NetBSD__) | |
320 | /* | |
321 | * Using envsys(4) via sysmon(4). | |
322 | */ | |
323 | int fd, rval, last_full_cap; | |
324 | bool is_found = false; | |
325 | char *sensor_desc; | |
326 | bool is_full = false; | |
327 | ||
328 | prop_dictionary_t dict; | |
329 | prop_array_t array; | |
330 | prop_object_iterator_t iter; | |
331 | prop_object_iterator_t iter2; | |
332 | prop_object_t obj, obj2, obj3, obj4, obj5; | |
333 | ||
334 | asprintf(&sensor_desc, "acpibat%d", number); | |
335 | ||
336 | fd = open("/dev/sysmon", O_RDONLY); | |
337 | if (fd < 0) { | |
338 | OUTPUT_FULL_TEXT("can't open /dev/sysmon"); | |
339 | return; | |
340 | } | |
341 | ||
342 | rval = prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &dict); | |
343 | if (rval == -1) { | |
344 | close(fd); | |
345 | return; | |
346 | } | |
347 | ||
348 | if (prop_dictionary_count(dict) == 0) { | |
349 | prop_object_release(dict); | |
350 | close(fd); | |
351 | return; | |
352 | } | |
353 | ||
354 | iter = prop_dictionary_iterator(dict); | |
355 | if (iter == NULL) { | |
356 | prop_object_release(dict); | |
357 | close(fd); | |
358 | } | |
359 | ||
360 | /* iterate over the dictionary returned by the kernel */ | |
361 | while ((obj = prop_object_iterator_next(iter)) != NULL) { | |
362 | /* skip this dict if it's not what we're looking for */ | |
363 | if ((strlen(prop_dictionary_keysym_cstring_nocopy(obj)) == strlen(sensor_desc)) && | |
364 | (strncmp(sensor_desc, | |
365 | prop_dictionary_keysym_cstring_nocopy(obj), | |
366 | strlen(sensor_desc)) != 0)) | |
367 | continue; | |
368 | ||
369 | is_found = true; | |
370 | ||
371 | array = prop_dictionary_get_keysym(dict, obj); | |
372 | if (prop_object_type(array) != PROP_TYPE_ARRAY) { | |
373 | prop_object_iterator_release(iter); | |
374 | prop_object_release(dict); | |
375 | close(fd); | |
376 | return; | |
377 | } | |
378 | ||
379 | iter2 = prop_array_iterator(array); | |
380 | if (!iter2) { | |
381 | prop_object_iterator_release(iter); | |
382 | prop_object_release(dict); | |
383 | close(fd); | |
384 | return; | |
385 | } | |
386 | ||
387 | /* iterate over array of dicts specific to target battery */ | |
388 | while ((obj2 = prop_object_iterator_next(iter2)) != NULL) { | |
389 | obj3 = prop_dictionary_get(obj2, "description"); | |
390 | ||
391 | if (obj3 && | |
392 | strlen(prop_string_cstring_nocopy(obj3)) == 8 && | |
393 | strncmp("charging", | |
394 | prop_string_cstring_nocopy(obj3), | |
395 | 8) == 0) { | |
396 | obj3 = prop_dictionary_get(obj2, "cur-value"); | |
397 | ||
398 | if (prop_number_integer_value(obj3)) | |
399 | status = CS_CHARGING; | |
400 | else | |
401 | status = CS_DISCHARGING; | |
402 | ||
403 | continue; | |
404 | } | |
405 | ||
406 | if (obj3 && | |
407 | strlen(prop_string_cstring_nocopy(obj3)) == 6 && | |
408 | strncmp("charge", | |
409 | prop_string_cstring_nocopy(obj3), | |
410 | 6) == 0) { | |
411 | obj3 = prop_dictionary_get(obj2, "cur-value"); | |
412 | obj4 = prop_dictionary_get(obj2, "max-value"); | |
413 | obj5 = prop_dictionary_get(obj2, "type"); | |
414 | ||
415 | remaining = prop_number_integer_value(obj3); | |
416 | full_design = prop_number_integer_value(obj4); | |
417 | ||
418 | if (remaining == full_design) | |
419 | is_full = true; | |
420 | ||
421 | if (strncmp("Ampere hour", | |
422 | prop_string_cstring_nocopy(obj5), | |
423 | 11) == 0) | |
424 | watt_as_unit = false; | |
425 | else | |
426 | watt_as_unit = true; | |
427 | ||
428 | continue; | |
429 | } | |
430 | ||
431 | if (obj3 && | |
432 | strlen(prop_string_cstring_nocopy(obj3)) == 14 && | |
433 | strncmp("discharge rate", | |
434 | prop_string_cstring_nocopy(obj3), | |
435 | 14) == 0) { | |
436 | obj3 = prop_dictionary_get(obj2, "cur-value"); | |
437 | present_rate = prop_number_integer_value(obj3); | |
438 | continue; | |
439 | } | |
440 | ||
441 | if (obj3 && | |
442 | strlen(prop_string_cstring_nocopy(obj3)) == 13 && | |
443 | strncmp("last full cap", | |
444 | prop_string_cstring_nocopy(obj3), | |
445 | 13) == 0) { | |
446 | obj3 = prop_dictionary_get(obj2, "cur-value"); | |
447 | last_full_cap = prop_number_integer_value(obj3); | |
448 | continue; | |
449 | } | |
450 | ||
451 | if (obj3 && | |
452 | strlen(prop_string_cstring_nocopy(obj3)) == 7 && | |
453 | strncmp("voltage", | |
454 | prop_string_cstring_nocopy(obj3), | |
455 | 7) == 0) { | |
456 | obj3 = prop_dictionary_get(obj2, "cur-value"); | |
457 | voltage = prop_number_integer_value(obj3); | |
458 | continue; | |
459 | } | |
460 | } | |
461 | prop_object_iterator_release(iter2); | |
462 | } | |
463 | ||
464 | prop_object_iterator_release(iter); | |
465 | prop_object_release(dict); | |
466 | close(fd); | |
467 | ||
468 | if (!is_found) { | |
469 | OUTPUT_FULL_TEXT(format_down); | |
470 | return; | |
471 | } | |
472 | ||
473 | if (last_full_capacity) | |
474 | full_design = last_full_cap; | |
475 | ||
476 | if (!watt_as_unit) { | |
477 | present_rate = (((float)voltage / 1000.0) * ((float)present_rate / 1000.0)); | |
478 | remaining = (((float)voltage / 1000.0) * ((float)remaining / 1000.0)); | |
479 | full_design = (((float)voltage / 1000.0) * ((float)full_design / 1000.0)); | |
480 | } | |
481 | ||
482 | float percentage_remaining = | |
483 | (((float)remaining / (float)full_design) * 100); | |
484 | ||
485 | if (integer_battery_capacity) | |
486 | (void)snprintf(percentagebuf, | |
487 | sizeof(percentagebuf), | |
488 | "%d%%", | |
489 | (int)percentage_remaining); | |
490 | else | |
491 | (void)snprintf(percentagebuf, | |
492 | sizeof(percentagebuf), | |
493 | "%.02f%%", | |
494 | percentage_remaining); | |
495 | ||
496 | /* | |
497 | * Handle percentage low_threshold here, and time low_threshold when | |
498 | * we have it. | |
499 | */ | |
500 | if (status == CS_DISCHARGING && low_threshold > 0) { | |
501 | if (strcasecmp(threshold_type, "percentage") == 0 && (((float)remaining / (float)full_design) * 100) < low_threshold) { | |
502 | START_COLOR("color_bad"); | |
503 | colorful_output = true; | |
504 | } | |
505 | } | |
506 | ||
507 | if (is_full) | |
508 | (void)snprintf(statusbuf, sizeof(statusbuf), "%s", BATT_STATUS_NAME(CS_FULL)); | |
509 | else | |
510 | (void)snprintf(statusbuf, sizeof(statusbuf), "%s", BATT_STATUS_NAME(status)); | |
511 | ||
512 | /* | |
513 | * The envsys(4) ACPI routines do not appear to provide a 'time | |
514 | * remaining' figure, so we must deduce it. | |
515 | */ | |
516 | float remaining_time; | |
517 | int seconds, hours, minutes, seconds_remaining; | |
518 | ||
519 | if (status == CS_CHARGING) | |
520 | remaining_time = ((float)full_design - (float)remaining) / (float)present_rate; | |
521 | else if (status == CS_DISCHARGING) | |
522 | remaining_time = ((float)remaining / (float)present_rate); | |
523 | else | |
524 | remaining_time = 0; | |
525 | ||
526 | seconds_remaining = (int)(remaining_time * 3600.0); | |
527 | ||
528 | hours = seconds_remaining / 3600; | |
529 | seconds = seconds_remaining - (hours * 3600); | |
530 | minutes = seconds / 60; | |
531 | seconds -= (minutes * 60); | |
532 | ||
533 | if (status != CS_CHARGING) { | |
534 | if (hide_seconds) | |
535 | (void)snprintf(remainingbuf, sizeof(remainingbuf), "%02d:%02d", | |
536 | max(hours, 0), max(minutes, 0)); | |
537 | else | |
538 | (void)snprintf(remainingbuf, sizeof(remainingbuf), "%02d:%02d:%02d", | |
539 | max(hours, 0), max(minutes, 0), max(seconds, 0)); | |
540 | ||
541 | if (low_threshold > 0) { | |
542 | if (strcasecmp(threshold_type, "time") == 0 && ((float)seconds_remaining / 60.0) < (u_int)low_threshold) { | |
543 | START_COLOR("color_bad"); | |
544 | colorful_output = true; | |
545 | } | |
546 | } | |
547 | } else { | |
548 | if (hide_seconds) | |
549 | (void)snprintf(remainingbuf, sizeof(remainingbuf), "(%02d:%02d until full)", | |
550 | max(hours, 0), max(minutes, 0)); | |
551 | else | |
552 | (void)snprintf(remainingbuf, sizeof(remainingbuf), "(%02d:%02d:%02d until full)", | |
553 | max(hours, 0), max(minutes, 0), max(seconds, 0)); | |
554 | } | |
555 | ||
556 | empty_time = time(NULL); | |
557 | empty_time += seconds_remaining; | |
558 | empty_tm = localtime(&empty_time); | |
559 | ||
560 | /* No need to show empty time if battery is charging */ | |
561 | if (status != CS_CHARGING) { | |
562 | if (hide_seconds) | |
563 | (void)snprintf(emptytimebuf, sizeof(emptytimebuf), "%02d:%02d", | |
564 | max(empty_tm->tm_hour, 0), max(empty_tm->tm_min, 0)); | |
565 | else | |
566 | (void)snprintf(emptytimebuf, sizeof(emptytimebuf), "%02d:%02d:%02d", | |
567 | max(empty_tm->tm_hour, 0), max(empty_tm->tm_min, 0), max(empty_tm->tm_sec, 0)); | |
568 | } | |
569 | ||
570 | (void)snprintf(consumptionbuf, sizeof(consumptionbuf), "%1.2fW", | |
571 | ((float)present_rate / 1000.0 / 1000.0)); | |
309 | 572 | #endif |
310 | 573 | |
311 | #define EAT_SPACE_FROM_OUTPUT_IF_EMPTY(_buf) \ | |
312 | do { \ | |
313 | if (strlen(_buf) == 0) { \ | |
314 | if (outwalk > buffer && isspace(outwalk[-1])) \ | |
315 | outwalk--; \ | |
316 | else if (isspace(*(walk+1))) \ | |
317 | walk++; \ | |
318 | } \ | |
319 | } while (0) | |
320 | ||
321 | for (walk = format; *walk != '\0'; walk++) { | |
322 | if (*walk != '%') { | |
323 | *(outwalk++) = *walk; | |
324 | continue; | |
325 | } | |
326 | ||
327 | if (strncmp(walk+1, "status", strlen("status")) == 0) { | |
328 | outwalk += sprintf(outwalk, "%s", statusbuf); | |
329 | walk += strlen("status"); | |
330 | } else if (strncmp(walk+1, "percentage", strlen("percentage")) == 0) { | |
331 | outwalk += sprintf(outwalk, "%s", percentagebuf); | |
332 | walk += strlen("percentage"); | |
333 | } else if (strncmp(walk+1, "remaining", strlen("remaining")) == 0) { | |
334 | outwalk += sprintf(outwalk, "%s", remainingbuf); | |
335 | walk += strlen("remaining"); | |
336 | EAT_SPACE_FROM_OUTPUT_IF_EMPTY(remainingbuf); | |
337 | } else if (strncmp(walk+1, "emptytime", strlen("emptytime")) == 0) { | |
338 | outwalk += sprintf(outwalk, "%s", emptytimebuf); | |
339 | walk += strlen("emptytime"); | |
340 | EAT_SPACE_FROM_OUTPUT_IF_EMPTY(emptytimebuf); | |
341 | } else if (strncmp(walk+1, "consumption", strlen("consumption")) == 0) { | |
342 | outwalk += sprintf(outwalk, "%s", consumptionbuf); | |
343 | walk += strlen("consumption"); | |
344 | EAT_SPACE_FROM_OUTPUT_IF_EMPTY(consumptionbuf); | |
345 | } | |
346 | } | |
347 | ||
348 | if (colorful_output) | |
349 | END_COLOR; | |
350 | ||
351 | OUTPUT_FULL_TEXT(buffer); | |
574 | #define EAT_SPACE_FROM_OUTPUT_IF_EMPTY(_buf) \ | |
575 | do { \ | |
576 | if (strlen(_buf) == 0) { \ | |
577 | if (outwalk > buffer && isspace(outwalk[-1])) \ | |
578 | outwalk--; \ | |
579 | else if (isspace(*(walk + 1))) \ | |
580 | walk++; \ | |
581 | } \ | |
582 | } while (0) | |
583 | ||
584 | for (walk = format; *walk != '\0'; walk++) { | |
585 | if (*walk != '%') { | |
586 | *(outwalk++) = *walk; | |
587 | continue; | |
588 | } | |
589 | ||
590 | if (BEGINS_WITH(walk + 1, "status")) { | |
591 | outwalk += sprintf(outwalk, "%s", statusbuf); | |
592 | walk += strlen("status"); | |
593 | } else if (BEGINS_WITH(walk + 1, "percentage")) { | |
594 | outwalk += sprintf(outwalk, "%s", percentagebuf); | |
595 | walk += strlen("percentage"); | |
596 | } else if (BEGINS_WITH(walk + 1, "remaining")) { | |
597 | outwalk += sprintf(outwalk, "%s", remainingbuf); | |
598 | walk += strlen("remaining"); | |
599 | EAT_SPACE_FROM_OUTPUT_IF_EMPTY(remainingbuf); | |
600 | } else if (BEGINS_WITH(walk + 1, "emptytime")) { | |
601 | outwalk += sprintf(outwalk, "%s", emptytimebuf); | |
602 | walk += strlen("emptytime"); | |
603 | EAT_SPACE_FROM_OUTPUT_IF_EMPTY(emptytimebuf); | |
604 | } else if (BEGINS_WITH(walk + 1, "consumption")) { | |
605 | outwalk += sprintf(outwalk, "%s", consumptionbuf); | |
606 | walk += strlen("consumption"); | |
607 | EAT_SPACE_FROM_OUTPUT_IF_EMPTY(consumptionbuf); | |
608 | } | |
609 | } | |
610 | ||
611 | if (colorful_output) | |
612 | END_COLOR; | |
613 | ||
614 | OUTPUT_FULL_TEXT(buffer); | |
352 | 615 | } |
0 | // vim:ts=8:expandtab | |
0 | // vim:ts=4:sw=4:expandtab | |
1 | 1 | #include <stdlib.h> |
2 | 2 | #include <limits.h> |
3 | 3 | #include <stdio.h> |
7 | 7 | |
8 | 8 | #include "i3status.h" |
9 | 9 | |
10 | #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) | |
10 | #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) | |
11 | 11 | #include <err.h> |
12 | 12 | #include <sys/types.h> |
13 | 13 | #include <sys/sysctl.h> |
14 | 14 | #define TZ_ZEROC 2732 |
15 | #define TZ_KELVTOC(x) (((x) - TZ_ZEROC) / 10), abs(((x) - TZ_ZEROC) % 10) | |
16 | #define TZ_AVG(x) ((x) - TZ_ZEROC) / 10 | |
15 | #define TZ_KELVTOC(x) (((x)-TZ_ZEROC) / 10), abs(((x)-TZ_ZEROC) % 10) | |
16 | #define TZ_AVG(x) ((x)-TZ_ZEROC) / 10 | |
17 | #endif | |
18 | ||
19 | #if defined(__DragonFly__) | |
20 | #include <sys/sysctl.h> | |
21 | #include <sys/types.h> | |
22 | #include <sys/sensors.h> | |
23 | #define MUKTOC(v) ((v - 273150000) / 1000000.0) | |
17 | 24 | #endif |
18 | 25 | |
19 | 26 | #if defined(__OpenBSD__) |
35 | 42 | #define MUKTOC(v) ((v - 273150000) / 1000000.0) |
36 | 43 | #endif |
37 | 44 | |
38 | ||
39 | static char *thermal_zone; | |
40 | ||
41 | 45 | /* |
42 | * Reads the CPU temperature from /sys/class/thermal/thermal_zone0/temp and | |
43 | * returns the temperature in degree celcius. | |
46 | * Reads the CPU temperature from /sys/class/thermal/thermal_zone%d/temp (or | |
47 | * the user provided path) and returns the temperature in degree celcius. | |
44 | 48 | * |
45 | 49 | */ |
46 | 50 | void print_cpu_temperature_info(yajl_gen json_gen, char *buffer, int zone, const char *path, const char *format, int max_threshold) { |
47 | char *outwalk = buffer; | |
51 | char *outwalk = buffer; | |
48 | 52 | #ifdef THERMAL_ZONE |
49 | const char *walk; | |
50 | bool colorful_output = false; | |
51 | ||
52 | if (thermal_zone == NULL) { | |
53 | if (path == NULL) | |
54 | asprintf(&thermal_zone, THERMAL_ZONE, zone); | |
55 | else | |
56 | asprintf(&thermal_zone, path, zone); | |
53 | const char *walk; | |
54 | bool colorful_output = false; | |
55 | char *thermal_zone; | |
56 | ||
57 | if (path == NULL) | |
58 | asprintf(&thermal_zone, THERMAL_ZONE, zone); | |
59 | else | |
60 | asprintf(&thermal_zone, path, zone); | |
61 | ||
62 | INSTANCE(thermal_zone); | |
63 | ||
64 | for (walk = format; *walk != '\0'; walk++) { | |
65 | if (*walk != '%') { | |
66 | *(outwalk++) = *walk; | |
67 | continue; | |
57 | 68 | } |
58 | path = thermal_zone; | |
59 | ||
60 | INSTANCE(path); | |
61 | ||
62 | for (walk = format; *walk != '\0'; walk++) { | |
63 | if (*walk != '%') { | |
64 | *(outwalk++) = *walk; | |
65 | continue; | |
66 | } | |
67 | ||
68 | if (BEGINS_WITH(walk+1, "degrees")) { | |
69 | ||
70 | if (BEGINS_WITH(walk + 1, "degrees")) { | |
69 | 71 | #if defined(LINUX) |
70 | static char buf[16]; | |
71 | long int temp; | |
72 | if (!slurp(path, buf, sizeof(buf))) | |
73 | goto error; | |
74 | temp = strtol(buf, NULL, 10); | |
75 | if (temp == LONG_MIN || temp == LONG_MAX || temp <= 0) | |
76 | *(outwalk++) = '?'; | |
77 | else { | |
78 | if ((temp/1000) >= max_threshold) { | |
79 | START_COLOR("color_bad"); | |
80 | colorful_output = true; | |
81 | } | |
82 | outwalk += sprintf(outwalk, "%ld", (temp/1000)); | |
83 | if (colorful_output) { | |
84 | END_COLOR; | |
85 | colorful_output = false; | |
86 | } | |
87 | } | |
88 | #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) | |
89 | int sysctl_rslt; | |
90 | size_t sysctl_size = sizeof(sysctl_rslt); | |
91 | if (sysctlbyname(path, &sysctl_rslt, &sysctl_size, NULL, 0)) | |
92 | goto error; | |
93 | ||
94 | if (TZ_AVG(sysctl_rslt) >= max_threshold) { | |
95 | START_COLOR("color_bad"); | |
96 | colorful_output = true; | |
97 | } | |
98 | outwalk += sprintf(outwalk, "%d.%d", TZ_KELVTOC(sysctl_rslt)); | |
99 | if (colorful_output) { | |
100 | END_COLOR; | |
101 | colorful_output = false; | |
102 | } | |
72 | static char buf[16]; | |
73 | long int temp; | |
74 | if (!slurp(thermal_zone, buf, sizeof(buf))) | |
75 | goto error; | |
76 | temp = strtol(buf, NULL, 10); | |
77 | if (temp == LONG_MIN || temp == LONG_MAX || temp <= 0) | |
78 | *(outwalk++) = '?'; | |
79 | else { | |
80 | if ((temp / 1000) >= max_threshold) { | |
81 | START_COLOR("color_bad"); | |
82 | colorful_output = true; | |
83 | } | |
84 | outwalk += sprintf(outwalk, "%ld", (temp / 1000)); | |
85 | if (colorful_output) { | |
86 | END_COLOR; | |
87 | colorful_output = false; | |
88 | } | |
89 | } | |
90 | #elif defined(__DragonFly__) | |
91 | struct sensor th_sensor; | |
92 | size_t th_sensorlen; | |
93 | ||
94 | th_sensorlen = sizeof(th_sensor); | |
95 | ||
96 | if (sysctlbyname(thermal_zone, &th_sensor, &th_sensorlen, NULL, 0) == -1) { | |
97 | perror("sysctlbyname"); | |
98 | goto error; | |
99 | } | |
100 | if (MUKTOC(th_sensor.value) >= max_threshold) { | |
101 | START_COLOR("color_bad"); | |
102 | colorful_output = true; | |
103 | } | |
104 | outwalk += sprintf(outwalk, "%.2f", MUKTOC(th_sensor.value)); | |
105 | if (colorful_output) { | |
106 | END_COLOR; | |
107 | colorful_output = false; | |
108 | } | |
109 | ||
110 | #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) | |
111 | int sysctl_rslt; | |
112 | size_t sysctl_size = sizeof(sysctl_rslt); | |
113 | if (sysctlbyname(thermal_zone, &sysctl_rslt, &sysctl_size, NULL, 0)) | |
114 | goto error; | |
115 | ||
116 | if (TZ_AVG(sysctl_rslt) >= max_threshold) { | |
117 | START_COLOR("color_bad"); | |
118 | colorful_output = true; | |
119 | } | |
120 | outwalk += sprintf(outwalk, "%d.%d", TZ_KELVTOC(sysctl_rslt)); | |
121 | if (colorful_output) { | |
122 | END_COLOR; | |
123 | colorful_output = false; | |
124 | } | |
103 | 125 | |
104 | 126 | #elif defined(__OpenBSD__) |
105 | struct sensordev sensordev; | |
106 | struct sensor sensor; | |
107 | size_t sdlen, slen; | |
108 | int dev, numt, mib[5] = { CTL_HW, HW_SENSORS, 0, 0, 0 }; | |
109 | ||
110 | sdlen = sizeof(sensordev); | |
111 | slen = sizeof(sensor); | |
112 | ||
113 | for (dev = 0; ; dev++) { | |
127 | struct sensordev sensordev; | |
128 | struct sensor sensor; | |
129 | size_t sdlen, slen; | |
130 | int dev, numt, mib[5] = {CTL_HW, HW_SENSORS, 0, 0, 0}; | |
131 | ||
132 | sdlen = sizeof(sensordev); | |
133 | slen = sizeof(sensor); | |
134 | ||
135 | for (dev = 0;; dev++) { | |
114 | 136 | mib[2] = dev; |
115 | 137 | if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) { |
116 | if (errno == ENXIO) | |
138 | if (errno == ENXIO) | |
139 | continue; | |
140 | if (errno == ENOENT) | |
141 | break; | |
142 | goto error; | |
143 | } | |
144 | /* 'path' is the node within the full path (defaults to acpitz0). */ | |
145 | if (BEGINS_WITH(sensordev.xname, thermal_zone)) { | |
146 | mib[3] = SENSOR_TEMP; | |
147 | /* Limit to temo0, but should retrieve from a full path... */ | |
148 | for (numt = 0; numt < 1 /*sensordev.maxnumt[SENSOR_TEMP]*/; numt++) { | |
149 | mib[4] = numt; | |
150 | if (sysctl(mib, 5, &sensor, &slen, NULL, 0) == -1) { | |
151 | if (errno != ENOENT) { | |
152 | warn("sysctl"); | |
117 | 153 | continue; |
118 | if (errno == ENOENT) | |
119 | break; | |
120 | goto error; | |
121 | } | |
122 | /* 'path' is the node within the full path (defaults to acpitz0). */ | |
123 | if (strncmp(sensordev.xname, path, strlen(path)) == 0) { | |
124 | mib[3] = SENSOR_TEMP; | |
125 | /* Limit to temo0, but should retrieve from a full path... */ | |
126 | for (numt = 0; numt < 1 /*sensordev.maxnumt[SENSOR_TEMP]*/; numt++) { | |
127 | mib[4] = numt; | |
128 | if (sysctl(mib, 5, &sensor, &slen, NULL, 0) == -1) { | |
129 | if (errno != ENOENT) { | |
130 | warn("sysctl"); | |
131 | continue; | |
132 | } | |
133 | } | |
134 | if ((int)MUKTOC(sensor.value) >= max_threshold) { | |
135 | START_COLOR("color_bad"); | |
136 | colorful_output = true; | |
137 | } | |
138 | ||
139 | outwalk += sprintf(outwalk, "%.2f", MUKTOC(sensor.value)); | |
140 | ||
141 | if (colorful_output) { | |
142 | END_COLOR; | |
143 | colorful_output = false; | |
144 | } | |
154 | } | |
145 | 155 | } |
146 | } | |
147 | } | |
156 | if ((int)MUKTOC(sensor.value) >= max_threshold) { | |
157 | START_COLOR("color_bad"); | |
158 | colorful_output = true; | |
159 | } | |
160 | ||
161 | outwalk += sprintf(outwalk, "%.2f", MUKTOC(sensor.value)); | |
162 | ||
163 | if (colorful_output) { | |
164 | END_COLOR; | |
165 | colorful_output = false; | |
166 | } | |
167 | } | |
168 | } | |
169 | } | |
148 | 170 | #elif defined(__NetBSD__) |
149 | int fd, rval; | |
150 | bool err = false; | |
151 | prop_dictionary_t dict; | |
152 | prop_array_t array; | |
153 | prop_object_iterator_t iter; | |
154 | prop_object_iterator_t iter2; | |
155 | prop_object_t obj, obj2, obj3; | |
156 | ||
157 | fd = open("/dev/sysmon", O_RDONLY); | |
158 | if (fd == -1) | |
159 | goto error; | |
160 | ||
161 | rval = prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &dict); | |
162 | if (rval == -1) { | |
163 | err = true; | |
164 | goto error_netbsd1; | |
165 | } | |
166 | ||
167 | /* No drivers registered? */ | |
168 | if (prop_dictionary_count(dict) == 0) { | |
169 | err = true; | |
170 | goto error_netbsd2; | |
171 | } | |
172 | ||
173 | /* print sensors for all devices registered */ | |
174 | iter = prop_dictionary_iterator(dict); | |
175 | if (iter == NULL) { | |
176 | err = true; | |
177 | goto error_netbsd2; | |
178 | } | |
179 | ||
180 | /* iterate over the dictionary returned by the kernel */ | |
181 | while ((obj = prop_object_iterator_next(iter)) != NULL) { | |
171 | int fd, rval; | |
172 | bool err = false; | |
173 | prop_dictionary_t dict; | |
174 | prop_array_t array; | |
175 | prop_object_iterator_t iter; | |
176 | prop_object_iterator_t iter2; | |
177 | prop_object_t obj, obj2, obj3; | |
178 | ||
179 | fd = open("/dev/sysmon", O_RDONLY); | |
180 | if (fd == -1) | |
181 | goto error; | |
182 | ||
183 | rval = prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &dict); | |
184 | if (rval == -1) { | |
185 | err = true; | |
186 | goto error_netbsd1; | |
187 | } | |
188 | ||
189 | /* No drivers registered? */ | |
190 | if (prop_dictionary_count(dict) == 0) { | |
191 | err = true; | |
192 | goto error_netbsd2; | |
193 | } | |
194 | ||
195 | iter = prop_dictionary_iterator(dict); | |
196 | if (iter == NULL) { | |
197 | err = true; | |
198 | goto error_netbsd2; | |
199 | } | |
200 | ||
201 | /* iterate over the dictionary returned by the kernel */ | |
202 | while ((obj = prop_object_iterator_next(iter)) != NULL) { | |
203 | /* skip this dict if it's not what we're looking for */ | |
204 | if ((strlen(prop_dictionary_keysym_cstring_nocopy(obj)) != strlen(thermal_zone)) || | |
205 | (strncmp(thermal_zone, | |
206 | prop_dictionary_keysym_cstring_nocopy(obj), | |
207 | strlen(thermal_zone)) != 0)) | |
208 | continue; | |
209 | ||
182 | 210 | array = prop_dictionary_get_keysym(dict, obj); |
183 | 211 | if (prop_object_type(array) != PROP_TYPE_ARRAY) { |
184 | 212 | err = true; |
185 | 213 | goto error_netbsd3; |
186 | 214 | } |
215 | ||
187 | 216 | iter2 = prop_array_iterator(array); |
188 | 217 | if (!iter2) { |
189 | 218 | err = true; |
190 | 219 | goto error_netbsd3; |
191 | 220 | } |
192 | 221 | |
193 | /* iterate over the array of dictionaries */ | |
222 | /* iterate over array of dicts specific to target sensor */ | |
194 | 223 | while ((obj2 = prop_object_iterator_next(iter2)) != NULL) { |
195 | obj3 = prop_dictionary_get(obj2, "description"); | |
196 | if (obj3 && | |
197 | strcmp(path, prop_string_cstring_nocopy(obj3)) == 0) | |
198 | { | |
199 | obj3 = prop_dictionary_get(obj2, "cur-value"); | |
200 | float temp = MUKTOC(prop_number_integer_value(obj3)); | |
201 | if ((int)temp >= max_threshold) { | |
202 | START_COLOR("color_bad"); | |
203 | colorful_output = true; | |
204 | } | |
205 | ||
206 | outwalk += sprintf(outwalk, "%.2f", temp); | |
207 | ||
208 | if (colorful_output) { | |
209 | END_COLOR; | |
210 | colorful_output = false; | |
211 | } | |
212 | break; | |
213 | } | |
214 | ||
224 | obj3 = prop_dictionary_get(obj2, "cur-value"); | |
225 | ||
226 | float temp = MUKTOC(prop_number_integer_value(obj3)); | |
227 | if ((int)temp >= max_threshold) { | |
228 | START_COLOR("color_bad"); | |
229 | colorful_output = true; | |
230 | } | |
231 | ||
232 | outwalk += sprintf(outwalk, "%.2f", temp); | |
233 | ||
234 | if (colorful_output) { | |
235 | END_COLOR; | |
236 | colorful_output = false; | |
237 | } | |
238 | ||
239 | break; | |
215 | 240 | } |
216 | 241 | prop_object_iterator_release(iter2); |
242 | } | |
243 | error_netbsd3: | |
244 | prop_object_iterator_release(iter); | |
245 | error_netbsd2: | |
246 | prop_object_release(dict); | |
247 | error_netbsd1: | |
248 | close(fd); | |
249 | if (err) | |
250 | goto error; | |
251 | ||
252 | #endif | |
253 | ||
254 | walk += strlen("degrees"); | |
217 | 255 | } |
218 | error_netbsd3: | |
219 | prop_object_iterator_release(iter); | |
220 | error_netbsd2: | |
221 | prop_object_release(dict); | |
222 | error_netbsd1: | |
223 | close(fd); | |
224 | if (err) goto error; | |
225 | ||
226 | #endif | |
227 | ||
228 | ||
229 | walk += strlen("degrees"); | |
230 | } | |
231 | } | |
232 | OUTPUT_FULL_TEXT(buffer); | |
233 | return; | |
256 | } | |
257 | ||
258 | free(thermal_zone); | |
259 | ||
260 | OUTPUT_FULL_TEXT(buffer); | |
261 | return; | |
234 | 262 | error: |
235 | 263 | #endif |
236 | OUTPUT_FULL_TEXT("cant read temp"); | |
237 | (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); | |
264 | free(thermal_zone); | |
265 | ||
266 | OUTPUT_FULL_TEXT("cant read temp"); | |
267 | (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); | |
238 | 268 | } |
0 | // vim:sw=8:sts=8:ts=8:expandtab | |
0 | // vim:ts=4:sw=4:expandtab | |
1 | 1 | #include <stdlib.h> |
2 | 2 | #include <limits.h> |
3 | 3 | #include <stdio.h> |
29 | 29 | #include "i3status.h" |
30 | 30 | |
31 | 31 | static int prev_total = 0; |
32 | static int prev_idle = 0; | |
32 | static int prev_idle = 0; | |
33 | 33 | |
34 | 34 | /* |
35 | 35 | * Reads the CPU utilization from /proc/stat and returns the usage as a |
37 | 37 | * |
38 | 38 | */ |
39 | 39 | void print_cpu_usage(yajl_gen json_gen, char *buffer, const char *format) { |
40 | const char *walk; | |
41 | char *outwalk = buffer; | |
42 | char buf[1024]; | |
43 | int curr_user = 0, curr_nice = 0, curr_system = 0, curr_idle = 0, curr_total; | |
44 | int diff_idle, diff_total, diff_usage; | |
40 | const char *walk; | |
41 | char *outwalk = buffer; | |
42 | char buf[1024]; | |
43 | int curr_user = 0, curr_nice = 0, curr_system = 0, curr_idle = 0, curr_total; | |
44 | int diff_idle, diff_total, diff_usage; | |
45 | 45 | |
46 | 46 | #if defined(LINUX) |
47 | static char statpath[512]; | |
48 | strcpy(statpath, "/proc/stat"); | |
49 | if (!slurp(statpath, buf, sizeof(buf)) || | |
50 | sscanf(buf, "cpu %d %d %d %d", &curr_user, &curr_nice, &curr_system, &curr_idle) != 4) | |
51 | goto error; | |
47 | static char statpath[512]; | |
48 | strcpy(statpath, "/proc/stat"); | |
49 | if (!slurp(statpath, buf, sizeof(buf)) || | |
50 | sscanf(buf, "cpu %d %d %d %d", &curr_user, &curr_nice, &curr_system, &curr_idle) != 4) | |
51 | goto error; | |
52 | 52 | |
53 | curr_total = curr_user + curr_nice + curr_system + curr_idle; | |
54 | diff_idle = curr_idle - prev_idle; | |
55 | diff_total = curr_total - prev_total; | |
56 | diff_usage = (diff_total ? (1000 * (diff_total - diff_idle)/diff_total + 5)/10 : 0); | |
57 | prev_total = curr_total; | |
58 | prev_idle = curr_idle; | |
53 | curr_total = curr_user + curr_nice + curr_system + curr_idle; | |
54 | diff_idle = curr_idle - prev_idle; | |
55 | diff_total = curr_total - prev_total; | |
56 | diff_usage = (diff_total ? (1000 * (diff_total - diff_idle) / diff_total + 5) / 10 : 0); | |
57 | prev_total = curr_total; | |
58 | prev_idle = curr_idle; | |
59 | 59 | #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) |
60 | 60 | |
61 | 61 | #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) |
62 | size_t size; | |
63 | long cp_time[CPUSTATES]; | |
64 | size = sizeof cp_time; | |
65 | if (sysctlbyname("kern.cp_time", &cp_time, &size, NULL, 0) < 0) | |
66 | goto error; | |
62 | size_t size; | |
63 | long cp_time[CPUSTATES]; | |
64 | size = sizeof cp_time; | |
65 | if (sysctlbyname("kern.cp_time", &cp_time, &size, NULL, 0) < 0) | |
66 | goto error; | |
67 | 67 | #else |
68 | /* This information is taken from the boot cpu, any other cpus are currently ignored. */ | |
69 | long cp_time[CPUSTATES]; | |
70 | int mib[2]; | |
71 | size_t size = sizeof(cp_time); | |
68 | /* This information is taken from the boot cpu, any other cpus are currently ignored. */ | |
69 | long cp_time[CPUSTATES]; | |
70 | int mib[2]; | |
71 | size_t size = sizeof(cp_time); | |
72 | 72 | |
73 | mib[0] = CTL_KERN; | |
74 | mib[1] = KERN_CPTIME; | |
73 | mib[0] = CTL_KERN; | |
74 | mib[1] = KERN_CPTIME; | |
75 | 75 | |
76 | if (sysctl(mib, 2, cp_time, &size, NULL, 0)) | |
77 | goto error; | |
76 | if (sysctl(mib, 2, cp_time, &size, NULL, 0)) | |
77 | goto error; | |
78 | 78 | #endif |
79 | 79 | |
80 | curr_user = cp_time[CP_USER]; | |
81 | curr_nice = cp_time[CP_NICE]; | |
82 | curr_system = cp_time[CP_SYS]; | |
83 | curr_idle = cp_time[CP_IDLE]; | |
84 | curr_total = curr_user + curr_nice + curr_system + curr_idle; | |
85 | diff_idle = curr_idle - prev_idle; | |
86 | diff_total = curr_total - prev_total; | |
87 | diff_usage = (diff_total ? (1000 * (diff_total - diff_idle)/diff_total + 5)/10 : 0); | |
88 | prev_total = curr_total; | |
89 | prev_idle = curr_idle; | |
80 | curr_user = cp_time[CP_USER]; | |
81 | curr_nice = cp_time[CP_NICE]; | |
82 | curr_system = cp_time[CP_SYS]; | |
83 | curr_idle = cp_time[CP_IDLE]; | |
84 | curr_total = curr_user + curr_nice + curr_system + curr_idle; | |
85 | diff_idle = curr_idle - prev_idle; | |
86 | diff_total = curr_total - prev_total; | |
87 | diff_usage = (diff_total ? (1000 * (diff_total - diff_idle) / diff_total + 5) / 10 : 0); | |
88 | prev_total = curr_total; | |
89 | prev_idle = curr_idle; | |
90 | 90 | #else |
91 | goto error; | |
91 | goto error; | |
92 | 92 | #endif |
93 | for (walk = format; *walk != '\0'; walk++) { | |
94 | if (*walk != '%') { | |
95 | *(outwalk++) = *walk; | |
96 | continue; | |
97 | } | |
98 | ||
99 | if (strncmp(walk+1, "usage", strlen("usage")) == 0) { | |
100 | outwalk += sprintf(outwalk, "%02d%%", diff_usage); | |
101 | walk += strlen("usage"); | |
102 | } | |
93 | for (walk = format; *walk != '\0'; walk++) { | |
94 | if (*walk != '%') { | |
95 | *(outwalk++) = *walk; | |
96 | continue; | |
103 | 97 | } |
104 | 98 | |
105 | OUTPUT_FULL_TEXT(buffer); | |
106 | return; | |
99 | if (BEGINS_WITH(walk + 1, "usage")) { | |
100 | outwalk += sprintf(outwalk, "%02d%%", diff_usage); | |
101 | walk += strlen("usage"); | |
102 | } | |
103 | } | |
104 | ||
105 | OUTPUT_FULL_TEXT(buffer); | |
106 | return; | |
107 | 107 | error: |
108 | OUTPUT_FULL_TEXT("cant read cpu usage"); | |
109 | (void)fputs("i3status: Cannot read CPU usage\n", stderr); | |
108 | OUTPUT_FULL_TEXT("cant read cpu usage"); | |
109 | (void)fputs("i3status: Cannot read CPU usage\n", stderr); | |
110 | 110 | } |
0 | // vim:ts=8:expandtab | |
0 | // vim:ts=4:sw=4:expandtab | |
1 | 1 | #include <time.h> |
2 | 2 | #include <stdio.h> |
3 | 3 | #include <stdlib.h> |
9 | 9 | |
10 | 10 | /* define fixed output-Strings */ |
11 | 11 | char *season_long[5] = { |
12 | "Chaos", | |
13 | "Discord", | |
14 | "Confusion", | |
15 | "Bureaucracy", | |
16 | "The Aftermath" | |
17 | }; | |
12 | "Chaos", | |
13 | "Discord", | |
14 | "Confusion", | |
15 | "Bureaucracy", | |
16 | "The Aftermath"}; | |
18 | 17 | |
19 | 18 | char *season_short[5] = { |
20 | "Chs", | |
21 | "Dsc", | |
22 | "Cfn", | |
23 | "Bcy", | |
24 | "Afm" | |
25 | }; | |
19 | "Chs", | |
20 | "Dsc", | |
21 | "Cfn", | |
22 | "Bcy", | |
23 | "Afm"}; | |
26 | 24 | |
27 | 25 | char *day_long[5] = { |
28 | "Sweetmorn", | |
29 | "Boomtime", | |
30 | "Pungenday", | |
31 | "Prickle-Prickle", | |
32 | "Setting Orange" | |
33 | }; | |
26 | "Sweetmorn", | |
27 | "Boomtime", | |
28 | "Pungenday", | |
29 | "Prickle-Prickle", | |
30 | "Setting Orange"}; | |
34 | 31 | |
35 | 32 | char *day_short[5] = { |
36 | "SM", | |
37 | "BT", | |
38 | "PD", | |
39 | "PP", | |
40 | "SO" | |
41 | }; | |
33 | "SM", | |
34 | "BT", | |
35 | "PD", | |
36 | "PP", | |
37 | "SO"}; | |
42 | 38 | |
43 | 39 | char *holidays[10] = { |
44 | "Mungday", | |
45 | "Mojoday", | |
46 | "Syaday", | |
47 | "Zaraday", | |
48 | "Maladay", | |
49 | "Chaoflux", | |
50 | "Discoflux", | |
51 | "Confuflux", | |
52 | "Bureflux", | |
53 | "Afflux" | |
54 | }; | |
40 | "Mungday", | |
41 | "Mojoday", | |
42 | "Syaday", | |
43 | "Zaraday", | |
44 | "Maladay", | |
45 | "Chaoflux", | |
46 | "Discoflux", | |
47 | "Confuflux", | |
48 | "Bureflux", | |
49 | "Afflux"}; | |
55 | 50 | |
56 | 51 | /* A helper-struct, taking the discordian date */ |
57 | 52 | struct disc_time { |
58 | int year; | |
59 | int season; | |
60 | int week_day; | |
61 | int season_day; | |
62 | int st_tibs_day; | |
53 | int year; | |
54 | int season; | |
55 | int week_day; | |
56 | int season_day; | |
57 | int st_tibs_day; | |
63 | 58 | }; |
64 | 59 | |
65 | 60 | /* Print the date *dt in format *format */ |
66 | 61 | static int format_output(char *outwalk, char *format, struct disc_time *dt) { |
67 | char *orig_outwalk = outwalk; | |
68 | char *i; | |
69 | char *tibs_end = 0; | |
70 | ||
71 | for (i = format; *i != '\0'; i++) { | |
72 | if (*i != '%') { | |
73 | *(outwalk++) = *i; | |
74 | continue; | |
75 | } | |
76 | switch (*(i+1)) { | |
77 | /* Weekday in long and abbreviation */ | |
78 | case 'A': | |
79 | outwalk += sprintf(outwalk, "%s", day_long[dt->week_day]); | |
80 | break; | |
81 | case 'a': | |
82 | outwalk += sprintf(outwalk, "%s", day_short[dt->week_day]); | |
83 | break; | |
84 | /* Season in long and abbreviation */ | |
85 | case 'B': | |
86 | outwalk += sprintf(outwalk, "%s", season_long[dt->season]); | |
87 | break; | |
88 | case 'b': | |
89 | outwalk += sprintf(outwalk, "%s", season_short[dt->season]); | |
90 | break; | |
91 | /* Day of the season (ordinal and cardinal) */ | |
92 | case 'd': | |
93 | outwalk += sprintf(outwalk, "%d", dt->season_day + 1); | |
94 | break; | |
95 | case 'e': | |
96 | outwalk += sprintf(outwalk, "%d", dt->season_day + 1); | |
97 | if (dt->season_day > 9 && dt->season_day < 13) { | |
98 | outwalk += sprintf(outwalk, "th"); | |
99 | break; | |
100 | } | |
101 | ||
102 | switch (dt->season_day % 10) { | |
103 | case 0: | |
104 | outwalk += sprintf(outwalk, "st"); | |
105 | break; | |
106 | case 1: | |
107 | outwalk += sprintf(outwalk, "nd"); | |
108 | break; | |
109 | case 2: | |
110 | outwalk += sprintf(outwalk, "rd"); | |
111 | break; | |
112 | default: | |
113 | outwalk += sprintf(outwalk, "th"); | |
114 | break; | |
115 | } | |
116 | break; | |
117 | /* YOLD */ | |
118 | case 'Y': | |
119 | outwalk += sprintf(outwalk, "%d", dt->year); | |
120 | break; | |
121 | /* Holidays */ | |
122 | case 'H': | |
123 | if (dt->season_day == 4) { | |
124 | outwalk += sprintf(outwalk, "%s", holidays[dt->season]); | |
125 | } | |
126 | if (dt->season_day == 49) { | |
127 | outwalk += sprintf(outwalk, "%s", holidays[dt->season + 5]); | |
128 | } | |
129 | break; | |
130 | /* Stop parsing the format string, except on Holidays */ | |
131 | case 'N': | |
132 | if (dt->season_day != 4 && dt->season_day != 49) { | |
133 | return (outwalk - orig_outwalk); | |
134 | } | |
135 | break; | |
136 | /* Newline- and Tabbing-characters */ | |
137 | case 'n': | |
138 | outwalk += sprintf(outwalk, "\n"); | |
139 | break; | |
140 | case 't': | |
141 | outwalk += sprintf(outwalk, "\t"); | |
142 | break; | |
143 | /* The St. Tib's Day replacement */ | |
144 | case '{': | |
145 | tibs_end = strstr(i, "%}"); | |
146 | if (tibs_end == NULL) { | |
147 | i++; | |
148 | break; | |
149 | } | |
150 | if (dt->st_tibs_day) { | |
151 | /* We outpt "St. Tib's Day... */ | |
152 | outwalk += sprintf(outwalk, "St. Tib's Day"); | |
153 | } else { | |
154 | /* ...or parse the substring between %{ and %} ... */ | |
155 | *tibs_end = '\0'; | |
156 | outwalk += format_output(outwalk, i + 2, dt); | |
157 | *tibs_end = '%'; | |
158 | } | |
159 | /* ...and continue with the rest */ | |
160 | i = tibs_end; | |
161 | break; | |
162 | case '}': | |
163 | i++; | |
164 | break; | |
165 | default: | |
166 | /* No escape-sequence, so we just skip */ | |
167 | outwalk += sprintf(outwalk, "%%%c", *(i+1)); | |
168 | break; | |
169 | } | |
62 | char *orig_outwalk = outwalk; | |
63 | char *i; | |
64 | char *tibs_end = 0; | |
65 | ||
66 | for (i = format; *i != '\0'; i++) { | |
67 | if (*i != '%') { | |
68 | *(outwalk++) = *i; | |
69 | continue; | |
70 | } | |
71 | switch (*(i + 1)) { | |
72 | /* Weekday in long and abbreviation */ | |
73 | case 'A': | |
74 | outwalk += sprintf(outwalk, "%s", day_long[dt->week_day]); | |
75 | break; | |
76 | case 'a': | |
77 | outwalk += sprintf(outwalk, "%s", day_short[dt->week_day]); | |
78 | break; | |
79 | /* Season in long and abbreviation */ | |
80 | case 'B': | |
81 | outwalk += sprintf(outwalk, "%s", season_long[dt->season]); | |
82 | break; | |
83 | case 'b': | |
84 | outwalk += sprintf(outwalk, "%s", season_short[dt->season]); | |
85 | break; | |
86 | /* Day of the season (ordinal and cardinal) */ | |
87 | case 'd': | |
88 | outwalk += sprintf(outwalk, "%d", dt->season_day + 1); | |
89 | break; | |
90 | case 'e': | |
91 | outwalk += sprintf(outwalk, "%d", dt->season_day + 1); | |
92 | if (dt->season_day > 9 && dt->season_day < 13) { | |
93 | outwalk += sprintf(outwalk, "th"); | |
94 | break; | |
95 | } | |
96 | ||
97 | switch (dt->season_day % 10) { | |
98 | case 0: | |
99 | outwalk += sprintf(outwalk, "st"); | |
100 | break; | |
101 | case 1: | |
102 | outwalk += sprintf(outwalk, "nd"); | |
103 | break; | |
104 | case 2: | |
105 | outwalk += sprintf(outwalk, "rd"); | |
106 | break; | |
107 | default: | |
108 | outwalk += sprintf(outwalk, "th"); | |
109 | break; | |
110 | } | |
111 | break; | |
112 | /* YOLD */ | |
113 | case 'Y': | |
114 | outwalk += sprintf(outwalk, "%d", dt->year); | |
115 | break; | |
116 | /* Holidays */ | |
117 | case 'H': | |
118 | if (dt->season_day == 4) { | |
119 | outwalk += sprintf(outwalk, "%s", holidays[dt->season]); | |
120 | } | |
121 | if (dt->season_day == 49) { | |
122 | outwalk += sprintf(outwalk, "%s", holidays[dt->season + 5]); | |
123 | } | |
124 | break; | |
125 | /* Stop parsing the format string, except on Holidays */ | |
126 | case 'N': | |
127 | if (dt->season_day != 4 && dt->season_day != 49) { | |
128 | return (outwalk - orig_outwalk); | |
129 | } | |
130 | break; | |
131 | /* Newline- and Tabbing-characters */ | |
132 | case 'n': | |
133 | outwalk += sprintf(outwalk, "\n"); | |
134 | break; | |
135 | case 't': | |
136 | outwalk += sprintf(outwalk, "\t"); | |
137 | break; | |
138 | /* The St. Tib's Day replacement */ | |
139 | case '{': | |
140 | tibs_end = strstr(i, "%}"); | |
141 | if (tibs_end == NULL) { | |
142 | i++; | |
143 | break; | |
144 | } | |
145 | if (dt->st_tibs_day) { | |
146 | /* We outpt "St. Tib's Day... */ | |
147 | outwalk += sprintf(outwalk, "St. Tib's Day"); | |
148 | } else { | |
149 | /* ...or parse the substring between %{ and %} ... */ | |
150 | *tibs_end = '\0'; | |
151 | outwalk += format_output(outwalk, i + 2, dt); | |
152 | *tibs_end = '%'; | |
153 | } | |
154 | /* ...and continue with the rest */ | |
155 | i = tibs_end; | |
156 | break; | |
157 | case '}': | |
170 | 158 | i++; |
159 | break; | |
160 | default: | |
161 | /* No escape-sequence, so we just skip */ | |
162 | outwalk += sprintf(outwalk, "%%%c", *(i + 1)); | |
163 | break; | |
171 | 164 | } |
172 | return (outwalk - orig_outwalk); | |
165 | i++; | |
166 | } | |
167 | return (outwalk - orig_outwalk); | |
173 | 168 | } |
174 | 169 | |
175 | 170 | /* Get the current date and convert it to discordian */ |
176 | 171 | struct disc_time *get_ddate(struct tm *current_tm) { |
177 | static struct disc_time dt; | |
178 | ||
179 | if (current_tm == NULL) | |
180 | return NULL; | |
181 | ||
182 | /* We have to know, whether we have to insert St. Tib's Day, so whether it's a leap | |
183 | year in gregorian calendar */ | |
184 | int is_leap_year = !(current_tm->tm_year % 4) && | |
185 | (!(current_tm->tm_year % 400) || current_tm->tm_year % 100); | |
186 | ||
187 | /* If St. Tib's Day has passed, it will be necessary to skip a day. */ | |
188 | int yday = current_tm->tm_yday; | |
189 | ||
190 | if (is_leap_year && yday == 59) { | |
191 | /* On St. Tibs Day we don't have to define a date */ | |
192 | dt.st_tibs_day = 1; | |
193 | } else { | |
194 | dt.st_tibs_day = 0; | |
195 | if (is_leap_year && yday > 59) | |
196 | yday -= 1; | |
197 | ||
198 | dt.season_day = yday % 73; | |
199 | dt.week_day = yday % 5; | |
200 | } | |
201 | dt.year = current_tm->tm_year + 3066; | |
202 | dt.season = yday / 73; | |
203 | return &dt; | |
172 | static struct disc_time dt; | |
173 | ||
174 | if (current_tm == NULL) | |
175 | return NULL; | |
176 | ||
177 | /* We have to know, whether we have to insert St. Tib's Day, so whether it's a leap | |
178 | * year in gregorian calendar */ | |
179 | int is_leap_year = !(current_tm->tm_year % 4) && | |
180 | (!(current_tm->tm_year % 400) || current_tm->tm_year % 100); | |
181 | ||
182 | /* If St. Tib's Day has passed, it will be necessary to skip a day. */ | |
183 | int yday = current_tm->tm_yday; | |
184 | ||
185 | if (is_leap_year && yday == 59) { | |
186 | /* On St. Tibs Day we don't have to define a date */ | |
187 | dt.st_tibs_day = 1; | |
188 | } else { | |
189 | dt.st_tibs_day = 0; | |
190 | if (is_leap_year && yday > 59) | |
191 | yday -= 1; | |
192 | ||
193 | dt.season_day = yday % 73; | |
194 | dt.week_day = yday % 5; | |
195 | } | |
196 | dt.year = current_tm->tm_year + 3066; | |
197 | dt.season = yday / 73; | |
198 | return &dt; | |
204 | 199 | } |
205 | 200 | |
206 | 201 | void print_ddate(yajl_gen json_gen, char *buffer, const char *format, time_t t) { |
207 | char *outwalk = buffer; | |
208 | static char *form = NULL; | |
209 | struct tm current_tm; | |
210 | struct disc_time *dt; | |
211 | set_timezone(NULL); /* Use local time. */ | |
212 | localtime_r(&t, ¤t_tm); | |
213 | if ((dt = get_ddate(¤t_tm)) == NULL) | |
214 | return; | |
215 | if (form == NULL) | |
216 | if ((form = malloc(strlen(format) + 1)) == NULL) | |
217 | return; | |
218 | strcpy(form, format); | |
219 | outwalk += format_output(outwalk, form, dt); | |
220 | OUTPUT_FULL_TEXT(buffer); | |
202 | char *outwalk = buffer; | |
203 | static char *form = NULL; | |
204 | struct tm current_tm; | |
205 | struct disc_time *dt; | |
206 | set_timezone(NULL); /* Use local time. */ | |
207 | localtime_r(&t, ¤t_tm); | |
208 | if ((dt = get_ddate(¤t_tm)) == NULL) | |
209 | return; | |
210 | if (form == NULL) | |
211 | if ((form = malloc(strlen(format) + 1)) == NULL) | |
212 | return; | |
213 | strcpy(form, format); | |
214 | outwalk += format_output(outwalk, form, dt); | |
215 | OUTPUT_FULL_TEXT(buffer); | |
221 | 216 | } |
0 | // vim:ts=8:expandtab | |
0 | // vim:ts=4:sw=4:expandtab | |
1 | 1 | #include <stdio.h> |
2 | 2 | #include <stdlib.h> |
3 | 3 | #include <string.h> |
4 | 4 | #include <ctype.h> |
5 | #include <mntent.h> | |
5 | 6 | #include <stdint.h> |
7 | #include <sys/stat.h> | |
6 | 8 | #include <sys/statvfs.h> |
7 | 9 | #include <sys/types.h> |
8 | 10 | #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || (__OpenBSD__) || defined(__DragonFly__) |
19 | 21 | |
20 | 22 | #define MAX_EXPONENT 4 |
21 | 23 | |
22 | static const char * const iec_symbols[MAX_EXPONENT+1] = {"", "Ki", "Mi", "Gi", "Ti"}; | |
23 | static const char * const si_symbols[MAX_EXPONENT+1] = {"", "k", "M", "G", "T"}; | |
24 | static const char * const custom_symbols[MAX_EXPONENT+1] = {"", "K", "M", "G", "T"}; | |
24 | static const char *const iec_symbols[MAX_EXPONENT + 1] = {"", "Ki", "Mi", "Gi", "Ti"}; | |
25 | static const char *const si_symbols[MAX_EXPONENT + 1] = {"", "k", "M", "G", "T"}; | |
26 | static const char *const custom_symbols[MAX_EXPONENT + 1] = {"", "K", "M", "G", "T"}; | |
25 | 27 | |
26 | 28 | /* |
27 | 29 | * Formats bytes according to the given base and set of symbols. |
28 | 30 | * |
29 | 31 | */ |
30 | static int format_bytes(char *outwalk, uint64_t bytes, uint64_t base, const char * const symbols[]) { | |
31 | double size = bytes; | |
32 | int exponent = 0; | |
33 | while (size >= base && exponent < MAX_EXPONENT) { | |
34 | size /= base; | |
35 | exponent += 1; | |
36 | } | |
37 | return sprintf(outwalk, "%.1f %sB", size, symbols[exponent]); | |
32 | static int format_bytes(char *outwalk, uint64_t bytes, uint64_t base, const char *const symbols[]) { | |
33 | double size = bytes; | |
34 | int exponent = 0; | |
35 | while (size >= base && exponent < MAX_EXPONENT) { | |
36 | size /= base; | |
37 | exponent += 1; | |
38 | } | |
39 | return sprintf(outwalk, "%.1f %sB", size, symbols[exponent]); | |
38 | 40 | } |
39 | 41 | |
40 | 42 | /* |
42 | 44 | * |
43 | 45 | */ |
44 | 46 | static int print_bytes_human(char *outwalk, uint64_t bytes, const char *prefix_type) { |
45 | if (strncmp(prefix_type, "decimal", strlen(prefix_type)) == 0) { | |
46 | return format_bytes(outwalk, bytes, DECIMAL_BASE, si_symbols); | |
47 | } else if (strncmp(prefix_type, "custom", strlen(prefix_type)) == 0) { | |
48 | return format_bytes(outwalk, bytes, BINARY_BASE, custom_symbols); | |
49 | } else { | |
50 | return format_bytes(outwalk, bytes, BINARY_BASE, iec_symbols); | |
51 | } | |
47 | if (strcasecmp(prefix_type, "decimal") == 0) { | |
48 | return format_bytes(outwalk, bytes, DECIMAL_BASE, si_symbols); | |
49 | } else if (strcasecmp(prefix_type, "custom") == 0) { | |
50 | return format_bytes(outwalk, bytes, BINARY_BASE, custom_symbols); | |
51 | } else { | |
52 | return format_bytes(outwalk, bytes, BINARY_BASE, iec_symbols); | |
53 | } | |
54 | } | |
55 | ||
56 | /* | |
57 | * Determines whether remaining bytes are below given threshold. | |
58 | * | |
59 | */ | |
60 | #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) | |
61 | static bool below_threshold(struct statfs buf, const char *prefix_type, const char *threshold_type, const double low_threshold) { | |
62 | #else | |
63 | static bool below_threshold(struct statvfs buf, const char *prefix_type, const char *threshold_type, const double low_threshold) { | |
64 | #endif | |
65 | if (strcasecmp(threshold_type, "percentage_free") == 0) { | |
66 | return 100.0 * (double)buf.f_bfree / (double)buf.f_blocks < low_threshold; | |
67 | } else if (strcasecmp(threshold_type, "percentage_avail") == 0) { | |
68 | return 100.0 * (double)buf.f_bavail / (double)buf.f_blocks < low_threshold; | |
69 | } else if (strcasecmp(threshold_type, "bytes_free") == 0) { | |
70 | return (double)buf.f_bsize * (double)buf.f_bfree < low_threshold; | |
71 | } else if (strcasecmp(threshold_type, "bytes_avail") == 0) { | |
72 | return (double)buf.f_bsize * (double)buf.f_bavail < low_threshold; | |
73 | } else if (threshold_type[0] != '\0' && strncasecmp(threshold_type + 1, "bytes_", strlen("bytes_")) == 0) { | |
74 | uint64_t base = strcasecmp(prefix_type, "decimal") == 0 ? DECIMAL_BASE : BINARY_BASE; | |
75 | double factor = 1; | |
76 | ||
77 | switch (threshold_type[0]) { | |
78 | case 'T': | |
79 | case 't': | |
80 | factor *= base; | |
81 | case 'G': | |
82 | case 'g': | |
83 | factor *= base; | |
84 | case 'M': | |
85 | case 'm': | |
86 | factor *= base; | |
87 | case 'K': | |
88 | case 'k': | |
89 | factor *= base; | |
90 | break; | |
91 | default: | |
92 | return false; | |
93 | } | |
94 | ||
95 | if (strcasecmp(threshold_type + 1, "bytes_free") == 0) { | |
96 | return (double)buf.f_bsize * (double)buf.f_bfree < low_threshold * factor; | |
97 | } else if (strcasecmp(threshold_type + 1, "bytes_avail") == 0) { | |
98 | return (double)buf.f_bsize * (double)buf.f_bavail < low_threshold * factor; | |
99 | } | |
100 | } | |
101 | ||
102 | return false; | |
52 | 103 | } |
53 | 104 | |
54 | 105 | /* |
56 | 107 | * human readable manner. |
57 | 108 | * |
58 | 109 | */ |
59 | void print_disk_info(yajl_gen json_gen, char *buffer, const char *path, const char *format, const char *prefix_type) { | |
60 | const char *walk; | |
61 | char *outwalk = buffer; | |
62 | ||
63 | INSTANCE(path); | |
110 | void print_disk_info(yajl_gen json_gen, char *buffer, const char *path, const char *format, const char *format_not_mounted, const char *prefix_type, const char *threshold_type, const double low_threshold) { | |
111 | const char *walk; | |
112 | char *outwalk = buffer; | |
113 | bool colorful_output = false; | |
114 | ||
115 | INSTANCE(path); | |
64 | 116 | |
65 | 117 | #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__DragonFly__) |
66 | struct statfs buf; | |
67 | ||
68 | if (statfs(path, &buf) == -1) | |
69 | return; | |
118 | struct statfs buf; | |
119 | ||
120 | if (statfs(path, &buf) == -1) | |
121 | return; | |
70 | 122 | #else |
71 | struct statvfs buf; | |
72 | ||
73 | if (statvfs(path, &buf) == -1) | |
74 | return; | |
123 | struct statvfs buf; | |
124 | ||
125 | if (statvfs(path, &buf) == -1) | |
126 | return; | |
127 | ||
128 | if (format_not_mounted != NULL) { | |
129 | FILE *mntentfile = setmntent("/etc/mtab", "r"); | |
130 | struct mntent *m; | |
131 | bool found = false; | |
132 | ||
133 | while ((m = getmntent(mntentfile)) != NULL) { | |
134 | if (strcmp(m->mnt_dir, path) == 0) { | |
135 | found = true; | |
136 | break; | |
137 | } | |
138 | } | |
139 | endmntent(mntentfile); | |
140 | ||
141 | if (!found) { | |
142 | format = format_not_mounted; | |
143 | } | |
144 | } | |
75 | 145 | #endif |
76 | 146 | |
77 | for (walk = format; *walk != '\0'; walk++) { | |
78 | if (*walk != '%') { | |
79 | *(outwalk++) = *walk; | |
80 | continue; | |
81 | } | |
82 | ||
83 | if (BEGINS_WITH(walk+1, "free")) { | |
84 | outwalk += print_bytes_human(outwalk, (uint64_t)buf.f_bsize * (uint64_t)buf.f_bfree, prefix_type); | |
85 | walk += strlen("free"); | |
86 | } | |
87 | ||
88 | if (BEGINS_WITH(walk+1, "used")) { | |
89 | outwalk += print_bytes_human(outwalk, (uint64_t)buf.f_bsize * ((uint64_t)buf.f_blocks - (uint64_t)buf.f_bfree), prefix_type); | |
90 | walk += strlen("used"); | |
91 | } | |
92 | ||
93 | if (BEGINS_WITH(walk+1, "total")) { | |
94 | outwalk += print_bytes_human(outwalk, (uint64_t)buf.f_bsize * (uint64_t)buf.f_blocks, prefix_type); | |
95 | walk += strlen("total"); | |
96 | } | |
97 | ||
98 | if (BEGINS_WITH(walk+1, "avail")) { | |
99 | outwalk += print_bytes_human(outwalk, (uint64_t)buf.f_bsize * (uint64_t)buf.f_bavail, prefix_type); | |
100 | walk += strlen("avail"); | |
101 | } | |
102 | ||
103 | if (BEGINS_WITH(walk+1, "percentage_free")) { | |
104 | outwalk += sprintf(outwalk, "%.01f%%", 100.0 * (double)buf.f_bfree / (double)buf.f_blocks); | |
105 | walk += strlen("percentage_free"); | |
106 | } | |
107 | ||
108 | if (BEGINS_WITH(walk+1, "percentage_used_of_avail")) { | |
109 | outwalk += sprintf(outwalk, "%.01f%%", 100.0 * (double)(buf.f_blocks - buf.f_bavail) / (double)buf.f_blocks); | |
110 | walk += strlen("percentage_used_of_avail"); | |
111 | } | |
112 | ||
113 | if (BEGINS_WITH(walk+1, "percentage_used")) { | |
114 | outwalk += sprintf(outwalk, "%.01f%%", 100.0 * (double)(buf.f_blocks - buf.f_bfree) / (double)buf.f_blocks); | |
115 | walk += strlen("percentage_used"); | |
116 | } | |
117 | ||
118 | if (BEGINS_WITH(walk+1, "percentage_avail")) { | |
119 | outwalk += sprintf(outwalk, "%.01f%%", 100.0 * (double)buf.f_bavail / (double)buf.f_blocks); | |
120 | walk += strlen("percentage_avail"); | |
121 | } | |
122 | } | |
123 | ||
124 | *outwalk = '\0'; | |
125 | OUTPUT_FULL_TEXT(buffer); | |
126 | } | |
147 | if (low_threshold > 0 && below_threshold(buf, prefix_type, threshold_type, low_threshold)) { | |
148 | START_COLOR("color_bad"); | |
149 | colorful_output = true; | |
150 | } | |
151 | ||
152 | for (walk = format; *walk != '\0'; walk++) { | |
153 | if (*walk != '%') { | |
154 | *(outwalk++) = *walk; | |
155 | continue; | |
156 | } | |
157 | ||
158 | if (BEGINS_WITH(walk + 1, "free")) { | |
159 | outwalk += print_bytes_human(outwalk, (uint64_t)buf.f_bsize * (uint64_t)buf.f_bfree, prefix_type); | |
160 | walk += strlen("free"); | |
161 | } | |
162 | ||
163 | if (BEGINS_WITH(walk + 1, "used")) { | |
164 | outwalk += print_bytes_human(outwalk, (uint64_t)buf.f_bsize * ((uint64_t)buf.f_blocks - (uint64_t)buf.f_bfree), prefix_type); | |
165 | walk += strlen("used"); | |
166 | } | |
167 | ||
168 | if (BEGINS_WITH(walk + 1, "total")) { | |
169 | outwalk += print_bytes_human(outwalk, (uint64_t)buf.f_bsize * (uint64_t)buf.f_blocks, prefix_type); | |
170 | walk += strlen("total"); | |
171 | } | |
172 | ||
173 | if (BEGINS_WITH(walk + 1, "avail")) { | |
174 | outwalk += print_bytes_human(outwalk, (uint64_t)buf.f_bsize * (uint64_t)buf.f_bavail, prefix_type); | |
175 | walk += strlen("avail"); | |
176 | } | |
177 | ||
178 | if (BEGINS_WITH(walk + 1, "percentage_free")) { | |
179 | outwalk += sprintf(outwalk, "%.01f%%", 100.0 * (double)buf.f_bfree / (double)buf.f_blocks); | |
180 | walk += strlen("percentage_free"); | |
181 | } | |
182 | ||
183 | if (BEGINS_WITH(walk + 1, "percentage_used_of_avail")) { | |
184 | outwalk += sprintf(outwalk, "%.01f%%", 100.0 * (double)(buf.f_blocks - buf.f_bavail) / (double)buf.f_blocks); | |
185 | walk += strlen("percentage_used_of_avail"); | |
186 | } | |
187 | ||
188 | if (BEGINS_WITH(walk + 1, "percentage_used")) { | |
189 | outwalk += sprintf(outwalk, "%.01f%%", 100.0 * (double)(buf.f_blocks - buf.f_bfree) / (double)buf.f_blocks); | |
190 | walk += strlen("percentage_used"); | |
191 | } | |
192 | ||
193 | if (BEGINS_WITH(walk + 1, "percentage_avail")) { | |
194 | outwalk += sprintf(outwalk, "%.01f%%", 100.0 * (double)buf.f_bavail / (double)buf.f_blocks); | |
195 | walk += strlen("percentage_avail"); | |
196 | } | |
197 | } | |
198 | ||
199 | if (colorful_output) | |
200 | END_COLOR; | |
201 | ||
202 | *outwalk = '\0'; | |
203 | OUTPUT_FULL_TEXT(buffer); | |
204 | } |
0 | // vim:ts=8:expandtab | |
0 | // vim:ts=4:sw=4:expandtab | |
1 | 1 | #include <string.h> |
2 | 2 | #include <limits.h> |
3 | 3 | #include <stdio.h> |
15 | 15 | #if defined(LINUX) |
16 | 16 | #include <linux/ethtool.h> |
17 | 17 | #include <linux/sockios.h> |
18 | #define PART_ETHSPEED "E: %s (%d Mbit/s)" | |
18 | #define PART_ETHSPEED "E: %s (%d Mbit/s)" | |
19 | 19 | #endif |
20 | 20 | |
21 | 21 | #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) |
22 | 22 | #include <net/if_media.h> |
23 | #define IFM_TYPE_MATCH(dt, t) \ | |
24 | (IFM_TYPE((dt)) == 0 || IFM_TYPE((dt)) == IFM_TYPE((t))) | |
23 | #define IFM_TYPE_MATCH(dt, t) \ | |
24 | (IFM_TYPE((dt)) == 0 || IFM_TYPE((dt)) == IFM_TYPE((t))) | |
25 | 25 | |
26 | #define PART_ETHSPEED "E: %s (%s)" | |
26 | #define PART_ETHSPEED "E: %s (%s)" | |
27 | 27 | #endif |
28 | 28 | |
29 | 29 | #if defined(__OpenBSD__) || defined(__NetBSD__) |
33 | 33 | |
34 | 34 | static int print_eth_speed(char *outwalk, const char *interface) { |
35 | 35 | #if defined(LINUX) |
36 | /* This code path requires root privileges */ | |
37 | int ethspeed = 0; | |
38 | struct ifreq ifr; | |
39 | struct ethtool_cmd ecmd; | |
36 | /* This code path requires root privileges */ | |
37 | int ethspeed = 0; | |
38 | struct ifreq ifr; | |
39 | struct ethtool_cmd ecmd; | |
40 | 40 | |
41 | ecmd.cmd = ETHTOOL_GSET; | |
42 | (void)memset(&ifr, 0, sizeof(ifr)); | |
43 | ifr.ifr_data = (caddr_t)&ecmd; | |
44 | (void)strcpy(ifr.ifr_name, interface); | |
45 | if (ioctl(general_socket, SIOCETHTOOL, &ifr) == 0) { | |
46 | ethspeed = (ecmd.speed == USHRT_MAX ? 0 : ecmd.speed); | |
47 | return sprintf(outwalk, "%d Mbit/s", ethspeed); | |
48 | } else return sprintf(outwalk, "?"); | |
41 | ecmd.cmd = ETHTOOL_GSET; | |
42 | (void)memset(&ifr, 0, sizeof(ifr)); | |
43 | ifr.ifr_data = (caddr_t)&ecmd; | |
44 | (void)strcpy(ifr.ifr_name, interface); | |
45 | if (ioctl(general_socket, SIOCETHTOOL, &ifr) == 0) { | |
46 | ethspeed = (ecmd.speed == USHRT_MAX ? 0 : ecmd.speed); | |
47 | return sprintf(outwalk, "%d Mbit/s", ethspeed); | |
48 | } else | |
49 | return sprintf(outwalk, "?"); | |
49 | 50 | #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) |
50 | char *ethspeed; | |
51 | struct ifmediareq ifm; | |
52 | (void)memset(&ifm, 0, sizeof(ifm)); | |
53 | (void)strncpy(ifm.ifm_name, interface, sizeof(ifm.ifm_name)); | |
54 | int ret = ioctl(general_socket, SIOCGIFMEDIA, (caddr_t)&ifm); | |
51 | char *ethspeed; | |
52 | struct ifmediareq ifm; | |
53 | (void)memset(&ifm, 0, sizeof(ifm)); | |
54 | (void)strncpy(ifm.ifm_name, interface, sizeof(ifm.ifm_name)); | |
55 | int ret = ioctl(general_socket, SIOCGIFMEDIA, (caddr_t)&ifm); | |
55 | 56 | |
56 | /* Get the description of the media type, partially taken from | |
57 | * FreeBSD's ifconfig */ | |
58 | const struct ifmedia_description *desc; | |
59 | struct ifmedia_description ifm_subtype_descriptions[] = | |
60 | IFM_SUBTYPE_ETHERNET_DESCRIPTIONS; | |
57 | /* Get the description of the media type, partially taken from | |
58 | * FreeBSD's ifconfig */ | |
59 | const struct ifmedia_description *desc; | |
60 | struct ifmedia_description ifm_subtype_descriptions[] = | |
61 | IFM_SUBTYPE_ETHERNET_DESCRIPTIONS; | |
61 | 62 | |
62 | for (desc = ifm_subtype_descriptions; | |
63 | desc->ifmt_string != NULL; | |
64 | desc++) { | |
65 | if (IFM_TYPE_MATCH(desc->ifmt_word, ifm.ifm_active) && | |
66 | IFM_SUBTYPE(desc->ifmt_word) == IFM_SUBTYPE(ifm.ifm_active)) | |
67 | break; | |
68 | } | |
69 | ethspeed = (desc->ifmt_string != NULL ? desc->ifmt_string : "?"); | |
70 | return sprintf(outwalk, "%s", ethspeed); | |
63 | for (desc = ifm_subtype_descriptions; | |
64 | desc->ifmt_string != NULL; | |
65 | desc++) { | |
66 | if (IFM_TYPE_MATCH(desc->ifmt_word, ifm.ifm_active) && | |
67 | IFM_SUBTYPE(desc->ifmt_word) == IFM_SUBTYPE(ifm.ifm_active)) | |
68 | break; | |
69 | } | |
70 | ethspeed = (desc->ifmt_string != NULL ? desc->ifmt_string : "?"); | |
71 | return sprintf(outwalk, "%s", ethspeed); | |
71 | 72 | #elif defined(__OpenBSD__) || defined(__NetBSD__) |
72 | char *ethspeed; | |
73 | struct ifmediareq ifmr; | |
73 | char *ethspeed; | |
74 | struct ifmediareq ifmr; | |
74 | 75 | |
75 | (void) memset(&ifmr, 0, sizeof(ifmr)); | |
76 | (void) strlcpy(ifmr.ifm_name, interface, sizeof(ifmr.ifm_name)); | |
76 | (void)memset(&ifmr, 0, sizeof(ifmr)); | |
77 | (void)strlcpy(ifmr.ifm_name, interface, sizeof(ifmr.ifm_name)); | |
77 | 78 | |
78 | if (ioctl(general_socket, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { | |
79 | if (errno != E2BIG) | |
80 | return sprintf(outwalk, "?"); | |
81 | } | |
79 | if (ioctl(general_socket, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { | |
80 | if (errno != E2BIG) | |
81 | return sprintf(outwalk, "?"); | |
82 | } | |
82 | 83 | |
83 | struct ifmedia_description *desc; | |
84 | struct ifmedia_description ifm_subtype_descriptions[] = | |
85 | IFM_SUBTYPE_DESCRIPTIONS; | |
84 | struct ifmedia_description *desc; | |
85 | struct ifmedia_description ifm_subtype_descriptions[] = | |
86 | IFM_SUBTYPE_DESCRIPTIONS; | |
86 | 87 | |
87 | for (desc = ifm_subtype_descriptions; desc->ifmt_string != NULL; desc++) { | |
88 | /* | |
88 | for (desc = ifm_subtype_descriptions; desc->ifmt_string != NULL; desc++) { | |
89 | /* | |
89 | 90 | * Skip these non-informative values and go right ahead to the |
90 | 91 | * actual speeds. |
91 | 92 | */ |
92 | if (strncmp(desc->ifmt_string, "autoselect", strlen("autoselect")) == 0 || | |
93 | strncmp(desc->ifmt_string, "auto", strlen("auto")) == 0) | |
94 | continue; | |
93 | if (BEGINS_WITH(desc->ifmt_string, "autoselect") || | |
94 | BEGINS_WITH(desc->ifmt_string, "auto")) | |
95 | continue; | |
95 | 96 | |
96 | if (IFM_TYPE_MATCH(desc->ifmt_word, ifmr.ifm_active) && | |
97 | IFM_SUBTYPE(desc->ifmt_word) == IFM_SUBTYPE(ifmr.ifm_active)) | |
98 | break; | |
99 | } | |
100 | ethspeed = (desc->ifmt_string != NULL ? desc->ifmt_string : "?"); | |
101 | return sprintf(outwalk, "%s", ethspeed); | |
97 | if (IFM_TYPE_MATCH(desc->ifmt_word, ifmr.ifm_active) && | |
98 | IFM_SUBTYPE(desc->ifmt_word) == IFM_SUBTYPE(ifmr.ifm_active)) | |
99 | break; | |
100 | } | |
101 | ethspeed = (desc->ifmt_string != NULL ? desc->ifmt_string : "?"); | |
102 | return sprintf(outwalk, "%s", ethspeed); | |
102 | 103 | |
103 | 104 | #else |
104 | return sprintf(outwalk, "?"); | |
105 | return sprintf(outwalk, "?"); | |
105 | 106 | #endif |
106 | 107 | } |
107 | 108 | |
110 | 111 | * |
111 | 112 | */ |
112 | 113 | void print_eth_info(yajl_gen json_gen, char *buffer, const char *interface, const char *format_up, const char *format_down) { |
113 | const char *walk; | |
114 | const char *ip_address = get_ip_addr(interface); | |
115 | char *outwalk = buffer; | |
114 | const char *walk; | |
115 | const char *ip_address = get_ip_addr(interface); | |
116 | char *outwalk = buffer; | |
116 | 117 | |
117 | INSTANCE(interface); | |
118 | INSTANCE(interface); | |
118 | 119 | |
119 | if (ip_address == NULL) { | |
120 | START_COLOR("color_bad"); | |
121 | outwalk += sprintf(outwalk, "%s", format_down); | |
122 | goto out; | |
120 | if (ip_address == NULL) { | |
121 | START_COLOR("color_bad"); | |
122 | outwalk += sprintf(outwalk, "%s", format_down); | |
123 | goto out; | |
124 | } | |
125 | ||
126 | START_COLOR("color_good"); | |
127 | ||
128 | for (walk = format_up; *walk != '\0'; walk++) { | |
129 | if (*walk != '%') { | |
130 | *(outwalk++) = *walk; | |
131 | continue; | |
123 | 132 | } |
124 | 133 | |
125 | START_COLOR("color_good"); | |
126 | ||
127 | for (walk = format_up; *walk != '\0'; walk++) { | |
128 | if (*walk != '%') { | |
129 | *(outwalk++) = *walk; | |
130 | continue; | |
131 | } | |
132 | ||
133 | if (strncmp(walk+1, "ip", strlen("ip")) == 0) { | |
134 | outwalk += sprintf(outwalk, "%s", ip_address); | |
135 | walk += strlen("ip"); | |
136 | } else if (strncmp(walk+1, "speed", strlen("speed")) == 0) { | |
137 | outwalk += print_eth_speed(outwalk, interface); | |
138 | walk += strlen("speed"); | |
139 | } | |
134 | if (BEGINS_WITH(walk + 1, "ip")) { | |
135 | outwalk += sprintf(outwalk, "%s", ip_address); | |
136 | walk += strlen("ip"); | |
137 | } else if (BEGINS_WITH(walk + 1, "speed")) { | |
138 | outwalk += print_eth_speed(outwalk, interface); | |
139 | walk += strlen("speed"); | |
140 | 140 | } |
141 | } | |
141 | 142 | |
142 | 143 | out: |
143 | END_COLOR; | |
144 | OUTPUT_FULL_TEXT(buffer); | |
144 | END_COLOR; | |
145 | OUTPUT_FULL_TEXT(buffer); | |
145 | 146 | } |
0 | // vim:ts=8:expandtab | |
0 | // vim:ts=4:sw=4:expandtab | |
1 | 1 | #include <sys/types.h> |
2 | 2 | #include <sys/socket.h> |
3 | 3 | #include <netinet/in.h> |
17 | 17 | * |
18 | 18 | */ |
19 | 19 | const char *get_ip_addr(const char *interface) { |
20 | static char part[512]; | |
21 | socklen_t len = sizeof(struct sockaddr_in); | |
22 | memset(part, 0, sizeof(part)); | |
20 | static char part[512]; | |
21 | socklen_t len = sizeof(struct sockaddr_in); | |
22 | memset(part, 0, sizeof(part)); | |
23 | 23 | |
24 | struct ifaddrs *ifaddr, *addrp; | |
25 | bool found = false; | |
24 | struct ifaddrs *ifaddr, *addrp; | |
25 | bool found = false; | |
26 | 26 | |
27 | getifaddrs(&ifaddr); | |
27 | getifaddrs(&ifaddr); | |
28 | 28 | |
29 | if (ifaddr == NULL) | |
30 | return NULL; | |
29 | if (ifaddr == NULL) | |
30 | return NULL; | |
31 | 31 | |
32 | /* Skip until we are at the AF_INET address of interface */ | |
33 | for (addrp = ifaddr; | |
32 | /* Skip until we are at the AF_INET address of interface */ | |
33 | for (addrp = ifaddr; | |
34 | 34 | |
35 | (addrp != NULL && | |
36 | (strcmp(addrp->ifa_name, interface) != 0 || | |
37 | addrp->ifa_addr == NULL || | |
38 | addrp->ifa_addr->sa_family != AF_INET)); | |
35 | (addrp != NULL && | |
36 | (strcmp(addrp->ifa_name, interface) != 0 || | |
37 | addrp->ifa_addr == NULL || | |
38 | addrp->ifa_addr->sa_family != AF_INET)); | |
39 | 39 | |
40 | addrp = addrp->ifa_next) { | |
41 | /* Check if the interface is down */ | |
42 | if (strcmp(addrp->ifa_name, interface) != 0) | |
43 | continue; | |
44 | found = true; | |
45 | if ((addrp->ifa_flags & IFF_RUNNING) == 0) { | |
46 | freeifaddrs(ifaddr); | |
47 | return NULL; | |
48 | } | |
40 | addrp = addrp->ifa_next) { | |
41 | /* Check if the interface is down */ | |
42 | if (strcmp(addrp->ifa_name, interface) != 0) | |
43 | continue; | |
44 | found = true; | |
45 | if ((addrp->ifa_flags & IFF_RUNNING) == 0) { | |
46 | freeifaddrs(ifaddr); | |
47 | return NULL; | |
49 | 48 | } |
49 | } | |
50 | 50 | |
51 | if (addrp == NULL) { | |
52 | freeifaddrs(ifaddr); | |
53 | return (found ? "no IP" : NULL); | |
54 | } | |
51 | if (addrp == NULL) { | |
52 | freeifaddrs(ifaddr); | |
53 | return (found ? "no IP" : NULL); | |
54 | } | |
55 | 55 | |
56 | int ret; | |
57 | if ((ret = getnameinfo(addrp->ifa_addr, len, part, sizeof(part), NULL, 0, NI_NUMERICHOST)) != 0) { | |
58 | fprintf(stderr, "i3status: getnameinfo(): %s\n", gai_strerror(ret)); | |
59 | freeifaddrs(ifaddr); | |
60 | return "no IP"; | |
61 | } | |
56 | int ret; | |
57 | if ((ret = getnameinfo(addrp->ifa_addr, len, part, sizeof(part), NULL, 0, NI_NUMERICHOST)) != 0) { | |
58 | fprintf(stderr, "i3status: getnameinfo(): %s\n", gai_strerror(ret)); | |
59 | freeifaddrs(ifaddr); | |
60 | return "no IP"; | |
61 | } | |
62 | 62 | |
63 | freeifaddrs(ifaddr); | |
64 | return part; | |
63 | freeifaddrs(ifaddr); | |
64 | return part; | |
65 | 65 | } |
66 |
0 | // vim:ts=8:expandtab | |
0 | // vim:ts=4:sw=4:expandtab | |
1 | 1 | #include <sys/types.h> |
2 | 2 | #include <sys/socket.h> |
3 | 3 | #include <netinet/in.h> |
15 | 15 | #include "i3status.h" |
16 | 16 | |
17 | 17 | static char *get_sockname(struct addrinfo *addr) { |
18 | static char buf[INET6_ADDRSTRLEN+1]; | |
19 | struct sockaddr_storage local; | |
20 | int ret; | |
21 | int fd; | |
18 | static char buf[INET6_ADDRSTRLEN + 1]; | |
19 | struct sockaddr_storage local; | |
20 | int ret; | |
21 | int fd; | |
22 | 22 | |
23 | if ((fd = socket(addr->ai_family, SOCK_DGRAM, 0)) == -1) { | |
24 | perror("socket()"); | |
25 | return NULL; | |
26 | } | |
23 | if ((fd = socket(addr->ai_family, SOCK_DGRAM, 0)) == -1) { | |
24 | perror("socket()"); | |
25 | return NULL; | |
26 | } | |
27 | 27 | |
28 | /* Since the socket was created with SOCK_DGRAM, this is | |
29 | * actually not establishing a connection or generating | |
30 | * any other network traffic. Instead, as a side-effect, | |
31 | * it saves the local address with which packets would | |
32 | * be sent to the destination. */ | |
33 | if (connect(fd, addr->ai_addr, addr->ai_addrlen) == -1) { | |
34 | /* We don’t display the error here because most | |
35 | * likely, there just is no IPv6 connectivity. | |
36 | * Thus, don’t spam the user’s console but just | |
37 | * try the next address. */ | |
38 | (void)close(fd); | |
39 | return NULL; | |
40 | } | |
28 | /* Since the socket was created with SOCK_DGRAM, this is | |
29 | * actually not establishing a connection or generating | |
30 | * any other network traffic. Instead, as a side-effect, | |
31 | * it saves the local address with which packets would | |
32 | * be sent to the destination. */ | |
33 | if (connect(fd, addr->ai_addr, addr->ai_addrlen) == -1) { | |
34 | /* We don’t display the error here because most | |
35 | * likely, there just is no IPv6 connectivity. | |
36 | * Thus, don’t spam the user’s console but just | |
37 | * try the next address. */ | |
38 | (void)close(fd); | |
39 | return NULL; | |
40 | } | |
41 | 41 | |
42 | socklen_t local_len = sizeof(struct sockaddr_storage); | |
43 | if (getsockname(fd, (struct sockaddr*)&local, &local_len) == -1) { | |
44 | perror("getsockname()"); | |
45 | (void)close(fd); | |
46 | return NULL; | |
47 | } | |
42 | socklen_t local_len = sizeof(struct sockaddr_storage); | |
43 | if (getsockname(fd, (struct sockaddr *)&local, &local_len) == -1) { | |
44 | perror("getsockname()"); | |
45 | (void)close(fd); | |
46 | return NULL; | |
47 | } | |
48 | 48 | |
49 | memset(buf, 0, INET6_ADDRSTRLEN + 1); | |
50 | if ((ret = getnameinfo((struct sockaddr*)&local, local_len, | |
51 | buf, sizeof(buf), NULL, 0, | |
52 | NI_NUMERICHOST)) != 0) { | |
53 | fprintf(stderr, "i3status: getnameinfo(): %s\n", gai_strerror(ret)); | |
54 | (void)close(fd); | |
55 | return NULL; | |
56 | } | |
49 | memset(buf, 0, INET6_ADDRSTRLEN + 1); | |
50 | if ((ret = getnameinfo((struct sockaddr *)&local, local_len, | |
51 | buf, sizeof(buf), NULL, 0, | |
52 | NI_NUMERICHOST)) != 0) { | |
53 | fprintf(stderr, "i3status: getnameinfo(): %s\n", gai_strerror(ret)); | |
54 | (void)close(fd); | |
55 | return NULL; | |
56 | } | |
57 | 57 | |
58 | (void)close(fd); | |
59 | return buf; | |
58 | (void)close(fd); | |
59 | return buf; | |
60 | 60 | } |
61 | 61 | |
62 | 62 | /* |
64 | 64 | * The char * is statically allocated and mustn't be freed |
65 | 65 | */ |
66 | 66 | static char *get_ipv6_addr(void) { |
67 | struct addrinfo hints; | |
68 | struct addrinfo *result, *resp; | |
69 | static struct addrinfo *cached = NULL; | |
67 | struct addrinfo hints; | |
68 | struct addrinfo *result, *resp; | |
69 | static struct addrinfo *cached = NULL; | |
70 | 70 | |
71 | /* To save dns lookups (if they are not cached locally) and creating | |
72 | * sockets, we save the fd and keep it open. */ | |
73 | if (cached != NULL) | |
74 | return get_sockname(cached); | |
71 | /* To save dns lookups (if they are not cached locally) and creating | |
72 | * sockets, we save the fd and keep it open. */ | |
73 | if (cached != NULL) | |
74 | return get_sockname(cached); | |
75 | 75 | |
76 | memset(&hints, 0, sizeof(struct addrinfo)); | |
77 | hints.ai_family = AF_INET6; | |
78 | hints.ai_socktype = SOCK_DGRAM; | |
76 | memset(&hints, 0, sizeof(struct addrinfo)); | |
77 | hints.ai_family = AF_INET6; | |
78 | hints.ai_socktype = SOCK_DGRAM; | |
79 | 79 | |
80 | /* We use the public IPv6 of the K root server here. It doesn’t matter | |
81 | * which IPv6 address we use (we don’t even send any packets), as long | |
82 | * as it’s considered global by the kernel. | |
83 | * NB: We don’t use a hostname since that would trigger a DNS lookup. | |
84 | * By using an IPv6 address, getaddrinfo() will *not* do a DNS lookup, | |
85 | * but return the address in the appropriate struct. */ | |
86 | if (getaddrinfo("2001:7fd::1", "domain", &hints, &result) != 0) { | |
87 | /* We don’t display the error here because most | |
88 | * likely, there just is no connectivity. | |
89 | * Thus, don’t spam the user’s console. */ | |
90 | return NULL; | |
80 | /* We use the public IPv6 of the K root server here. It doesn’t matter | |
81 | * which IPv6 address we use (we don’t even send any packets), as long | |
82 | * as it’s considered global by the kernel. | |
83 | * NB: We don’t use a hostname since that would trigger a DNS lookup. | |
84 | * By using an IPv6 address, getaddrinfo() will *not* do a DNS lookup, | |
85 | * but return the address in the appropriate struct. */ | |
86 | if (getaddrinfo("2001:7fd::1", "domain", &hints, &result) != 0) { | |
87 | /* We don’t display the error here because most | |
88 | * likely, there just is no connectivity. | |
89 | * Thus, don’t spam the user’s console. */ | |
90 | return NULL; | |
91 | } | |
92 | ||
93 | for (resp = result; resp != NULL; resp = resp->ai_next) { | |
94 | char *addr_string = get_sockname(resp); | |
95 | /* If we could not get our own address and there is more than | |
96 | * one result for resolving k.root-servers.net, we cannot | |
97 | * cache. Otherwise, no matter if we got IPv6 connectivity or | |
98 | * not, we will cache the (single) result and are done. */ | |
99 | if (!addr_string && result->ai_next != NULL) | |
100 | continue; | |
101 | ||
102 | if ((cached = malloc(sizeof(struct addrinfo))) == NULL) | |
103 | return NULL; | |
104 | memcpy(cached, resp, sizeof(struct addrinfo)); | |
105 | if ((cached->ai_addr = malloc(resp->ai_addrlen)) == NULL) { | |
106 | cached = NULL; | |
107 | return NULL; | |
91 | 108 | } |
109 | memcpy(cached->ai_addr, resp->ai_addr, resp->ai_addrlen); | |
110 | freeaddrinfo(result); | |
111 | return addr_string; | |
112 | } | |
92 | 113 | |
93 | for (resp = result; resp != NULL; resp = resp->ai_next) { | |
94 | char *addr_string = get_sockname(resp); | |
95 | /* If we could not get our own address and there is more than | |
96 | * one result for resolving k.root-servers.net, we cannot | |
97 | * cache. Otherwise, no matter if we got IPv6 connectivity or | |
98 | * not, we will cache the (single) result and are done. */ | |
99 | if (!addr_string && result->ai_next != NULL) | |
100 | continue; | |
101 | ||
102 | if ((cached = malloc(sizeof(struct addrinfo))) == NULL) | |
103 | return NULL; | |
104 | memcpy(cached, resp, sizeof(struct addrinfo)); | |
105 | if ((cached->ai_addr = malloc(resp->ai_addrlen)) == NULL) { | |
106 | cached = NULL; | |
107 | return NULL; | |
108 | } | |
109 | memcpy(cached->ai_addr, resp->ai_addr, resp->ai_addrlen); | |
110 | freeaddrinfo(result); | |
111 | return addr_string; | |
112 | } | |
113 | ||
114 | freeaddrinfo(result); | |
115 | return NULL; | |
114 | freeaddrinfo(result); | |
115 | return NULL; | |
116 | 116 | } |
117 | 117 | |
118 | 118 | void print_ipv6_info(yajl_gen json_gen, char *buffer, const char *format_up, const char *format_down) { |
119 | const char *walk; | |
120 | char *addr_string = get_ipv6_addr(); | |
121 | char *outwalk = buffer; | |
119 | const char *walk; | |
120 | char *addr_string = get_ipv6_addr(); | |
121 | char *outwalk = buffer; | |
122 | 122 | |
123 | if (addr_string == NULL) { | |
124 | START_COLOR("color_bad"); | |
125 | outwalk += sprintf(outwalk, "%s", format_down); | |
126 | END_COLOR; | |
127 | OUTPUT_FULL_TEXT(buffer); | |
128 | return; | |
123 | if (addr_string == NULL) { | |
124 | START_COLOR("color_bad"); | |
125 | outwalk += sprintf(outwalk, "%s", format_down); | |
126 | END_COLOR; | |
127 | OUTPUT_FULL_TEXT(buffer); | |
128 | return; | |
129 | } | |
130 | ||
131 | START_COLOR("color_good"); | |
132 | for (walk = format_up; *walk != '\0'; walk++) { | |
133 | if (*walk != '%') { | |
134 | *(outwalk++) = *walk; | |
135 | continue; | |
129 | 136 | } |
130 | 137 | |
131 | START_COLOR("color_good"); | |
132 | for (walk = format_up; *walk != '\0'; walk++) { | |
133 | if (*walk != '%') { | |
134 | *(outwalk++) = *walk; | |
135 | continue; | |
136 | } | |
137 | ||
138 | if (strncmp(walk+1, "ip", strlen("ip")) == 0) { | |
139 | outwalk += sprintf(outwalk, "%s", addr_string); | |
140 | walk += strlen("ip"); | |
141 | } | |
138 | if (BEGINS_WITH(walk + 1, "ip")) { | |
139 | outwalk += sprintf(outwalk, "%s", addr_string); | |
140 | walk += strlen("ip"); | |
142 | 141 | } |
143 | END_COLOR; | |
144 | OUTPUT_FULL_TEXT(buffer); | |
142 | } | |
143 | END_COLOR; | |
144 | OUTPUT_FULL_TEXT(buffer); | |
145 | 145 | } |
0 | // vim:ts=8:expandtab | |
0 | // vim:ts=4:sw=4:expandtab | |
1 | 1 | #include "i3status.h" |
2 | 2 | #include <stdlib.h> |
3 | 3 | #include <stdio.h> |
6 | 6 | #include <yajl/yajl_version.h> |
7 | 7 | |
8 | 8 | void print_load(yajl_gen json_gen, char *buffer, const char *format, const float max_threshold) { |
9 | char *outwalk = buffer; | |
10 | /* Get load */ | |
9 | char *outwalk = buffer; | |
10 | /* Get load */ | |
11 | 11 | |
12 | 12 | #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(linux) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) || defined(sun) || defined(__DragonFly__) |
13 | double loadavg[3]; | |
14 | const char *walk; | |
15 | bool colorful_output = false; | |
13 | double loadavg[3]; | |
14 | const char *walk; | |
15 | bool colorful_output = false; | |
16 | 16 | |
17 | if (getloadavg(loadavg, 3) == -1) | |
18 | goto error; | |
17 | if (getloadavg(loadavg, 3) == -1) | |
18 | goto error; | |
19 | 19 | |
20 | for (walk = format; *walk != '\0'; walk++) { | |
21 | if (*walk != '%') { | |
22 | *(outwalk++) = *walk; | |
23 | continue; | |
24 | } | |
25 | if (loadavg[0] >= max_threshold) { | |
26 | START_COLOR("color_bad"); | |
27 | colorful_output = true; | |
28 | } | |
29 | ||
30 | if (BEGINS_WITH(walk+1, "1min")) { | |
31 | outwalk += sprintf(outwalk, "%1.2f", loadavg[0]); | |
32 | walk += strlen("1min"); | |
33 | } | |
34 | ||
35 | if (BEGINS_WITH(walk+1, "5min")) { | |
36 | outwalk += sprintf(outwalk, "%1.2f", loadavg[1]); | |
37 | walk += strlen("5min"); | |
38 | } | |
39 | ||
40 | if (BEGINS_WITH(walk+1, "15min")) { | |
41 | outwalk += sprintf(outwalk, "%1.2f", loadavg[2]); | |
42 | walk += strlen("15min"); | |
43 | } | |
44 | if (colorful_output) | |
45 | END_COLOR; | |
20 | for (walk = format; *walk != '\0'; walk++) { | |
21 | if (*walk != '%') { | |
22 | *(outwalk++) = *walk; | |
23 | continue; | |
24 | } | |
25 | if (loadavg[0] >= max_threshold) { | |
26 | START_COLOR("color_bad"); | |
27 | colorful_output = true; | |
46 | 28 | } |
47 | 29 | |
48 | *outwalk = '\0'; | |
49 | OUTPUT_FULL_TEXT(buffer); | |
30 | if (BEGINS_WITH(walk + 1, "1min")) { | |
31 | outwalk += sprintf(outwalk, "%1.2f", loadavg[0]); | |
32 | walk += strlen("1min"); | |
33 | } | |
50 | 34 | |
51 | return; | |
35 | if (BEGINS_WITH(walk + 1, "5min")) { | |
36 | outwalk += sprintf(outwalk, "%1.2f", loadavg[1]); | |
37 | walk += strlen("5min"); | |
38 | } | |
39 | ||
40 | if (BEGINS_WITH(walk + 1, "15min")) { | |
41 | outwalk += sprintf(outwalk, "%1.2f", loadavg[2]); | |
42 | walk += strlen("15min"); | |
43 | } | |
44 | if (colorful_output) | |
45 | END_COLOR; | |
46 | } | |
47 | ||
48 | *outwalk = '\0'; | |
49 | OUTPUT_FULL_TEXT(buffer); | |
50 | ||
51 | return; | |
52 | 52 | error: |
53 | 53 | #endif |
54 | OUTPUT_FULL_TEXT("cant read load"); | |
55 | (void)fputs("i3status: Cannot read system load using getloadavg()\n", stderr); | |
54 | OUTPUT_FULL_TEXT("cant read load"); | |
55 | (void)fputs("i3status: Cannot read system load using getloadavg()\n", stderr); | |
56 | 56 | } |
0 | // vim:ts=4:sw=4:expandtab | |
0 | 1 | #include <stdio.h> |
1 | 2 | #include <string.h> |
2 | 3 | #include <yajl/yajl_gen.h> |
5 | 6 | #include "i3status.h" |
6 | 7 | |
7 | 8 | void print_path_exists(yajl_gen json_gen, char *buffer, const char *title, const char *path, const char *format) { |
8 | const char *walk; | |
9 | char *outwalk = buffer; | |
10 | struct stat st; | |
11 | const bool exists = (stat(path, &st) == 0); | |
9 | const char *walk; | |
10 | char *outwalk = buffer; | |
11 | struct stat st; | |
12 | const bool exists = (stat(path, &st) == 0); | |
12 | 13 | |
13 | INSTANCE(path); | |
14 | INSTANCE(path); | |
14 | 15 | |
15 | START_COLOR((exists ? "color_good" : "color_bad")); | |
16 | START_COLOR((exists ? "color_good" : "color_bad")); | |
16 | 17 | |
17 | for (walk = format; *walk != '\0'; walk++) { | |
18 | if (*walk != '%') { | |
19 | *(outwalk++) = *walk; | |
20 | continue; | |
21 | } | |
22 | ||
23 | if (strncmp(walk+1, "title", strlen("title")) == 0) { | |
24 | outwalk += sprintf(outwalk, "%s", title); | |
25 | walk += strlen("title"); | |
26 | } else if (strncmp(walk+1, "status", strlen("status")) == 0) { | |
27 | outwalk += sprintf(outwalk, "%s", (exists ? "yes" : "no")); | |
28 | walk += strlen("status"); | |
29 | } | |
18 | for (walk = format; *walk != '\0'; walk++) { | |
19 | if (*walk != '%') { | |
20 | *(outwalk++) = *walk; | |
21 | continue; | |
30 | 22 | } |
31 | 23 | |
32 | END_COLOR; | |
33 | OUTPUT_FULL_TEXT(buffer); | |
24 | if (BEGINS_WITH(walk + 1, "title")) { | |
25 | outwalk += sprintf(outwalk, "%s", title); | |
26 | walk += strlen("title"); | |
27 | } else if (BEGINS_WITH(walk + 1, "status")) { | |
28 | outwalk += sprintf(outwalk, "%s", (exists ? "yes" : "no")); | |
29 | walk += strlen("status"); | |
30 | } | |
31 | } | |
32 | ||
33 | END_COLOR; | |
34 | OUTPUT_FULL_TEXT(buffer); | |
34 | 35 | } |
0 | // vim:ts=4:sw=4:expandtab | |
0 | 1 | #include <stdio.h> |
1 | 2 | #include <string.h> |
2 | 3 | #include <yajl/yajl_gen.h> |
4 | 5 | #include "i3status.h" |
5 | 6 | |
6 | 7 | void print_run_watch(yajl_gen json_gen, char *buffer, const char *title, const char *pidfile, const char *format) { |
7 | bool running = process_runs(pidfile); | |
8 | const char *walk; | |
9 | char *outwalk = buffer; | |
8 | bool running = process_runs(pidfile); | |
9 | const char *walk; | |
10 | char *outwalk = buffer; | |
10 | 11 | |
11 | INSTANCE(pidfile); | |
12 | INSTANCE(pidfile); | |
12 | 13 | |
13 | START_COLOR((running ? "color_good" : "color_bad")); | |
14 | START_COLOR((running ? "color_good" : "color_bad")); | |
14 | 15 | |
15 | for (walk = format; *walk != '\0'; walk++) { | |
16 | if (*walk != '%') { | |
17 | *(outwalk++) = *walk; | |
18 | continue; | |
19 | } | |
20 | ||
21 | if (strncmp(walk+1, "title", strlen("title")) == 0) { | |
22 | outwalk += sprintf(outwalk, "%s", title); | |
23 | walk += strlen("title"); | |
24 | } else if (strncmp(walk+1, "status", strlen("status")) == 0) { | |
25 | outwalk += sprintf(outwalk, "%s", (running ? "yes" : "no")); | |
26 | walk += strlen("status"); | |
27 | } | |
16 | for (walk = format; *walk != '\0'; walk++) { | |
17 | if (*walk != '%') { | |
18 | *(outwalk++) = *walk; | |
19 | continue; | |
28 | 20 | } |
29 | 21 | |
30 | END_COLOR; | |
31 | OUTPUT_FULL_TEXT(buffer); | |
22 | if (BEGINS_WITH(walk + 1, "title")) { | |
23 | outwalk += sprintf(outwalk, "%s", title); | |
24 | walk += strlen("title"); | |
25 | } else if (BEGINS_WITH(walk + 1, "status")) { | |
26 | outwalk += sprintf(outwalk, "%s", (running ? "yes" : "no")); | |
27 | walk += strlen("status"); | |
28 | } | |
29 | } | |
30 | ||
31 | END_COLOR; | |
32 | OUTPUT_FULL_TEXT(buffer); | |
32 | 33 | } |
0 | // vim:ts=8:expandtab | |
0 | // vim:ts=4:sw=4:expandtab | |
1 | 1 | #include <time.h> |
2 | 2 | #include <stdio.h> |
3 | 3 | #include <stdlib.h> |
12 | 12 | static const char *current_timezone = NULL; |
13 | 13 | |
14 | 14 | void set_timezone(const char *tz) { |
15 | if (!local_timezone_init) { | |
16 | /* First call, initialize. */ | |
17 | local_timezone = getenv("TZ"); | |
18 | local_timezone_init = true; | |
15 | if (!local_timezone_init) { | |
16 | /* First call, initialize. */ | |
17 | local_timezone = getenv("TZ"); | |
18 | local_timezone_init = true; | |
19 | } | |
20 | if (tz == NULL || tz[0] == '\0') { | |
21 | /* User wants localtime. */ | |
22 | tz = local_timezone; | |
23 | } | |
24 | if (tz != current_timezone) { | |
25 | if (tz) { | |
26 | setenv("TZ", tz, 1); | |
27 | } else { | |
28 | unsetenv("TZ"); | |
19 | 29 | } |
20 | if (tz == NULL || tz[0] == '\0') { | |
21 | /* User wants localtime. */ | |
22 | tz = local_timezone; | |
23 | } | |
24 | if (tz != current_timezone) { | |
25 | if (tz) { | |
26 | setenv("TZ", tz, 1); | |
27 | } else { | |
28 | unsetenv("TZ"); | |
29 | } | |
30 | tzset(); | |
31 | current_timezone = tz; | |
32 | } | |
30 | tzset(); | |
31 | current_timezone = tz; | |
32 | } | |
33 | 33 | } |
34 | 34 | |
35 | 35 | void print_time(yajl_gen json_gen, char *buffer, const char *format, const char *tz, time_t t) { |
36 | char *outwalk = buffer; | |
37 | struct tm tm; | |
36 | char *outwalk = buffer; | |
37 | struct tm tm; | |
38 | 38 | |
39 | /* Convert time and format output. */ | |
40 | set_timezone(tz); | |
41 | localtime_r(&t, &tm); | |
42 | outwalk += strftime(outwalk, 4095, format, &tm); | |
43 | *outwalk = '\0'; | |
44 | OUTPUT_FULL_TEXT(buffer); | |
39 | /* Convert time and format output. */ | |
40 | set_timezone(tz); | |
41 | localtime_r(&t, &tm); | |
42 | outwalk += strftime(outwalk, 4095, format, &tm); | |
43 | *outwalk = '\0'; | |
44 | OUTPUT_FULL_TEXT(buffer); | |
45 | 45 | } |
0 | // vim:ts=8:expandtab | |
0 | // vim:ts=4:sw=4:expandtab | |
1 | 1 | #include <time.h> |
2 | 2 | #include <string.h> |
3 | 3 | #include <stdlib.h> |
26 | 26 | #include "queue.h" |
27 | 27 | |
28 | 28 | void print_volume(yajl_gen json_gen, char *buffer, const char *fmt, const char *fmt_muted, const char *device, const char *mixer, int mixer_idx) { |
29 | char *outwalk = buffer; | |
30 | int pbval = 1; | |
29 | char *outwalk = buffer; | |
30 | int pbval = 1; | |
31 | 31 | |
32 | /* Printing volume only works with ALSA at the moment */ | |
33 | if (output_format == O_I3BAR) { | |
34 | char *instance; | |
35 | asprintf(&instance, "%s.%s.%d", device, mixer, mixer_idx); | |
36 | INSTANCE(instance); | |
37 | free(instance); | |
32 | /* Printing volume only works with ALSA at the moment */ | |
33 | if (output_format == O_I3BAR) { | |
34 | char *instance; | |
35 | asprintf(&instance, "%s.%s.%d", device, mixer, mixer_idx); | |
36 | INSTANCE(instance); | |
37 | free(instance); | |
38 | } | |
39 | #ifdef LINUX | |
40 | int err; | |
41 | snd_mixer_t *m; | |
42 | snd_mixer_selem_id_t *sid; | |
43 | snd_mixer_elem_t *elem; | |
44 | long min, max, val; | |
45 | int avg; | |
46 | ||
47 | if ((err = snd_mixer_open(&m, 0)) < 0) { | |
48 | fprintf(stderr, "i3status: ALSA: Cannot open mixer: %s\n", snd_strerror(err)); | |
49 | goto out; | |
50 | } | |
51 | ||
52 | /* Attach this mixer handle to the given device */ | |
53 | if ((err = snd_mixer_attach(m, device)) < 0) { | |
54 | fprintf(stderr, "i3status: ALSA: Cannot attach mixer to device: %s\n", snd_strerror(err)); | |
55 | snd_mixer_close(m); | |
56 | goto out; | |
57 | } | |
58 | ||
59 | /* Register this mixer */ | |
60 | if ((err = snd_mixer_selem_register(m, NULL, NULL)) < 0) { | |
61 | fprintf(stderr, "i3status: ALSA: snd_mixer_selem_register: %s\n", snd_strerror(err)); | |
62 | snd_mixer_close(m); | |
63 | goto out; | |
64 | } | |
65 | ||
66 | if ((err = snd_mixer_load(m)) < 0) { | |
67 | fprintf(stderr, "i3status: ALSA: snd_mixer_load: %s\n", snd_strerror(err)); | |
68 | snd_mixer_close(m); | |
69 | goto out; | |
70 | } | |
71 | ||
72 | snd_mixer_selem_id_malloc(&sid); | |
73 | if (sid == NULL) { | |
74 | snd_mixer_close(m); | |
75 | goto out; | |
76 | } | |
77 | ||
78 | /* Find the given mixer */ | |
79 | snd_mixer_selem_id_set_index(sid, mixer_idx); | |
80 | snd_mixer_selem_id_set_name(sid, mixer); | |
81 | if (!(elem = snd_mixer_find_selem(m, sid))) { | |
82 | fprintf(stderr, "i3status: ALSA: Cannot find mixer %s (index %i)\n", | |
83 | snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid)); | |
84 | snd_mixer_close(m); | |
85 | snd_mixer_selem_id_free(sid); | |
86 | goto out; | |
87 | } | |
88 | ||
89 | /* Get the volume range to convert the volume later */ | |
90 | snd_mixer_selem_get_playback_volume_range(elem, &min, &max); | |
91 | ||
92 | snd_mixer_handle_events(m); | |
93 | snd_mixer_selem_get_playback_volume(elem, 0, &val); | |
94 | if (max != 100) { | |
95 | float avgf = ((float)val / max) * 100; | |
96 | avg = (int)avgf; | |
97 | avg = (avgf - avg < 0.5 ? avg : (avg + 1)); | |
98 | } else | |
99 | avg = (int)val; | |
100 | ||
101 | /* Check for mute */ | |
102 | if (snd_mixer_selem_has_playback_switch(elem)) { | |
103 | if ((err = snd_mixer_selem_get_playback_switch(elem, 0, &pbval)) < 0) | |
104 | fprintf(stderr, "i3status: ALSA: playback_switch: %s\n", snd_strerror(err)); | |
105 | if (!pbval) { | |
106 | START_COLOR("color_degraded"); | |
107 | fmt = fmt_muted; | |
38 | 108 | } |
39 | #ifdef LINUX | |
40 | int err; | |
41 | snd_mixer_t *m; | |
42 | snd_mixer_selem_id_t *sid; | |
43 | snd_mixer_elem_t *elem; | |
44 | long min, max, val; | |
45 | int avg; | |
109 | } | |
46 | 110 | |
47 | if ((err = snd_mixer_open(&m, 0)) < 0) { | |
48 | fprintf(stderr, "i3status: ALSA: Cannot open mixer: %s\n", snd_strerror(err)); | |
49 | goto out; | |
50 | } | |
111 | snd_mixer_close(m); | |
112 | snd_mixer_selem_id_free(sid); | |
51 | 113 | |
52 | /* Attach this mixer handle to the given device */ | |
53 | if ((err = snd_mixer_attach(m, device)) < 0) { | |
54 | fprintf(stderr, "i3status: ALSA: Cannot attach mixer to device: %s\n", snd_strerror(err)); | |
55 | snd_mixer_close(m); | |
56 | goto out; | |
57 | } | |
58 | ||
59 | /* Register this mixer */ | |
60 | if ((err = snd_mixer_selem_register(m, NULL, NULL)) < 0) { | |
61 | fprintf(stderr, "i3status: ALSA: snd_mixer_selem_register: %s\n", snd_strerror(err)); | |
62 | snd_mixer_close(m); | |
63 | goto out; | |
64 | } | |
65 | ||
66 | if ((err = snd_mixer_load(m)) < 0) { | |
67 | fprintf(stderr, "i3status: ALSA: snd_mixer_load: %s\n", snd_strerror(err)); | |
68 | snd_mixer_close(m); | |
69 | goto out; | |
70 | } | |
71 | ||
72 | snd_mixer_selem_id_malloc(&sid); | |
73 | if (sid == NULL) { | |
74 | snd_mixer_close(m); | |
75 | goto out; | |
76 | } | |
77 | ||
78 | /* Find the given mixer */ | |
79 | snd_mixer_selem_id_set_index(sid, mixer_idx); | |
80 | snd_mixer_selem_id_set_name(sid, mixer); | |
81 | if (!(elem = snd_mixer_find_selem(m, sid))) { | |
82 | fprintf(stderr, "i3status: ALSA: Cannot find mixer %s (index %i)\n", | |
83 | snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid)); | |
84 | snd_mixer_close(m); | |
85 | snd_mixer_selem_id_free(sid); | |
86 | goto out; | |
87 | } | |
88 | ||
89 | /* Get the volume range to convert the volume later */ | |
90 | snd_mixer_selem_get_playback_volume_range(elem, &min, &max); | |
91 | ||
92 | snd_mixer_handle_events (m); | |
93 | snd_mixer_selem_get_playback_volume (elem, 0, &val); | |
94 | if (max != 100) { | |
95 | float avgf = ((float)val / max) * 100; | |
96 | avg = (int)avgf; | |
97 | avg = (avgf - avg < 0.5 ? avg : (avg+1)); | |
98 | } else avg = (int)val; | |
99 | ||
100 | /* Check for mute */ | |
101 | if (snd_mixer_selem_has_playback_switch(elem)) { | |
102 | if ((err = snd_mixer_selem_get_playback_switch(elem, 0, &pbval)) < 0) | |
103 | fprintf (stderr, "i3status: ALSA: playback_switch: %s\n", snd_strerror(err)); | |
104 | if (!pbval) { | |
105 | START_COLOR("color_degraded"); | |
106 | fmt = fmt_muted; | |
107 | } | |
108 | } | |
109 | ||
110 | snd_mixer_close(m); | |
111 | snd_mixer_selem_id_free(sid); | |
112 | ||
113 | const char *walk = fmt; | |
114 | for (; *walk != '\0'; walk++) { | |
115 | if (*walk != '%') { | |
116 | *(outwalk++) = *walk; | |
117 | continue; | |
118 | } | |
119 | if (BEGINS_WITH(walk+1, "%")) { | |
120 | outwalk += sprintf(outwalk, "%%"); | |
121 | walk += strlen("%"); | |
122 | } | |
123 | if (BEGINS_WITH(walk+1, "volume")) { | |
124 | outwalk += sprintf(outwalk, "%d%%", avg); | |
125 | walk += strlen("volume"); | |
126 | } | |
127 | } | |
114 | const char *walk = fmt; | |
115 | for (; *walk != '\0'; walk++) { | |
116 | if (*walk != '%') { | |
117 | *(outwalk++) = *walk; | |
118 | continue; | |
119 | } | |
120 | if (BEGINS_WITH(walk + 1, "%")) { | |
121 | outwalk += sprintf(outwalk, "%%"); | |
122 | walk += strlen("%"); | |
123 | } | |
124 | if (BEGINS_WITH(walk + 1, "volume")) { | |
125 | outwalk += sprintf(outwalk, "%d%%", avg); | |
126 | walk += strlen("volume"); | |
127 | } | |
128 | } | |
128 | 129 | #endif |
129 | 130 | #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) |
130 | char *mixerpath; | |
131 | char defaultmixer[] = "/dev/mixer"; | |
132 | int mixfd, vol, devmask = 0; | |
133 | pbval = 1; | |
131 | char *mixerpath; | |
132 | char defaultmixer[] = "/dev/mixer"; | |
133 | int mixfd, vol, devmask = 0; | |
134 | pbval = 1; | |
134 | 135 | |
135 | if (mixer_idx > 0) | |
136 | asprintf(&mixerpath, "/dev/mixer%d", mixer_idx); | |
137 | else | |
138 | mixerpath = defaultmixer; | |
136 | if (mixer_idx > 0) | |
137 | asprintf(&mixerpath, "/dev/mixer%d", mixer_idx); | |
138 | else | |
139 | mixerpath = defaultmixer; | |
139 | 140 | |
140 | if ((mixfd = open(mixerpath, O_RDWR)) < 0) | |
141 | return; | |
141 | if ((mixfd = open(mixerpath, O_RDWR)) < 0) | |
142 | return; | |
142 | 143 | |
143 | if (mixer_idx > 0) | |
144 | free(mixerpath); | |
144 | if (mixer_idx > 0) | |
145 | free(mixerpath); | |
145 | 146 | |
146 | if (ioctl(mixfd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) | |
147 | return; | |
148 | if (ioctl(mixfd, MIXER_READ(0),&vol) == -1) | |
149 | return; | |
147 | if (ioctl(mixfd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) | |
148 | return; | |
149 | if (ioctl(mixfd, MIXER_READ(0), &vol) == -1) | |
150 | return; | |
150 | 151 | |
151 | if (((vol & 0x7f) == 0) && (((vol >> 8) & 0x7f) == 0)) { | |
152 | START_COLOR("color_degraded"); | |
153 | pbval = 0; | |
152 | if (((vol & 0x7f) == 0) && (((vol >> 8) & 0x7f) == 0)) { | |
153 | START_COLOR("color_degraded"); | |
154 | pbval = 0; | |
155 | } | |
156 | ||
157 | const char *walk = fmt; | |
158 | for (; *walk != '\0'; walk++) { | |
159 | if (*walk != '%') { | |
160 | *(outwalk++) = *walk; | |
161 | continue; | |
154 | 162 | } |
155 | ||
156 | const char *walk = fmt; | |
157 | for (; *walk != '\0'; walk++) { | |
158 | if (*walk != '%') { | |
159 | *(outwalk++) = *walk; | |
160 | continue; | |
161 | } | |
162 | if (BEGINS_WITH(walk+1, "%")) { | |
163 | outwalk += sprintf(outwalk, "%%"); | |
164 | walk += strlen("%"); | |
165 | } | |
166 | if (BEGINS_WITH(walk+1, "volume")) { | |
167 | outwalk += sprintf(outwalk, "%d%%", vol & 0x7f); | |
168 | walk += strlen("volume"); | |
169 | } | |
163 | if (BEGINS_WITH(walk + 1, "%")) { | |
164 | outwalk += sprintf(outwalk, "%%"); | |
165 | walk += strlen("%"); | |
170 | 166 | } |
171 | close(mixfd); | |
167 | if (BEGINS_WITH(walk + 1, "volume")) { | |
168 | outwalk += sprintf(outwalk, "%d%%", vol & 0x7f); | |
169 | walk += strlen("volume"); | |
170 | } | |
171 | } | |
172 | close(mixfd); | |
172 | 173 | #endif |
173 | 174 | |
174 | 175 | out: |
175 | *outwalk = '\0'; | |
176 | OUTPUT_FULL_TEXT(buffer); | |
177 | ||
178 | if (!pbval) | |
179 | END_COLOR; | |
176 | *outwalk = '\0'; | |
177 | if (!pbval) | |
178 | END_COLOR; | |
179 | OUTPUT_FULL_TEXT(buffer); | |
180 | 180 | } |
0 | // vim:ts=8:expandtab | |
0 | // vim:ts=4:sw=4:expandtab | |
1 | 1 | #include <stdio.h> |
2 | 2 | #include <string.h> |
3 | 3 | #include <yajl/yajl_gen.h> |
50 | 50 | |
51 | 51 | #include "i3status.h" |
52 | 52 | |
53 | #define WIRELESS_INFO_FLAG_HAS_ESSID (1 << 0) | |
54 | #define WIRELESS_INFO_FLAG_HAS_QUALITY (1 << 1) | |
55 | #define WIRELESS_INFO_FLAG_HAS_SIGNAL (1 << 2) | |
56 | #define WIRELESS_INFO_FLAG_HAS_NOISE (1 << 3) | |
53 | #define WIRELESS_INFO_FLAG_HAS_ESSID (1 << 0) | |
54 | #define WIRELESS_INFO_FLAG_HAS_QUALITY (1 << 1) | |
55 | #define WIRELESS_INFO_FLAG_HAS_SIGNAL (1 << 2) | |
56 | #define WIRELESS_INFO_FLAG_HAS_NOISE (1 << 3) | |
57 | #define WIRELESS_INFO_FLAG_HAS_FREQUENCY (1 << 4) | |
57 | 58 | |
58 | 59 | #define PERCENT_VALUE(value, total) ((int)(value * 100 / (float)total + 0.5f)) |
59 | 60 | |
60 | 61 | typedef struct { |
61 | int flags; | |
62 | char essid[IW_ESSID_MAX_SIZE + 1]; | |
63 | int quality; | |
64 | int quality_max; | |
65 | int quality_average; | |
66 | int signal_level; | |
67 | int signal_level_max; | |
68 | int noise_level; | |
69 | int noise_level_max; | |
70 | int bitrate; | |
62 | int flags; | |
63 | char essid[IW_ESSID_MAX_SIZE + 1]; | |
64 | int quality; | |
65 | int quality_max; | |
66 | int quality_average; | |
67 | int signal_level; | |
68 | int signal_level_max; | |
69 | int noise_level; | |
70 | int noise_level_max; | |
71 | int bitrate; | |
72 | double frequency; | |
71 | 73 | } wireless_info_t; |
72 | 74 | |
73 | 75 | static int get_wireless_info(const char *interface, wireless_info_t *info) { |
74 | memset(info, 0, sizeof(wireless_info_t)); | |
76 | memset(info, 0, sizeof(wireless_info_t)); | |
75 | 77 | |
76 | 78 | #ifdef LINUX |
77 | int skfd = iw_sockets_open(); | |
78 | if (skfd < 0) { | |
79 | perror("iw_sockets_open"); | |
80 | return 0; | |
81 | } | |
82 | ||
83 | wireless_config wcfg; | |
84 | if (iw_get_basic_config(skfd, interface, &wcfg) < 0) { | |
85 | close(skfd); | |
86 | return 0; | |
87 | } | |
88 | ||
89 | if (wcfg.has_essid && wcfg.essid_on) { | |
90 | info->flags |= WIRELESS_INFO_FLAG_HAS_ESSID; | |
91 | strncpy(&info->essid[0], wcfg.essid, IW_ESSID_MAX_SIZE); | |
92 | info->essid[IW_ESSID_MAX_SIZE] = '\0'; | |
93 | } | |
94 | ||
95 | /* If the function iw_get_stats does not return proper stats, the | |
96 | wifi is considered as down. | |
97 | Since ad-hoc network does not have theses stats, we need to return | |
98 | here for this mode. */ | |
99 | if (wcfg.mode == 1) { | |
100 | close(skfd); | |
101 | return 1; | |
102 | } | |
103 | ||
104 | /* Wireless quality is a relative value in a driver-specific range. | |
105 | Signal and noise level can be either relative or absolute values | |
106 | in dBm. Furthermore, noise and quality can be expressed directly | |
107 | in dBm or in RCPI (802.11k), which we convert to dBm. When those | |
108 | values are expressed directly in dBm, they range from -192 to 63, | |
109 | and since the values are packed into 8 bits, we need to perform | |
110 | 8-bit arithmetic on them. Assume absolute values if everything | |
111 | else fails (driver bug). */ | |
112 | ||
113 | iwrange range; | |
114 | if (iw_get_range_info(skfd, interface, &range) < 0) { | |
115 | close(skfd); | |
116 | return 0; | |
117 | } | |
118 | ||
119 | iwstats stats; | |
120 | if (iw_get_stats(skfd, interface, &stats, &range, 1) < 0) { | |
121 | close(skfd); | |
122 | return 0; | |
123 | } | |
124 | ||
125 | if (stats.qual.level != 0 || (stats.qual.updated & (IW_QUAL_DBM | IW_QUAL_RCPI))) { | |
126 | if (!(stats.qual.updated & IW_QUAL_QUAL_INVALID)) { | |
127 | info->quality = stats.qual.qual; | |
128 | info->quality_max = range.max_qual.qual; | |
129 | info->quality_average = range.avg_qual.qual; | |
130 | info->flags |= WIRELESS_INFO_FLAG_HAS_QUALITY; | |
131 | } | |
132 | ||
133 | if (stats.qual.updated & IW_QUAL_RCPI) { | |
134 | if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) { | |
135 | info->signal_level = stats.qual.level / 2.0 - 110 + 0.5; | |
136 | info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL; | |
137 | } | |
138 | if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) { | |
139 | info->noise_level = stats.qual.noise / 2.0 - 110 + 0.5; | |
140 | info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE; | |
141 | } | |
142 | } | |
143 | else { | |
144 | if ((stats.qual.updated & IW_QUAL_DBM) || stats.qual.level > range.max_qual.level) { | |
145 | if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) { | |
146 | info->signal_level = stats.qual.level; | |
147 | if (info->signal_level > 63) | |
148 | info->signal_level -= 256; | |
149 | info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL; | |
150 | } | |
151 | if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) { | |
152 | info->noise_level = stats.qual.noise; | |
153 | if (info->noise_level > 63) | |
154 | info->noise_level -= 256; | |
155 | info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE; | |
156 | } | |
157 | } | |
158 | else { | |
159 | if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) { | |
160 | info->signal_level = stats.qual.level; | |
161 | info->signal_level_max = range.max_qual.level; | |
162 | info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL; | |
163 | } | |
164 | if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) { | |
165 | info->noise_level = stats.qual.noise; | |
166 | info->noise_level_max = range.max_qual.noise; | |
167 | info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE; | |
168 | } | |
169 | } | |
170 | } | |
171 | } | |
172 | else { | |
173 | if (!(stats.qual.updated & IW_QUAL_QUAL_INVALID)) { | |
174 | info->quality = stats.qual.qual; | |
175 | info->flags |= WIRELESS_INFO_FLAG_HAS_QUALITY; | |
176 | } | |
79 | int skfd = iw_sockets_open(); | |
80 | if (skfd < 0) { | |
81 | perror("iw_sockets_open"); | |
82 | return 0; | |
83 | } | |
84 | ||
85 | wireless_config wcfg; | |
86 | if (iw_get_basic_config(skfd, interface, &wcfg) < 0) { | |
87 | close(skfd); | |
88 | return 0; | |
89 | } | |
90 | ||
91 | if (wcfg.has_essid && wcfg.essid_on) { | |
92 | info->flags |= WIRELESS_INFO_FLAG_HAS_ESSID; | |
93 | strncpy(&info->essid[0], wcfg.essid, IW_ESSID_MAX_SIZE); | |
94 | info->essid[IW_ESSID_MAX_SIZE] = '\0'; | |
95 | } | |
96 | ||
97 | if (wcfg.has_freq) { | |
98 | info->frequency = wcfg.freq; | |
99 | info->flags |= WIRELESS_INFO_FLAG_HAS_FREQUENCY; | |
100 | } | |
101 | ||
102 | /* If the function iw_get_stats does not return proper stats, the | |
103 | * wifi is considered as down. | |
104 | * Since ad-hoc network does not have theses stats, we need to return | |
105 | * here for this mode. */ | |
106 | if (wcfg.mode == 1) { | |
107 | close(skfd); | |
108 | return 1; | |
109 | } | |
110 | ||
111 | /* Wireless quality is a relative value in a driver-specific range. | |
112 | * Signal and noise level can be either relative or absolute values | |
113 | * in dBm. Furthermore, noise and quality can be expressed directly | |
114 | * in dBm or in RCPI (802.11k), which we convert to dBm. When those | |
115 | * values are expressed directly in dBm, they range from -192 to 63, | |
116 | * and since the values are packed into 8 bits, we need to perform | |
117 | * 8-bit arithmetic on them. Assume absolute values if everything | |
118 | * else fails (driver bug). */ | |
119 | ||
120 | iwrange range; | |
121 | if (iw_get_range_info(skfd, interface, &range) < 0) { | |
122 | close(skfd); | |
123 | return 0; | |
124 | } | |
125 | ||
126 | iwstats stats; | |
127 | if (iw_get_stats(skfd, interface, &stats, &range, 1) < 0) { | |
128 | close(skfd); | |
129 | return 0; | |
130 | } | |
131 | ||
132 | if (stats.qual.level != 0 || (stats.qual.updated & (IW_QUAL_DBM | IW_QUAL_RCPI))) { | |
133 | if (!(stats.qual.updated & IW_QUAL_QUAL_INVALID)) { | |
134 | info->quality = stats.qual.qual; | |
135 | info->quality_max = range.max_qual.qual; | |
136 | info->quality_average = range.avg_qual.qual; | |
137 | info->flags |= WIRELESS_INFO_FLAG_HAS_QUALITY; | |
138 | } | |
139 | ||
140 | if (stats.qual.updated & IW_QUAL_RCPI) { | |
141 | if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) { | |
142 | info->signal_level = stats.qual.level / 2.0 - 110 + 0.5; | |
143 | info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL; | |
144 | } | |
145 | if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) { | |
146 | info->noise_level = stats.qual.noise / 2.0 - 110 + 0.5; | |
147 | info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE; | |
148 | } | |
149 | } else { | |
150 | if ((stats.qual.updated & IW_QUAL_DBM) || stats.qual.level > range.max_qual.level) { | |
177 | 151 | if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) { |
178 | info->quality = stats.qual.level; | |
179 | info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL; | |
152 | info->signal_level = stats.qual.level; | |
153 | if (info->signal_level > 63) | |
154 | info->signal_level -= 256; | |
155 | info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL; | |
180 | 156 | } |
181 | 157 | if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) { |
182 | info->quality = stats.qual.noise; | |
183 | info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE; | |
158 | info->noise_level = stats.qual.noise; | |
159 | if (info->noise_level > 63) | |
160 | info->noise_level -= 256; | |
161 | info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE; | |
184 | 162 | } |
185 | } | |
186 | ||
187 | struct iwreq wrq; | |
188 | if (iw_get_ext(skfd, interface, SIOCGIWRATE, &wrq) >= 0) | |
189 | info->bitrate = wrq.u.bitrate.value; | |
190 | ||
191 | close(skfd); | |
192 | return 1; | |
163 | } else { | |
164 | if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) { | |
165 | info->signal_level = stats.qual.level; | |
166 | info->signal_level_max = range.max_qual.level; | |
167 | info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL; | |
168 | } | |
169 | if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) { | |
170 | info->noise_level = stats.qual.noise; | |
171 | info->noise_level_max = range.max_qual.noise; | |
172 | info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE; | |
173 | } | |
174 | } | |
175 | } | |
176 | } else { | |
177 | if (!(stats.qual.updated & IW_QUAL_QUAL_INVALID)) { | |
178 | info->quality = stats.qual.qual; | |
179 | info->flags |= WIRELESS_INFO_FLAG_HAS_QUALITY; | |
180 | } | |
181 | if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) { | |
182 | info->quality = stats.qual.level; | |
183 | info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL; | |
184 | } | |
185 | if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) { | |
186 | info->quality = stats.qual.noise; | |
187 | info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE; | |
188 | } | |
189 | } | |
190 | ||
191 | struct iwreq wrq; | |
192 | if (iw_get_ext(skfd, interface, SIOCGIWRATE, &wrq) >= 0) | |
193 | info->bitrate = wrq.u.bitrate.value; | |
194 | ||
195 | close(skfd); | |
196 | return 1; | |
193 | 197 | #endif |
194 | 198 | #if defined(__FreeBSD__) || defined(__DragonFly__) |
195 | int s, len, inwid; | |
196 | uint8_t buf[24 * 1024], *cp; | |
197 | struct ieee80211req na; | |
198 | char network_id[IEEE80211_NWID_LEN + 1]; | |
199 | ||
200 | if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) | |
201 | return (0); | |
202 | ||
203 | memset(&na, 0, sizeof(na)); | |
204 | strlcpy(na.i_name, interface, sizeof(na.i_name)); | |
205 | na.i_type = IEEE80211_IOC_SSID; | |
206 | na.i_data = &info->essid[0]; | |
207 | na.i_len = IEEE80211_NWID_LEN + 1; | |
208 | if ((inwid = ioctl(s, SIOCG80211, (caddr_t)&na)) == -1) { | |
209 | close(s); | |
210 | return (0); | |
211 | } | |
212 | if (inwid == 0) { | |
213 | if (na.i_len <= IEEE80211_NWID_LEN) | |
214 | len = na.i_len + 1; | |
199 | int s, len, inwid; | |
200 | uint8_t buf[24 * 1024], *cp; | |
201 | struct ieee80211req na; | |
202 | char network_id[IEEE80211_NWID_LEN + 1]; | |
203 | ||
204 | if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) | |
205 | return (0); | |
206 | ||
207 | memset(&na, 0, sizeof(na)); | |
208 | strlcpy(na.i_name, interface, sizeof(na.i_name)); | |
209 | na.i_type = IEEE80211_IOC_SSID; | |
210 | na.i_data = &info->essid[0]; | |
211 | na.i_len = IEEE80211_NWID_LEN + 1; | |
212 | if ((inwid = ioctl(s, SIOCG80211, (caddr_t)&na)) == -1) { | |
213 | close(s); | |
214 | return (0); | |
215 | } | |
216 | if (inwid == 0) { | |
217 | if (na.i_len <= IEEE80211_NWID_LEN) | |
218 | len = na.i_len + 1; | |
219 | else | |
220 | len = IEEE80211_NWID_LEN + 1; | |
221 | info->essid[len - 1] = '\0'; | |
222 | } else { | |
223 | close(s); | |
224 | return (0); | |
225 | } | |
226 | info->flags |= WIRELESS_INFO_FLAG_HAS_ESSID; | |
227 | ||
228 | memset(&na, 0, sizeof(na)); | |
229 | strlcpy(na.i_name, interface, sizeof(na.i_name)); | |
230 | na.i_type = IEEE80211_IOC_SCAN_RESULTS; | |
231 | na.i_data = buf; | |
232 | na.i_len = sizeof(buf); | |
233 | ||
234 | if (ioctl(s, SIOCG80211, (caddr_t)&na) == -1) { | |
235 | printf("fail\n"); | |
236 | close(s); | |
237 | return (0); | |
238 | } | |
239 | ||
240 | close(s); | |
241 | len = na.i_len; | |
242 | cp = buf; | |
243 | struct ieee80211req_scan_result *sr; | |
244 | uint8_t *vp; | |
245 | sr = (struct ieee80211req_scan_result *)cp; | |
246 | vp = (u_int8_t *)(sr + 1); | |
247 | strlcpy(network_id, (const char *)vp, sr->isr_ssid_len + 1); | |
248 | if (!strcmp(network_id, &info->essid[0])) { | |
249 | info->signal_level = sr->isr_rssi; | |
250 | info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL; | |
251 | info->noise_level = sr->isr_noise; | |
252 | info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE; | |
253 | info->quality = sr->isr_intval; | |
254 | info->flags |= WIRELESS_INFO_FLAG_HAS_QUALITY; | |
255 | } | |
256 | ||
257 | return 1; | |
258 | #endif | |
259 | #ifdef __OpenBSD__ | |
260 | struct ifreq ifr; | |
261 | struct ieee80211_bssid bssid; | |
262 | struct ieee80211_nwid nwid; | |
263 | struct ieee80211_nodereq nr; | |
264 | ||
265 | struct ether_addr ea; | |
266 | ||
267 | int s, len, ibssid, inwid; | |
268 | u_int8_t zero_bssid[IEEE80211_ADDR_LEN]; | |
269 | ||
270 | if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) | |
271 | return (0); | |
272 | ||
273 | memset(&ifr, 0, sizeof(ifr)); | |
274 | ifr.ifr_data = (caddr_t)&nwid; | |
275 | (void)strlcpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name)); | |
276 | inwid = ioctl(s, SIOCG80211NWID, (caddr_t)&ifr); | |
277 | ||
278 | memset(&bssid, 0, sizeof(bssid)); | |
279 | strlcpy(bssid.i_name, interface, sizeof(bssid.i_name)); | |
280 | ibssid = ioctl(s, SIOCG80211BSSID, &bssid); | |
281 | ||
282 | if (ibssid != 0 || inwid != 0) { | |
283 | close(s); | |
284 | return 0; | |
285 | } | |
286 | ||
287 | /* NWID */ | |
288 | { | |
289 | if (nwid.i_len <= IEEE80211_NWID_LEN) | |
290 | len = nwid.i_len + 1; | |
291 | else | |
292 | len = IEEE80211_NWID_LEN + 1; | |
293 | ||
294 | strncpy(&info->essid[0], nwid.i_nwid, len); | |
295 | info->essid[IW_ESSID_MAX_SIZE] = '\0'; | |
296 | info->flags |= WIRELESS_INFO_FLAG_HAS_ESSID; | |
297 | } | |
298 | ||
299 | /* Signal strength */ | |
300 | { | |
301 | memset(&zero_bssid, 0, sizeof(zero_bssid)); | |
302 | if (ibssid == 0 && memcmp(bssid.i_bssid, zero_bssid, IEEE80211_ADDR_LEN) != 0) { | |
303 | memcpy(&ea.ether_addr_octet, bssid.i_bssid, sizeof(ea.ether_addr_octet)); | |
304 | ||
305 | bzero(&nr, sizeof(nr)); | |
306 | bcopy(bssid.i_bssid, &nr.nr_macaddr, sizeof(nr.nr_macaddr)); | |
307 | strlcpy(nr.nr_ifname, interface, sizeof(nr.nr_ifname)); | |
308 | ||
309 | if (ioctl(s, SIOCG80211NODE, &nr) == 0 && nr.nr_rssi) { | |
310 | if (nr.nr_max_rssi) | |
311 | info->signal_level_max = IEEE80211_NODEREQ_RSSI(&nr); | |
215 | 312 | else |
216 | len = IEEE80211_NWID_LEN + 1; | |
217 | info->essid[len -1] = '\0'; | |
218 | } else { | |
219 | close(s); | |
220 | return (0); | |
221 | } | |
222 | info->flags |= WIRELESS_INFO_FLAG_HAS_ESSID; | |
223 | ||
224 | memset(&na, 0, sizeof(na)); | |
225 | strlcpy(na.i_name, interface, sizeof(na.i_name)); | |
226 | na.i_type = IEEE80211_IOC_SCAN_RESULTS; | |
227 | na.i_data = buf; | |
228 | na.i_len = sizeof(buf); | |
229 | ||
230 | if (ioctl(s, SIOCG80211, (caddr_t)&na) == -1) { | |
231 | printf("fail\n"); | |
232 | close(s); | |
233 | return (0); | |
234 | } | |
235 | ||
236 | close(s); | |
237 | len = na.i_len; | |
238 | cp = buf; | |
239 | struct ieee80211req_scan_result *sr; | |
240 | uint8_t *vp; | |
241 | sr = (struct ieee80211req_scan_result *)cp; | |
242 | vp = (u_int8_t *)(sr + 1); | |
243 | strlcpy(network_id, (const char *)vp, sr->isr_ssid_len + 1); | |
244 | if (!strcmp(network_id, &info->essid[0])) { | |
245 | info->signal_level = sr->isr_rssi; | |
313 | info->signal_level = nr.nr_rssi; | |
314 | ||
246 | 315 | info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL; |
247 | info->noise_level = sr->isr_noise; | |
248 | info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE; | |
249 | info->quality = sr->isr_intval; | |
250 | info->flags |= WIRELESS_INFO_FLAG_HAS_QUALITY; | |
251 | } | |
252 | ||
253 | return 1; | |
254 | #endif | |
255 | #ifdef __OpenBSD__ | |
256 | struct ifreq ifr; | |
257 | struct ieee80211_bssid bssid; | |
258 | struct ieee80211_nwid nwid; | |
259 | struct ieee80211_nodereq nr; | |
260 | ||
261 | struct ether_addr ea; | |
262 | ||
263 | int s, len, ibssid, inwid; | |
264 | u_int8_t zero_bssid[IEEE80211_ADDR_LEN]; | |
265 | ||
266 | if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) | |
267 | return (0); | |
268 | ||
269 | memset(&ifr, 0, sizeof(ifr)); | |
270 | ifr.ifr_data = (caddr_t)&nwid; | |
271 | (void)strlcpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name)); | |
272 | inwid = ioctl(s, SIOCG80211NWID, (caddr_t)&ifr); | |
273 | ||
274 | memset(&bssid, 0, sizeof(bssid)); | |
275 | strlcpy(bssid.i_name, interface, sizeof(bssid.i_name)); | |
276 | ibssid = ioctl(s, SIOCG80211BSSID, &bssid); | |
277 | ||
278 | if (ibssid != 0 || inwid != 0) { | |
279 | close(s); | |
280 | return 0; | |
281 | } | |
282 | ||
283 | /* NWID */ | |
284 | { | |
285 | if (nwid.i_len <= IEEE80211_NWID_LEN) | |
286 | len = nwid.i_len + 1; | |
287 | else | |
288 | len = IEEE80211_NWID_LEN + 1; | |
289 | ||
290 | strncpy(&info->essid[0], nwid.i_nwid, len); | |
291 | info->essid[IW_ESSID_MAX_SIZE] = '\0'; | |
292 | info->flags |= WIRELESS_INFO_FLAG_HAS_ESSID; | |
293 | } | |
294 | ||
295 | /* Signal strength */ | |
296 | { | |
297 | memset(&zero_bssid, 0, sizeof(zero_bssid)); | |
298 | if (ibssid == 0 && memcmp(bssid.i_bssid, zero_bssid, IEEE80211_ADDR_LEN) != 0) { | |
299 | memcpy(&ea.ether_addr_octet, bssid.i_bssid, sizeof(ea.ether_addr_octet)); | |
300 | ||
301 | bzero(&nr, sizeof(nr)); | |
302 | bcopy(bssid.i_bssid, &nr.nr_macaddr, sizeof(nr.nr_macaddr)); | |
303 | strlcpy(nr.nr_ifname, interface, sizeof(nr.nr_ifname)); | |
304 | ||
305 | if (ioctl(s, SIOCG80211NODE, &nr) == 0 && nr.nr_rssi) { | |
306 | if (nr.nr_max_rssi) | |
307 | info->signal_level_max = IEEE80211_NODEREQ_RSSI(&nr); | |
308 | else | |
309 | info->signal_level = nr.nr_rssi; | |
310 | ||
311 | info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL; | |
312 | } | |
313 | } | |
314 | } | |
315 | ||
316 | close(s); | |
317 | return 1; | |
318 | #endif | |
319 | return 0; | |
316 | } | |
317 | } | |
318 | } | |
319 | ||
320 | close(s); | |
321 | return 1; | |
322 | #endif | |
323 | return 0; | |
320 | 324 | } |
321 | 325 | |
322 | 326 | void print_wireless_info(yajl_gen json_gen, char *buffer, const char *interface, const char *format_up, const char *format_down) { |
323 | const char *walk; | |
324 | char *outwalk = buffer; | |
325 | wireless_info_t info; | |
326 | ||
327 | INSTANCE(interface); | |
328 | ||
329 | const char *ip_address = get_ip_addr(interface); | |
330 | if (ip_address == NULL) { | |
331 | START_COLOR("color_bad"); | |
332 | outwalk += sprintf(outwalk, "%s", format_down); | |
333 | goto out; | |
334 | } | |
335 | ||
336 | if (get_wireless_info(interface, &info)) { | |
337 | walk = format_up; | |
338 | if (info.flags & WIRELESS_INFO_FLAG_HAS_QUALITY) | |
339 | START_COLOR((info.quality < info.quality_average ? "color_degraded" : "color_good")); | |
327 | const char *walk; | |
328 | char *outwalk = buffer; | |
329 | wireless_info_t info; | |
330 | ||
331 | INSTANCE(interface); | |
332 | ||
333 | const char *ip_address = get_ip_addr(interface); | |
334 | if (ip_address == NULL) { | |
335 | START_COLOR("color_bad"); | |
336 | outwalk += sprintf(outwalk, "%s", format_down); | |
337 | goto out; | |
338 | } | |
339 | ||
340 | if (get_wireless_info(interface, &info)) { | |
341 | walk = format_up; | |
342 | if (info.flags & WIRELESS_INFO_FLAG_HAS_QUALITY) | |
343 | START_COLOR((info.quality < info.quality_average ? "color_degraded" : "color_good")); | |
344 | else | |
345 | START_COLOR("color_good"); | |
346 | } else { | |
347 | walk = format_down; | |
348 | START_COLOR("color_bad"); | |
349 | } | |
350 | ||
351 | for (; *walk != '\0'; walk++) { | |
352 | if (*walk != '%') { | |
353 | *(outwalk++) = *walk; | |
354 | continue; | |
355 | } | |
356 | ||
357 | if (BEGINS_WITH(walk + 1, "quality")) { | |
358 | if (info.flags & WIRELESS_INFO_FLAG_HAS_QUALITY) { | |
359 | if (info.quality_max) | |
360 | outwalk += sprintf(outwalk, "%03d%%", PERCENT_VALUE(info.quality, info.quality_max)); | |
340 | 361 | else |
341 | START_COLOR("color_good"); | |
342 | } else { | |
343 | walk = format_down; | |
344 | START_COLOR("color_bad"); | |
345 | } | |
346 | ||
347 | for (; *walk != '\0'; walk++) { | |
348 | if (*walk != '%') { | |
349 | *(outwalk++) = *walk; | |
350 | continue; | |
351 | } | |
352 | ||
353 | if (BEGINS_WITH(walk+1, "quality")) { | |
354 | if (info.flags & WIRELESS_INFO_FLAG_HAS_QUALITY) { | |
355 | if (info.quality_max) | |
356 | outwalk += sprintf(outwalk, "%03d%%", PERCENT_VALUE(info.quality, info.quality_max)); | |
357 | else | |
358 | outwalk += sprintf(outwalk, "%d", info.quality); | |
359 | } else { | |
360 | *(outwalk++) = '?'; | |
361 | } | |
362 | walk += strlen("quality"); | |
363 | } | |
364 | ||
365 | if (BEGINS_WITH(walk+1, "signal")) { | |
366 | if (info.flags & WIRELESS_INFO_FLAG_HAS_SIGNAL) { | |
367 | if (info.signal_level_max) | |
368 | outwalk += sprintf(outwalk, "%03d%%", PERCENT_VALUE(info.signal_level, info.signal_level_max)); | |
369 | else | |
370 | outwalk += sprintf(outwalk, "%d dBm", info.signal_level); | |
371 | } else { | |
372 | *(outwalk++) = '?'; | |
373 | } | |
374 | walk += strlen("signal"); | |
375 | } | |
376 | ||
377 | if (BEGINS_WITH(walk+1, "noise")) { | |
378 | if (info.flags & WIRELESS_INFO_FLAG_HAS_NOISE) { | |
379 | if (info.noise_level_max) | |
380 | outwalk += sprintf(outwalk, "%03d%%", PERCENT_VALUE(info.noise_level, info.noise_level_max)); | |
381 | else | |
382 | outwalk += sprintf(outwalk, "%d dBm", info.noise_level); | |
383 | } else { | |
384 | *(outwalk++) = '?'; | |
385 | } | |
386 | walk += strlen("noise"); | |
387 | } | |
388 | ||
389 | if (BEGINS_WITH(walk+1, "essid")) { | |
390 | if (info.flags & WIRELESS_INFO_FLAG_HAS_ESSID) | |
391 | outwalk += sprintf(outwalk, "%s", info.essid); | |
392 | else | |
393 | *(outwalk++) = '?'; | |
394 | walk += strlen("essid"); | |
395 | } | |
396 | ||
397 | if (BEGINS_WITH(walk+1, "ip")) { | |
398 | outwalk += sprintf(outwalk, "%s", ip_address); | |
399 | walk += strlen("ip"); | |
400 | } | |
362 | outwalk += sprintf(outwalk, "%d", info.quality); | |
363 | } else { | |
364 | *(outwalk++) = '?'; | |
365 | } | |
366 | walk += strlen("quality"); | |
367 | } | |
368 | ||
369 | if (BEGINS_WITH(walk + 1, "signal")) { | |
370 | if (info.flags & WIRELESS_INFO_FLAG_HAS_SIGNAL) { | |
371 | if (info.signal_level_max) | |
372 | outwalk += sprintf(outwalk, "%03d%%", PERCENT_VALUE(info.signal_level, info.signal_level_max)); | |
373 | else | |
374 | outwalk += sprintf(outwalk, "%d dBm", info.signal_level); | |
375 | } else { | |
376 | *(outwalk++) = '?'; | |
377 | } | |
378 | walk += strlen("signal"); | |
379 | } | |
380 | ||
381 | if (BEGINS_WITH(walk + 1, "noise")) { | |
382 | if (info.flags & WIRELESS_INFO_FLAG_HAS_NOISE) { | |
383 | if (info.noise_level_max) | |
384 | outwalk += sprintf(outwalk, "%03d%%", PERCENT_VALUE(info.noise_level, info.noise_level_max)); | |
385 | else | |
386 | outwalk += sprintf(outwalk, "%d dBm", info.noise_level); | |
387 | } else { | |
388 | *(outwalk++) = '?'; | |
389 | } | |
390 | walk += strlen("noise"); | |
391 | } | |
392 | ||
393 | if (BEGINS_WITH(walk + 1, "essid")) { | |
394 | if (info.flags & WIRELESS_INFO_FLAG_HAS_ESSID) | |
395 | outwalk += sprintf(outwalk, "%s", info.essid); | |
396 | else | |
397 | *(outwalk++) = '?'; | |
398 | walk += strlen("essid"); | |
399 | } | |
400 | ||
401 | if (BEGINS_WITH(walk + 1, "frequency")) { | |
402 | if (info.flags & WIRELESS_INFO_FLAG_HAS_FREQUENCY) | |
403 | outwalk += sprintf(outwalk, "%1.1f GHz", info.frequency / 1e9); | |
404 | else | |
405 | *(outwalk++) = '?'; | |
406 | walk += strlen("frequency"); | |
407 | } | |
408 | ||
409 | if (BEGINS_WITH(walk + 1, "ip")) { | |
410 | outwalk += sprintf(outwalk, "%s", ip_address); | |
411 | walk += strlen("ip"); | |
412 | } | |
401 | 413 | |
402 | 414 | #ifdef LINUX |
403 | if (BEGINS_WITH(walk+1, "bitrate")) { | |
404 | char br_buffer[128]; | |
405 | ||
406 | iw_print_bitrate(br_buffer, sizeof(br_buffer), info.bitrate); | |
407 | ||
408 | outwalk += sprintf(outwalk, "%s", br_buffer); | |
409 | walk += strlen("bitrate"); | |
410 | } | |
411 | #endif | |
412 | } | |
415 | if (BEGINS_WITH(walk + 1, "bitrate")) { | |
416 | char br_buffer[128]; | |
417 | ||
418 | iw_print_bitrate(br_buffer, sizeof(br_buffer), info.bitrate); | |
419 | ||
420 | outwalk += sprintf(outwalk, "%s", br_buffer); | |
421 | walk += strlen("bitrate"); | |
422 | } | |
423 | #endif | |
424 | } | |
413 | 425 | |
414 | 426 | out: |
415 | END_COLOR; | |
416 | OUTPUT_FULL_TEXT(buffer); | |
427 | END_COLOR; | |
428 | OUTPUT_FULL_TEXT(buffer); | |
417 | 429 | } |
0 | // vim:ts=8:expandtab | |
0 | // vim:ts=4:sw=4:expandtab | |
1 | 1 | #include <stdbool.h> |
2 | 2 | #include <glob.h> |
3 | 3 | #include <string.h> |
14 | 14 | * anything). kill() will return ESRCH if the process does not exist and 0 or |
15 | 15 | * EPERM (depending on the uid) if it exists. |
16 | 16 | * |
17 | * If multiple files match the glob pattern, all of them will be checked until | |
18 | * the first running process is found. | |
19 | * | |
17 | 20 | */ |
18 | 21 | bool process_runs(const char *path) { |
19 | static char pidbuf[16]; | |
20 | static glob_t globbuf; | |
21 | memset(pidbuf, 0, sizeof(pidbuf)); | |
22 | static char pidbuf[16]; | |
23 | static glob_t globbuf; | |
24 | memset(pidbuf, 0, sizeof(pidbuf)); | |
22 | 25 | |
23 | if (glob(path, GLOB_NOCHECK | GLOB_TILDE, NULL, &globbuf) < 0) | |
24 | die("glob() failed\n"); | |
25 | if (!slurp((globbuf.gl_pathc > 0 ? globbuf.gl_pathv[0] : path), pidbuf, sizeof(pidbuf))) { | |
26 | globfree(&globbuf); | |
27 | return false; | |
26 | if (glob(path, GLOB_NOCHECK | GLOB_TILDE, NULL, &globbuf) < 0) | |
27 | die("glob() failed\n"); | |
28 | if (globbuf.gl_pathc == 0) { | |
29 | /* No glob matches, the specified path does not contain a wildcard. */ | |
30 | globfree(&globbuf); | |
31 | if (!slurp(path, pidbuf, sizeof(pidbuf))) | |
32 | return false; | |
33 | return (kill(strtol(pidbuf, NULL, 10), 0) == 0 || errno == EPERM); | |
34 | } | |
35 | for (size_t i = 0; i < globbuf.gl_pathc; i++) { | |
36 | if (!slurp(globbuf.gl_pathv[i], pidbuf, sizeof(pidbuf))) { | |
37 | globfree(&globbuf); | |
38 | return false; | |
28 | 39 | } |
29 | globfree(&globbuf); | |
40 | if (kill(strtol(pidbuf, NULL, 10), 0) == 0 || errno == EPERM) { | |
41 | globfree(&globbuf); | |
42 | return true; | |
43 | } | |
44 | } | |
45 | globfree(&globbuf); | |
30 | 46 | |
31 | return (kill(strtol(pidbuf, NULL, 10), 0) == 0 || errno == EPERM); | |
47 | return false; | |
32 | 48 | } |