Xinqi Bao's Git

add config checks on startup for better error handling
[slstatus.git] / slstatus.c
1 /* See LICENSE file for copyright and license details. */
2
3 /* global libraries */
4 #include <alsa/asoundlib.h>
5 #include <fcntl.h>
6 #include <stdarg.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <time.h>
13 #include <unistd.h>
14 #include <X11/Xlib.h>
15
16 /* local libraries */
17 #include "config.h"
18
19 /* functions */
20 void setstatus(char *str);
21 int config_check();
22 char *smprintf(char *fmt, ...);
23 char *get_battery();
24 char *get_cpu_temperature();
25 char *get_cpu_usage();
26 char *get_datetime();
27 char *get_ram_usage();
28 char *get_volume();
29 char *get_wifi_signal();
30
31 /* global variables */
32 static Display *dpy;
33
34 /* set statusbar (WM_NAME) */
35 void
36 setstatus(char *str)
37 {
38 XStoreName(dpy, DefaultRootWindow(dpy), str);
39 XSync(dpy, False);
40 }
41
42 /* smprintf function */
43 char *
44 smprintf(char *fmt, ...)
45 {
46 va_list fmtargs;
47 char *ret = NULL;
48 va_start(fmtargs, fmt);
49 if (vasprintf(&ret, fmt, fmtargs) < 0)
50 return NULL;
51 va_end(fmtargs);
52
53 return ret;
54 }
55
56 #define CHECK_FILE(X,Y) do { \
57 if (stat(X,&Y) < 0) return -1; \
58 if (!S_ISREG(Y.st_mode)) return -1; \
59 } while (0);
60
61 /* check configured paths */
62 int
63 config_check()
64 {
65 struct stat fs;
66 CHECK_FILE(batterynowfile, fs);
67 CHECK_FILE(batteryfullfile, fs);
68 CHECK_FILE(tempfile, fs);
69 return 0;
70 }
71
72 /* battery percentage */
73 char *
74 get_battery()
75 {
76 int now, full, perc;
77 FILE *fp;
78
79 /* open battery now file */
80 if (!(fp = fopen(batterynowfile, "r"))) {
81 fprintf(stderr, "Error opening battery file.");
82 exit(1);
83 }
84
85 /* read value */
86 fscanf(fp, "%i", &now);
87
88 /* close battery now file */
89 fclose(fp);
90
91 /* open battery full file */
92 if (!(fp = fopen(batteryfullfile, "r"))) {
93 fprintf(stderr, "Error opening battery file.");
94 exit(1);
95 }
96
97 /* read value */
98 fscanf(fp, "%i", &full);
99
100 /* close battery full file */
101 fclose(fp);
102
103 /* calculate percent */
104 perc = now / (full / 100);
105
106 /* return perc as string */
107 return smprintf("%d%%", perc);
108 }
109
110 /* cpu temperature */
111 char *
112 get_cpu_temperature()
113 {
114 int temperature;
115 FILE *fp;
116
117 /* open temperature file */
118 if (!(fp = fopen(tempfile, "r"))) {
119 fprintf(stderr, "Could not open temperature file.\n");
120 exit(1);
121 }
122
123 /* extract temperature */
124 fscanf(fp, "%d", &temperature);
125
126 /* close temperature file */
127 fclose(fp);
128
129 /* return temperature in degrees */
130 return smprintf("%d°C", temperature / 1000);
131 }
132
133 /* cpu percentage */
134 char *
135 get_cpu_usage()
136 {
137 int perc;
138 long double a[4], b[4];
139 FILE *fp;
140
141 /* open stat file */
142 if (!(fp = fopen("/proc/stat","r"))) {
143 fprintf(stderr, "Error opening stat file.");
144 exit(1);
145 }
146
147 /* read values */
148 fscanf(fp, "%*s %Lf %Lf %Lf %Lf", &a[0], &a[1], &a[2], &a[3]);
149
150 /* close stat file */
151 fclose(fp);
152
153 /* wait a second (for avg values) */
154 sleep(1);
155
156 /* open stat file */
157 if (!(fp = fopen("/proc/stat","r"))) {
158 fprintf(stderr, "Error opening stat file.");
159 exit(1);
160 }
161
162 /* read values */
163 fscanf(fp, "%*s %Lf %Lf %Lf %Lf", &b[0], &b[1], &b[2], &b[3]);
164
165 /* close stat file */
166 fclose(fp);
167
168 /* calculate avg in this second */
169 perc = 100 * ((b[0]+b[1]+b[2]) - (a[0]+a[1]+a[2])) / ((b[0]+b[1]+b[2]+b[3]) - (a[0]+a[1]+a[2]+a[3]));
170
171 /* return perc as string */
172 return smprintf("%d%%", perc);
173 }
174
175 /* date and time */
176 char *
177 get_datetime()
178 {
179 time_t tm;
180 size_t bufsize = 64;
181 char *buf = malloc(bufsize);
182
183 /* get time in format */
184 time(&tm);
185 if(!strftime(buf, bufsize, timeformat, localtime(&tm))) {
186 fprintf(stderr, "Strftime failed.\n");
187 exit(1);
188 }
189
190 /* return time */
191 return smprintf("%s", buf);
192 }
193
194 /* ram percentage */
195 char *
196 get_ram_usage()
197 {
198 int perc;
199 long total, free, buffers, cached;
200 FILE *fp;
201
202 /* open meminfo file */
203 if (!(fp = fopen("/proc/meminfo", "r"))) {
204 fprintf(stderr, "Error opening meminfo file.");
205 exit(1);
206 }
207
208 /* read the values */
209 fscanf(fp, "MemTotal: %ld kB\n", &total);
210 fscanf(fp, "MemFree: %ld kB\n", &free);
211 fscanf(fp, "MemAvailable: %ld kB\nBuffers: %ld kB\n", &buffers, &buffers);
212 fscanf(fp, "Cached: %ld kB\n", &cached);
213
214 /* close meminfo file */
215 fclose(fp);
216
217 /* calculate percentage */
218 perc = 100 * ((total - free) - (buffers + cached)) / total;
219
220 /* return perc as string */
221 return smprintf("%d%%", perc);
222 }
223
224 /* alsa volume percentage */
225 char *
226 get_volume()
227 {
228 int mute = 0;
229 long vol = 0, max = 0, min = 0;
230
231 /* get volume from alsa */
232 snd_mixer_t *handle;
233 snd_mixer_elem_t *pcm_mixer, *mas_mixer;
234 snd_mixer_selem_id_t *vol_info, *mute_info;
235 snd_mixer_open(&handle, 0);
236 snd_mixer_attach(handle, soundcard);
237 snd_mixer_selem_register(handle, NULL, NULL);
238 snd_mixer_load(handle);
239 snd_mixer_selem_id_malloc(&vol_info);
240 snd_mixer_selem_id_malloc(&mute_info);
241 snd_mixer_selem_id_set_name(vol_info, channel);
242 snd_mixer_selem_id_set_name(mute_info, channel);
243 pcm_mixer = snd_mixer_find_selem(handle, vol_info);
244 mas_mixer = snd_mixer_find_selem(handle, mute_info);
245 snd_mixer_selem_get_playback_volume_range((snd_mixer_elem_t *)pcm_mixer, &min, &max);
246 snd_mixer_selem_get_playback_volume((snd_mixer_elem_t *)pcm_mixer, SND_MIXER_SCHN_MONO, &vol);
247 snd_mixer_selem_get_playback_switch(mas_mixer, SND_MIXER_SCHN_MONO, &mute);
248 if (vol_info)
249 snd_mixer_selem_id_free(vol_info);
250 if (mute_info)
251 snd_mixer_selem_id_free(mute_info);
252 if (handle)
253 snd_mixer_close(handle);
254
255 /* return the string (mute) */
256 if (!mute)
257 return smprintf("mute");
258 else
259 return smprintf("%d%%", (vol * 100) / max);
260 }
261
262 /* wifi percentage */
263 char *
264 get_wifi_signal()
265 {
266 int bufsize = 255;
267 int strength;
268 char buf[bufsize];
269 char *datastart;
270 char path_start[16] = "/sys/class/net/";
271 char path_end[11] = "/operstate";
272 char path[32];
273 char status[5];
274 char needle[sizeof wificard + 1];
275 FILE *fp;
276
277 /* generate the path name */
278 memset(path, 0, sizeof path);
279 strcat(path, path_start);
280 strcat(path, wificard);
281 strcat(path, path_end);
282
283 /* open wifi file */
284 if(!(fp = fopen(path, "r"))) {
285 fprintf(stderr, "Error opening wifi operstate file.");
286 exit(1);
287 }
288
289 /* read the status */
290 fgets(status, 5, fp);
291
292 /* close wifi file */
293 fclose(fp);
294
295 /* check if interface down */
296 if(strcmp(status, "up\n") != 0){
297 return "n/a";
298 }
299
300 /* open wifi file */
301 if (!(fp = fopen("/proc/net/wireless", "r"))) {
302 fprintf(stderr, "Error opening wireless file.");
303 exit(1);
304 }
305
306 /* extract the signal strength */
307 strcpy(needle, wificard);
308 strcat(needle, ":");
309 fgets(buf, bufsize, fp);
310 fgets(buf, bufsize, fp);
311 fgets(buf, bufsize, fp);
312 if ((datastart = strstr(buf, needle)) != NULL) {
313 datastart = strstr(buf, ":");
314 sscanf(datastart + 1, " %*d %d %*d %*d %*d %*d %*d %*d %*d %*d", &strength);
315 }
316
317 /* close wifi file */
318 fclose(fp);
319
320 /* return strength in percent */
321 return smprintf("%d%%", strength);
322 }
323
324 /* main function */
325 int
326 main()
327 {
328 char status[1024];
329 char *battery = NULL;
330 char *cpu_temperature = NULL;
331 char *cpu_usage = NULL;
332 char *datetime = NULL;
333 char *ram_usage = NULL;
334 char *volume = NULL;
335 char *wifi_signal = NULL;
336
337 /* check config for sanity */
338 if (config_check() < 0) {
339 fprintf(stderr, "Config error, please check paths and recompile\n");
340 exit(1);
341 }
342 /* open display */
343 if (!(dpy = XOpenDisplay(0x0))) {
344 fprintf(stderr, "Cannot open display!\n");
345 exit(1);
346 }
347
348 /* return status every second */
349 for (;;) {
350 /* assign the values */
351 battery = get_battery();
352 cpu_temperature = get_cpu_temperature();
353 cpu_usage = get_cpu_usage();
354 datetime = get_datetime();
355 ram_usage = get_ram_usage();
356 volume = get_volume();
357 wifi_signal = get_wifi_signal();
358
359 /* return the status */
360 sprintf(status, FORMATSTRING, ARGUMENTS);
361 setstatus(status);
362
363 /* free the values */
364 free(battery);
365 free(cpu_temperature);
366 free(cpu_usage);
367 free(datetime);
368 free(ram_usage);
369 free(volume);
370 free(wifi_signal);
371 sleep(update_interval);
372 }
373
374 /* close display */
375 XCloseDisplay(dpy);
376
377 /* exit successfully */
378 return 0;
379 }