1 /*
2  * ascpu is the CPU statistics monitor utility for X Windows
3  * Copyright (c) 1998-2005  Albert Dorofeev <albert@tigr.net>
4  * For the updates see http://www.tigr.net/
5  *
6  * This software is distributed under GPL. For details see LICENSE file.
7  */
8 
9 #include <stdio.h>
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <unistd.h>
13 #include <ctype.h>
14 #include <stdlib.h>
15 #include <math.h>
16 #include <time.h>
17 #ifdef	__FreeBSD__
18 #include <nlist.h>
19 #include <fcntl.h>
20 #include <kvm.h>
21 #include <sys/types.h>
22 #include <err.h>
23 #include <string.h>
24 #include <sys/resource.h>
25 #include <sys/sysctl.h>
26 #include <sys/wait.h>
27 #endif
28 
29 #ifdef __hpux__
30 #include <sys/pstat.h>
31 #include <sys/dk.h>
32 #endif
33 
34 #ifdef _AIX32   /* AIX > 3.1 */
35 #include <nlist.h>
36 #include <sys/param.h>
37 #include <sys/sysinfo.h>
38 #endif /* _AIX32 */
39 
40 #include <X11/Xlib.h>
41 #include <X11/xpm.h>
42 #include <X11/Xatom.h>
43 
44 #include "x_color.h"
45 #include "background.xpm"
46 #include "state.h"
47 
48 #define FOR_CLICK 1
49 
50 #ifdef FOR_CLICK
51 extern char Command[255];
52 #endif
53 
54 struct ascpu_state state;
55 
56 /* nice idea from ascd */
57 typedef struct _XpmIcon {
58     Pixmap pixmap;
59     Pixmap mask;
60     XpmAttributes attributes;
61 } XpmIcon;
62 
63 XpmIcon backgroundXpm;
64 
65 /* X windows related global variables */
66 Display * mainDisplay = 0;      /* The display we are working on */
67 Window Root;                    /* The root window of X11 */
68 Window mainWindow;              /* Application window */
69 Window iconWindow;              /* Icon window */
70 XGCValues mainGCV;              /* graphics context values */
71 GC mainGC;                      /* Graphics context */
72 Atom wm_delete_window;
73 Atom wm_protocols;
74 
75 Pixel back_pix, fore_pix;
76 
77 /* background pixmap colors */
78 char bgpixmap_color[3][50];
79 
80 /* pixels we need */
81 Pixel pix[4];
82 
83 /* last time we updated */
84 time_t last_time = 0;
85 
86 /* requests for update */
87 int update_request = 0;
88 
89 #ifdef	__FreeBSD__
90 static int		cp_time_mib[2];
91 static int		cp_times_mib[2];
92 static kvm_t		*kd;
93 static struct nlist	nlst[] = {
94 	{"_cp_time"}, {0}
95 };
96 #endif
97 
98 #ifdef	__linux__
99 char	proc_extended = 0;
100 #endif
101 
102 #ifdef _AIX32   /* AIX > 3.1 */
103 /* Kernel memory file */
104 #define KMEM_FILE "/dev/kmem"
105 /* Descriptor of kernel memory file */
106 static int  kmem;
107 /* Offset of sysinfo structure in kernel memory file */
108 static long sysinfo_offset;
109 /* Structure to access kernel symbol table */
110 static struct nlist namelist[] = {
111   { {"sysinfo"}, 0, 0, {0}, 0, 0 },
112   { {0},         0, 0, {0}, 0, 0 }
113 };
114 #endif /* _AIX32 */
115 
116 /*
117  * The information over the CPU load is always kept in 4 variables
118  * The order is:
119  * 	user
120  * 	nice
121  * 	system
122  *	interrupt(FreeBSD specific)
123  * 	idle
124  */
125 struct cpu_load_struct {
126 	unsigned long int load[5];
127 };
128 
129 /*
130  * The structure for the line sizes - the same as the
131  * CPU structure only in pixels - ready to draw
132  */
133 struct line_size_struct {
134 	unsigned short int len[4];
135 };
136 /* last time and just now read values */
137 struct cpu_load_struct last = {{0, 0, 0, 0}};
138 struct cpu_load_struct fresh = {{0, 0, 0, 0, 0}};
139 
140 /* The running history window values: load, calculated lines, counter */
141 struct cpu_load_struct running_last = {{0, 0, 0, 0}};
142 struct line_size_struct running_lines = {{0, 0, 0, 0}};
143 unsigned long int running_counter = 0;
144 
145 /* the average load gadget values */
146 struct cpu_load_struct average_diff = {{0, 0, 0, 0}};
147 struct line_size_struct average_lines = {{0, 0, 0, 0}};
148 struct {
149 	float load[4];
150 } average = {{0, 0, 0, 0}};
151 struct cpu_load_struct *average_history = 0;
152 unsigned long int average_counter = 0;
153 unsigned long int average_ptr = 0;
154 
155 
156 /*
157  * This function clears up all X related
158  * stuff and exits. It is called in case
159  * of emergencies .
160  */
161 
ascpu_cleanup()162 void ascpu_cleanup()
163 {
164         if ( mainDisplay ) {
165                 XCloseDisplay(mainDisplay);
166         }
167 	if ( average_history )
168 		free( average_history );
169         exit(0);
170 }
171 
172 
update_running()173 void update_running()
174 {
175 	unsigned short total;
176 	float factor;
177 	struct cpu_load_struct running_diff;
178 	int i, last_offset, new_offset;
179         long diff_buf;
180 
181 	++running_counter;
182 	if ( running_counter < state.hist_samples )
183 		return;
184 
185 	total = 0;
186 	for (i=0; i<4; ++i) {
187 		diff_buf = fresh.load[i] - running_last.load[i];
188                 running_diff.load[i] = (diff_buf < 0) ? 0:diff_buf;
189 		total += running_diff.load[i];
190 	}
191 	if (total) {
192 		factor = ((float)(backgroundXpm.attributes.height - 2)) / total;
193 		for ( i=0; i<4; ++i )
194 			running_lines.len[i] =
195 				rint((float)running_diff.load[i] * factor);
196 		/* Make sure we have the exact number of pixels to
197 		 * draw - errors of rounding may make up for an extra
198 		 * or a missing pixel */
199 		while ( ( running_lines.len[0] + running_lines.len[1] +
200 			running_lines.len[2] + running_lines.len[3] ) >
201 				(backgroundXpm.attributes.height - 2) ) {
202 			if ( running_lines.len[0] )
203 				--running_lines.len[0];
204 			else if ( running_lines.len[1] )
205 				--running_lines.len[1];
206 			else if ( running_lines.len[2] )
207 				--running_lines.len[2];
208 			else
209 				--running_lines.len[3];
210 		}
211 		while ( ( running_lines.len[0] + running_lines.len[1] +
212 			running_lines.len[2] + running_lines.len[3] ) <
213 				(backgroundXpm.attributes.height - 2) ) {
214 			++running_lines.len[3];
215 		}
216 	} else {
217 		/* Just in case something goes wrong *shrug* */
218 		running_lines.len[0] = running_lines.len[1] =
219 		running_lines.len[2] = 0;
220 		running_lines.len[3] = backgroundXpm.attributes.height - 2;
221 	}
222 
223 #ifdef DEBUG
224 	printf("Running :	%d	%d	%d	%d	=%d\n",
225 			running_lines.len[0], running_lines.len[1],
226 			running_lines.len[2], running_lines.len[3],
227 			running_lines.len[0] + running_lines.len[1] +
228 			running_lines.len[2] + running_lines.len[3] );
229 #endif
230 	running_counter = 0;
231 	memcpy( &running_last, &fresh, sizeof(running_last) );
232 
233 	/*
234 	 * And now draw the lines we got into the bitmap
235 	 * of the background. We shift what we had there
236 	 * before to the left and draw an extra line
237 	 * that we just calculated on the right side.
238 	 */
239         XCopyArea(
240                 mainDisplay,
241                 backgroundXpm.pixmap,
242                 backgroundXpm.pixmap,
243                 mainGC,
244                 7,
245                 1,
246                 (backgroundXpm.attributes.width - 8),
247                 (backgroundXpm.attributes.height - 2),
248                 6,
249                 1
250                 );
251 	last_offset = new_offset = 1;
252 	for ( i=0; i<4; ++i ) {
253 		mainGCV.foreground = pix[i];
254 		XChangeGC(
255 			mainDisplay,
256 			mainGC,
257 			GCForeground,
258 			&mainGCV
259 			);
260 		new_offset += running_lines.len[3-i];
261 		XDrawLine(
262 			mainDisplay,
263 			backgroundXpm.pixmap,
264 			mainGC,
265 			(backgroundXpm.attributes.width - 2),
266 			last_offset,
267 			(backgroundXpm.attributes.width - 2),
268 			new_offset
269 			);
270 		last_offset = new_offset;
271 	}
272 	++update_request;
273 }
274 
275 /*
276  * Calculate the average load values when the average
277  * time is 1 - this is the case when we just have the
278  * momentary values.
279  */
momentary_average()280 void momentary_average()
281 {
282 	unsigned short total;
283 	float factor;
284 	int i;
285 
286 	total = average_diff.load[0] + average_diff.load[1] +
287 		average_diff.load[2] + average_diff.load[3];
288 	if (total) {
289 		factor = ((float)(backgroundXpm.attributes.height - 2)) / total;
290 		for ( i=0; i<4; ++i )
291 			average_lines.len[i] =
292 				rint((float)average_diff.load[i] * factor);
293 		/* Make sure we have the exact number of pixels to
294 		 * draw - errors of rounding may make up for an extra
295 		 * or a missing pixel */
296 		while ( ( average_lines.len[0] + average_lines.len[1] +
297 			average_lines.len[2] + average_lines.len[3] ) >
298 				(backgroundXpm.attributes.height - 2) ) {
299 			if ( average_lines.len[0] )
300 				--average_lines.len[0];
301 			else if ( average_lines.len[1] )
302 				--average_lines.len[1];
303 			else if ( average_lines.len[2] )
304 				--average_lines.len[2];
305 			else
306 				--average_lines.len[3];
307 		}
308 		while ( ( average_lines.len[0] + average_lines.len[1] +
309 			average_lines.len[2] + average_lines.len[3] ) <
310 				(backgroundXpm.attributes.height - 2) ) {
311 			++average_lines.len[3];
312 		}
313 	} else {
314 		/* Just in case something goes wrong *shrug* */
315 		average_lines.len[0] = average_lines.len[1] =
316 		average_lines.len[2] = 0;
317 		average_lines.len[3] = backgroundXpm.attributes.height - 2;
318 	}
319 
320 #ifdef DEBUG
321 	printf("Average :	%d	%d	%d	%d	=%d\n",
322 			average_lines.len[0], average_lines.len[1],
323 			average_lines.len[2], average_lines.len[3],
324 			average_lines.len[0] + average_lines.len[1] +
325 			average_lines.len[2] + average_lines.len[3] );
326 #endif
327 }
328 
329 /*
330  * Calculate and update the value of the average load
331  * for the past hist_samples intervals. The oldest value
332  * is thrown out of the average and the new value is added.
333  */
real_average()334 void real_average()
335 {
336 	int i;
337 	unsigned short total;
338 	float factor;
339 
340 	if ( average_counter >= state.avg_samples ) {
341 		for ( i=0; i<4; ++i ) {
342 			average.load[i] =
343 				(average.load[i] * average_counter -
344 				average_history[average_ptr].load[i]) /
345 				( average_counter - 1 );
346 		}
347 		--average_counter;
348 	}
349 	for ( i=0; i<4; ++i ) {
350 		average_history[average_ptr].load[i] =
351 			average_diff.load[i];
352 		average.load[i] = (average.load[i] * average_counter +
353 				average_history[average_ptr].load[i]) /
354 				( average_counter + 1 );
355 	}
356 	++average_counter;
357 	++average_ptr;
358 	if ( average_ptr >= state.avg_samples ) average_ptr = 0;
359 
360 	/*
361 	 * And now the lines for the indicator
362 	 */
363 	total = average.load[0] + average.load[1] +
364 		average.load[2] + average.load[3];
365 	if (total) {
366 		factor = ((float)(backgroundXpm.attributes.height - 2)) / total;
367 		for ( i=0; i<4; ++i )
368 			average_lines.len[i] =
369 				rint((float)average.load[i] * factor);
370 		/* Make sure we have the exact number of pixels to
371 		 * draw - errors of rounding may make up for an extra
372 		 * or a missing pixel */
373 		while ( ( average_lines.len[0] + average_lines.len[1] +
374 			average_lines.len[2] + average_lines.len[3] ) >
375 				(backgroundXpm.attributes.height - 2) ) {
376 			if ( average_lines.len[0] )
377 				--average_lines.len[0];
378 			else if ( average_lines.len[1] )
379 				--average_lines.len[1];
380 			else if ( average_lines.len[2] )
381 				--average_lines.len[2];
382 			else
383 				--average_lines.len[3];
384 		}
385 		while ( ( average_lines.len[0] + average_lines.len[1] +
386 			average_lines.len[2] + average_lines.len[3] ) <
387 				(backgroundXpm.attributes.height - 2) ) {
388 			++average_lines.len[3];
389 		}
390 	} else {
391 		/* Just in case something goes wrong *shrug* */
392 		average_lines.len[0] = average_lines.len[1] =
393 		average_lines.len[2] = 0;
394 		average_lines.len[3] = backgroundXpm.attributes.height - 2;
395 	}
396 
397 #ifdef DEBUG
398 	printf("Average :	%d	%d	%d	%d	=%d\n",
399 			average_lines.len[0], average_lines.len[1],
400 			average_lines.len[2], average_lines.len[3],
401 			average_lines.len[0] + average_lines.len[1] +
402 			average_lines.len[2] + average_lines.len[3] );
403 #endif
404 }
405 
406 /*
407  * Trigger calculation of the average value and then
408  * draw the calculated lines.
409  */
update_average()410 void update_average()
411 {
412 	int offset;
413 	int i;
414         long diff_buf;
415 	for (i=0; i<4; ++i) {
416 		diff_buf = fresh.load[i] - last.load[i];
417                 average_diff.load[i] = (diff_buf < 0) ? 0:diff_buf;
418 	}
419         if ( state.avg_samples > 1 ) {
420 		real_average();
421 	} else {
422 		momentary_average();
423 	}
424 	/*
425 	 * And now draw the line that was calculated
426 	 */
427 	offset = 1;
428 	for ( i=0; i<4; ++i ) {
429 		mainGCV.foreground = pix[i];
430 		XChangeGC(
431 			mainDisplay,
432 			mainGC,
433 			GCForeground,
434 			&mainGCV
435 			);
436 		XFillRectangle(
437 			mainDisplay,
438 			backgroundXpm.pixmap,
439 			mainGC,
440 			1,
441 			offset,
442 			3,
443 			average_lines.len[3-i]
444 			);
445 		offset += average_lines.len[3-i];
446 	}
447 	++update_request;
448 }
449 
draw_window(Window win)450 void draw_window(Window win)
451 {
452         XCopyArea(
453                 mainDisplay,
454                 backgroundXpm.pixmap,
455                 win,
456                 mainGC,
457                 0,
458                 0,
459                 backgroundXpm.attributes.width,
460                 backgroundXpm.attributes.height,
461                 0,
462                 0
463                 );
464 }
465 
466 
467 static char buffer[MAX_CPU*40]; /* about 35 chars per line of CPU info */
468 static char scan_format[MAX_CPU*30]; /* 26 is correct I think but not sure */
469 /*
470  * Prepares the format for reading the /proc/stat
471  */
prepare_format()472 void prepare_format()
473 {
474 	int i;
475 	scan_format[0] = '\0';
476 #ifdef __linux__
477 	int fd, len, major, minor, micro;
478 	if ((fd = open("/proc/sys/kernel/osrelease", O_RDONLY)) == -1) {
479 		printf("ascpu : cannot open /proc/sys/kernel/osrelease\n");
480 		exit(1);
481 	}
482 	if ((len = read(fd, buffer, sizeof(buffer)-1)) < 0) {
483 		printf("ascpu : cannot read /proc/sys/kernel/osrelease\n");
484 		exit(1);
485 	}
486 	buffer[len] = '\0';
487 	close(fd);
488 	if (!sscanf(buffer, "%d.%d.%d", &major, &minor, &micro)) {
489 		printf("ascpu : cannot get Linux Major/Minor version\n");
490 		exit(1);
491 	}
492 	if (major > 2 || (major == 2 && minor > 6) || (major == 2 && minor == 6 && micro >= 10)) {
493 #ifdef DEBUG
494 		printf("Linux Kernel >= 2.6.10 detected\n");
495 #endif
496 		proc_extended = 2;
497 	} else if (major == 2 && minor >= 5) {
498 #ifdef DEBUG
499 		printf("Linux Kernel >= 2.5 and < 2.6.10 detected\n");
500 #endif
501 		proc_extended = 1;
502 	} else {
503 		proc_extended = 0;
504 #ifdef DEBUG
505 		printf("Linux Kernel < 2.5 detected\n");
506 #endif
507 	}
508 	if ( state.cpu_number < 0 ) {
509 		sprintf(scan_format, proc_extended?((proc_extended-1)?"cpu %%ld %%ld %%ld %%ld %%*ld %%*ld %%*ld %%*ld\n":"cpu %%ld %%ld %%ld %%ld %%*ld %%*ld %%*ld\n"):"cpu %%ld %%ld %%ld %%ld\n");
510 	} else {
511 		sprintf(scan_format, proc_extended?((proc_extended-1)?"cpu %%*ld %%*ld %%*ld %%*ld %%*ld %%*ld %%*ld %%*ld\n":"cpu %%*ld %%*ld %%*ld %%*ld %%*ld %%*ld %%*ld\n"):"cpu %%*ld %%*ld %%*ld %%*ld\n");
512 		for (i=0; i<state.cpu_number; ++i) {
513 			sprintf(&scan_format[strlen(scan_format)],
514 				proc_extended?((proc_extended-1)?"cpu%d %%*ld %%*ld %%*ld %%*ld %%*ld %%*ld %%*ld %%*ld\n":"cpu%d %%*ld %%*ld %%*ld %%*ld %%*ld %%*ld %%*ld\n"):"cpu%d %%*ld %%*ld %%*ld %%*ld\n",
515 				i);
516 		}
517 		sprintf(&scan_format[strlen(scan_format)],
518 			proc_extended?((proc_extended-1)?"cpu%d %%ld %%ld %%ld %%ld %%*ld %%*ld %%*ld %%*ld\n":"cpu%d %%ld %%ld %%ld %%ld %%*ld %%*ld %%*ld\n"):"cpu%d %%ld %%ld %%ld %%ld\n",
519 			state.cpu_number);
520 	}
521 #else
522 	if ( state.cpu_number < 0 ) {
523 		sprintf(scan_format, "cpu %%ld %%ld %%ld %%ld\n");
524 	} else {
525 		sprintf(scan_format, "cpu %%*ld %%*ld %%*ld %%*ld\n");
526 		for (i=0; i<state.cpu_number; ++i) {
527 			sprintf(&scan_format[strlen(scan_format)],
528 					"cpu%d %%*ld %%*ld %%*ld %%*ld\n",
529 					i);
530 		}
531 		sprintf(&scan_format[strlen(scan_format)],
532 				"cpu%d %%ld %%ld %%ld %%ld\n",
533 				state.cpu_number);
534 	}
535 #endif
536 #ifdef DEBUG
537 	printf("The format is [%s]\n", scan_format);
538 #endif
539 }
540 
541 /*
542  * reads the /proc/stat
543  */
read_stat()544 void read_stat()
545 {
546 	int fd, len;
547 
548 #ifdef __hpux__
549 	struct pst_dynamic store_pst_dynamic;
550 	long int *p;
551 	int i;
552 #endif
553 
554 #ifdef _AIX32   /* AIX > 3.1 */
555         time_t *p;
556         struct sysinfo sysinfo;
557 #endif
558 
559 	/*
560 	 * Save the old values
561 	 */
562 	memcpy(&last, &fresh, sizeof(last));
563 
564 #ifdef __FreeBSD__
565 	if (state.cpu_number >= 0 && cp_times_mib[0] != 0) {
566 		long cp_times[MAX_CPU][CPUSTATES];
567 		size_t cp_times_len = sizeof(cp_times);
568 		int error = sysctl(cp_times_mib, 2, cp_times, &cp_times_len, NULL, 0);
569 		if (error) {
570 			printf("ascpu: cannot sysctl cp_times\n");
571 			exit(1);
572 		}
573 
574 		long *cp_time = cp_times[state.cpu_number];
575 		fresh.load[0] = cp_time[CP_USER];
576 		fresh.load[1] = cp_time[CP_NICE];
577 		fresh.load[2] = cp_time[CP_SYS] + cp_time[CP_INTR];
578 		fresh.load[3] = cp_time[CP_IDLE];
579 	} else if (state.cpu_number == -1 && cp_time_mib[0] != 0) {
580 		long cp_time[CPUSTATES];
581 		size_t cp_time_len = sizeof(cp_time);
582 		int error = sysctl(cp_time_mib, 2, cp_time, &cp_time_len, NULL, 0);
583 		if (error) {
584 			printf("ascpu: cannot sysctl cp_time\n");
585 			exit(1);
586 		}
587 
588 		fresh.load[0] = cp_time[CP_USER];
589 		fresh.load[1] = cp_time[CP_NICE];
590 		fresh.load[2] = cp_time[CP_SYS] + cp_time[CP_INTR];
591 		fresh.load[3] = cp_time[CP_IDLE];
592 	} else {
593 		if (nlst[0].n_type == 0) {
594 			printf("ascpu : cannot get nlist\n");
595 			exit(1);
596 		}
597 		if (kvm_read(kd, nlst[0].n_value, &fresh, sizeof(fresh)) != sizeof(fresh)) {
598 			printf("ascpu : cannot read kvm\n");
599 			exit(1);
600 		}
601 		/* compatible with Linux(overwrite 'interrupt' with 'idle' field) */
602 		fresh.load[3] = fresh.load[4];
603 	}
604 #endif
605 
606 #ifdef __hpux__
607 
608 	/*
609         params: structure buf pointer, structure size, num of structs
610         some index. HP say always 1, 0 for the last two. the order in
611         fresh[] matches HP's psd_cpu_time[] and psd_mp_cpu_time[N][],
612         so I can use the indices in dk.h for everything
613 	*/
614 
615 	if( pstat_getdynamic( &store_pst_dynamic, \
616 			      sizeof store_pst_dynamic, 1, 0 )) {
617 
618 	  if( state.cpu_number < 0 ) {
619 	    /* give the average over all processors */
620 	    p = store_pst_dynamic.psd_cpu_time;
621 	  }
622 	  else {
623 	    /* user-specified processor only */
624 	    p = store_pst_dynamic.psd_mp_cpu_time[state.cpu_number];
625 	  }
626 
627 	  fresh.load[CP_USER] = p[CP_USER];
628 	  fresh.load[CP_NICE] = p[CP_NICE];
629 	  fresh.load[CP_SYS]  = p[CP_SYS] + p[CP_BLOCK] +\
630 	    p[CP_INTR] + p[CP_SSYS] + p[CP_SWAIT];
631 	  fresh.load[CP_IDLE] = p[CP_IDLE] + p[CP_WAIT];
632 
633 	}
634 	else {
635 	  printf("ascpu: Error while calling pstat_getdynamic(2)\n");
636 	  ascpu_cleanup();
637 	}
638 
639 #endif
640 
641 #ifdef _AIX32   /* AIX > 3.1 */
642         if (lseek (kmem, sysinfo_offset, SEEK_SET) != sysinfo_offset) {
643           printf("ascpu : cannot lseek %s\n", KMEM_FILE);
644           ascpu_cleanup();
645         }
646         if (read (kmem, (char *)&sysinfo, sizeof(struct sysinfo))
647             != sizeof(struct sysinfo)) {
648           printf("ascpu : cannot read %s\n", KMEM_FILE);
649           ascpu_cleanup();
650         }
651         p = sysinfo.cpu;
652         fresh.load[0] = p[CPU_USER];
653         fresh.load[1] = p[CPU_WAIT];
654         fresh.load[2] = p[CPU_KERNEL];
655         fresh.load[3] = fresh.load[4] = p[CPU_IDLE];
656 #endif /* _AIX32 */
657 
658 #ifdef __linux__
659 
660 	if ((fd = open(state.proc_stat_filename, O_RDONLY)) == -1) {
661 		printf("ascpu : cannot open %s\n", state.proc_stat_filename);
662 		exit(1);
663 	}
664 	len = read(fd, buffer, sizeof(buffer)-1);
665 	close(fd);
666 	if (len == 0) return; /* EOF? */
667 	if (len < 0) {
668 		printf("ascpu : cannot read %s\n", state.proc_stat_filename);
669 		exit(1);
670 	}
671 	buffer[len] = '\0';
672 
673 	len = sscanf(buffer, scan_format,
674 		&fresh.load[0],
675 		&fresh.load[1],
676 		&fresh.load[2],
677 		&fresh.load[3]);
678 	if ( len <= 0 ) {
679 		printf("ascpu : invalid character while reading %s\n",
680 			state.proc_stat_filename);
681 	}
682 #endif
683 	/*
684 	 * Check if need "nice" CPU time at all
685 	 */
686 	if ( state.no_nice ) {
687 		fresh.load[3] += fresh.load[1];
688 		fresh.load[1] = 0;
689 	}
690 #ifdef DEBUG
691 	printf("diff:		%ld	%ld	%ld	%ld	%ld\n",
692 		fresh.load[0] - last.load[0],
693 		fresh.load[1] - last.load[1],
694 		fresh.load[2] - last.load[2],
695 		fresh.load[3] - last.load[3],
696 		fresh.load[4] - last.load[4] );
697 #endif
698 }
699 
700 
701 #ifdef FOR_CLICK
702 /*
703  * This function executes an external command while
704  * checking whether we should drop the privileges.
705  *
706  * Since we might need privileges later we fork and
707  * then drop privileges in one of the instances which
708  * will then execute the command and die.
709  *
710  * This fixes the security hole for FreeBSD and AIX
711  * where this program needs privileges to access
712  * the system information.
713  */
ExecuteExternal()714 void ExecuteExternal()
715 {
716 	uid_t ruid, euid;
717 	int pid;
718 #ifdef DEBUG
719 	printf("ascpu: system(%s)\n",Command);
720 #endif
721 
722 	if (setgid(getgid()) != 0)
723 		err(1, "Can't drop setgid privileges");
724 	if (setuid(getuid()) != 0)
725 		err(1, "Can't drop setuid privileges");
726 
727 	if( ! Command ) {
728 		return;
729 	}
730 	ruid = getuid();
731 	euid = geteuid();
732 	if ( ruid == euid ) {
733 		system( Command );
734 		return;
735 	}
736 	pid = fork();
737 	if ( pid == -1 ) {
738 		printf("ascpu : fork() failed (%s), command not executed",
739 				strerror(errno));
740 		return;
741 	}
742 	if ( pid != 0 ) {
743 		/* parent process simply waits for the child and continues */
744 		if ( waitpid(pid, 0, 0) == -1 ) {
745 			printf("ascpu : waitpid() for child failed (%s)",
746 				strerror(errno));
747 		}
748 		return;
749 	}
750 	/*
751 	 * child process drops the privileges
752 	 * executes the command and dies
753 	 */
754 	if ( setuid(ruid) ) {
755 		printf("ascpu : setuid failed (%s), command not executed",
756 				strerror(errno));
757 		exit(127);
758 	}
759 	system( Command );
760 	exit(0);
761 }
762 #endif
763 
764 /*
765  * This checks for X11 events. We distinguish the following:
766 #ifdef FOR_CLICK
767  * - request to execute some command
768 #endif
769  * - request to repaint the window
770  * - request to quit (Close button)
771  */
CheckX11Events()772 void CheckX11Events()
773 {
774         XEvent Event;
775         while (XPending(mainDisplay)) {
776                 XNextEvent(mainDisplay, &Event);
777                 switch(Event.type)
778                 {
779 #ifdef FOR_CLICK
780 		case ButtonPress:
781 			ExecuteExternal();
782 			break;
783 #endif
784                 case Expose:
785                         if(Event.xexpose.count == 0) {
786 				++update_request;
787 			}
788                         break;
789                 case ClientMessage:
790                         if ((Event.xclient.message_type == wm_protocols)
791                           && (Event.xclient.data.l[0] == wm_delete_window))
792 			{
793 #ifdef DEBUG
794 				printf("caught wm_delete_window, closing\n");
795 #endif
796                                 ascpu_cleanup();
797 			}
798                         break;
799                 }
800         }
801 }
802 
ascpu_redraw()803 void ascpu_redraw()
804 {
805 	draw_window(mainWindow);
806 	draw_window(iconWindow);
807 	update_request = 0;
808 }
809 
ascpu_update()810 void ascpu_update()
811 {
812 	time_t cur_time;
813 	cur_time = time(0);
814 	if ( abs(cur_time - last_time) >= state.update_interval ) {
815 		last_time = cur_time;
816 		read_stat();
817 		update_running();
818 		update_average();
819 	}
820 	CheckX11Events();
821 	if ( update_request ) {
822 		ascpu_redraw();
823 	}
824 }
825 
ascpu_initialize(int argc,char ** argv,char * window_name,char * display_name,char * mainGeometry,int withdrawn,int iconic,int pushed_in,int sys_color_defined,char * sys_color,int nice_color_defined,char * nice_color,int user_color_defined,char * user_color,int idle_color_defined,char * idle_color)826 void ascpu_initialize(
827 			int argc, char** argv,
828 			char * window_name,
829 			char * display_name,
830 			char * mainGeometry,
831                         int withdrawn,
832                         int iconic,
833                         int pushed_in,
834 			int sys_color_defined,
835 			char * sys_color,
836 			int nice_color_defined,
837 			char * nice_color,
838 			int user_color_defined,
839 			char * user_color,
840 			int idle_color_defined,
841 			char * idle_color)
842 {
843 	int screen;
844 	Status status;
845 	XWindowAttributes winAttr;
846 	XSizeHints SizeHints;
847 	XTextProperty title;
848 	XClassHint classHint;
849 	int gravity;
850 	XWMHints WmHints;
851 	XEvent Event;
852 	int color_depth;
853 	int tmp;
854 	int result;
855 	int x_negative = 0;
856 	int y_negative = 0;
857 
858 	mainDisplay = XOpenDisplay(display_name);
859 	if ( ! mainDisplay ) {
860 		printf("ascpu : grrrr... can't open display %s. Sorry ...\n",
861 			XDisplayName(display_name));
862 		exit(1);
863 	}
864 	screen = DefaultScreen(mainDisplay);
865 	Root = RootWindow(mainDisplay, screen);
866 	back_pix = GetColor(state.bgcolor, mainDisplay, Root);
867 	fore_pix = GetColor(state.fgcolor, mainDisplay, Root);
868 	color_depth = DefaultDepth(mainDisplay, screen);
869 #ifdef DEBUG
870         printf("ascpu : detected color depth %d bpp, using %d bpp\n",
871                 color_depth, color_depth);
872 #endif
873 	/* adjust the background pixmap */
874 	if (pushed_in) {
875 		sprintf(bgpixmap_color[0],
876 			". c %s",
877 			DarkenCharColor(state.bgcolor, 1.6, mainDisplay, Root));
878 		sprintf(bgpixmap_color[1],
879 			"c c %s",
880 			state.bgcolor);
881 		sprintf(bgpixmap_color[2],
882 			"q c %s",
883 			LightenCharColor(state.bgcolor, 2.8, mainDisplay, Root));
884 	} else {
885 		sprintf(bgpixmap_color[2],
886 			"q c %s",
887 			DarkenCharColor(state.bgcolor, 1.2, mainDisplay, Root));
888 		sprintf(bgpixmap_color[1],
889 			"c c %s",
890 			state.bgcolor);
891 		sprintf(bgpixmap_color[0],
892 			". c %s",
893 			LightenCharColor(state.bgcolor, 2.5, mainDisplay, Root));
894 	}
895 	for (tmp = 0; tmp < 3; ++tmp)
896 		background[tmp+1] = bgpixmap_color[tmp];
897 
898 	backgroundXpm.attributes.valuemask |=
899                 (XpmReturnPixels | XpmReturnExtensions);
900         status = XpmCreatePixmapFromData(
901                 mainDisplay,                    /* display */
902                 Root,                           /* window */
903                 background,                     /* xpm */
904                 &backgroundXpm.pixmap,          /* resulting pixmap */
905                 &backgroundXpm.mask,
906                 &backgroundXpm.attributes
907                 );
908         if(status != XpmSuccess) {
909                 printf("ascpu : (%d) not enough free color cells for background.\n", status);
910 		ascpu_cleanup();
911         }
912 #ifdef DEBUG
913 	printf("bg pixmap %d x %d\n",
914 		backgroundXpm.attributes.width,
915 		backgroundXpm.attributes.height);
916 #endif
917 	if (strlen(mainGeometry)) {
918 		/* Check the user-specified size */
919 		result = XParseGeometry(
920 			mainGeometry,
921 			&SizeHints.x,
922 			&SizeHints.y,
923 			&SizeHints.width,
924 			&SizeHints.height
925 			);
926 		if (result & XNegative)
927 			x_negative = 1;
928 		if (result & YNegative)
929 			y_negative = 1;
930 	}
931 
932         SizeHints.flags= USSize|USPosition;
933         SizeHints.x = 0;
934         SizeHints.y = 0;
935         XWMGeometry(
936                 mainDisplay,
937                 screen,
938                 mainGeometry,
939                 NULL,
940                 1,
941                 & SizeHints,
942                 &SizeHints.x,
943                 &SizeHints.y,
944                 &SizeHints.width,
945                 &SizeHints.height,
946                 &gravity
947                 );
948         SizeHints.min_width =
949         SizeHints.max_width =
950         SizeHints.width = backgroundXpm.attributes.width;
951         SizeHints.min_height =
952         SizeHints.max_height =
953         SizeHints.height= backgroundXpm.attributes.height;
954         SizeHints.flags |= PMinSize|PMaxSize;
955 
956 	/* Correct the offsets if the X/Y are negative */
957 	SizeHints.win_gravity = NorthWestGravity;
958 	if (x_negative) {
959 		SizeHints.x -= SizeHints.width;
960 		SizeHints.win_gravity = NorthEastGravity;
961 	}
962 	if (y_negative) {
963 		SizeHints.y -= SizeHints.height;
964 		if (x_negative)
965 			SizeHints.win_gravity = SouthEastGravity;
966 		else
967 			SizeHints.win_gravity = SouthWestGravity;
968 	}
969 	SizeHints.flags |= PWinGravity;
970 
971         mainWindow = XCreateSimpleWindow(
972                 mainDisplay,            /* display */
973                 Root,                   /* parent */
974                 SizeHints.x,            /* x */
975                 SizeHints.y,            /* y */
976                 SizeHints.width,        /* width */
977                 SizeHints.height,       /* height */
978                 0,                      /* border_width */
979                 fore_pix,               /* border */
980                 back_pix                /* background */
981                 );
982 
983         iconWindow = XCreateSimpleWindow(
984                 mainDisplay,            /* display */
985                 Root,                   /* parent */
986                 SizeHints.x,            /* x */
987                 SizeHints.y,            /* y */
988                 SizeHints.width,        /* width */
989                 SizeHints.height,       /* height */
990                 0,                      /* border_width */
991                 fore_pix,               /* border */
992                 back_pix                /* background */
993                 );
994 
995         XSetWMNormalHints(mainDisplay, mainWindow, &SizeHints);
996         status = XClearWindow(mainDisplay, mainWindow);
997 
998         status = XGetWindowAttributes(
999                 mainDisplay,    /* display */
1000                 mainWindow,     /* window */
1001                 & winAttr       /* window_attributes_return */
1002                 );
1003         status = XSetWindowBackgroundPixmap(
1004                 mainDisplay,            /* display */
1005                 mainWindow,             /* window */
1006                 backgroundXpm.pixmap    /* background_pixmap */
1007                 );
1008         status = XSetWindowBackgroundPixmap(
1009                 mainDisplay,            /* display */
1010                 iconWindow,             /* window */
1011                 backgroundXpm.pixmap    /* background_pixmap */
1012                 );
1013 
1014         status = XStringListToTextProperty(&window_name, 1, &title);
1015         XSetWMName(mainDisplay, mainWindow, &title);
1016         XSetWMName(mainDisplay, iconWindow, &title);
1017 
1018         classHint.res_name = "ascpu" ;
1019         classHint.res_class = "ASCPU";
1020         XSetClassHint(mainDisplay, mainWindow, &classHint);
1021         XStoreName(mainDisplay, mainWindow, window_name);
1022         XSetIconName(mainDisplay, mainWindow, window_name);
1023 
1024         status = XSelectInput(
1025                 mainDisplay,    /* display */
1026                 mainWindow,     /* window */
1027                 ExposureMask    /* event_mask */
1028 #ifdef FOR_CLICK
1029 		| ButtonPressMask
1030 #endif
1031                 );
1032 
1033         status = XSelectInput(
1034                 mainDisplay,    /* display */
1035                 iconWindow,     /* window */
1036                 ExposureMask    /* event_mask */
1037 #ifdef FOR_CLICK
1038 		| ButtonPressMask
1039 #endif
1040                 );
1041 
1042         /* Creating Graphics context */
1043         mainGCV.foreground = fore_pix;
1044         mainGCV.background = back_pix;
1045         mainGCV.graphics_exposures = False;
1046         mainGCV.line_style = LineSolid;
1047         mainGCV.fill_style = FillSolid;
1048         mainGCV.line_width = 1;
1049         mainGC = XCreateGC(
1050                 mainDisplay,
1051                 mainWindow,
1052                 GCForeground|GCBackground|GCLineWidth|
1053                         GCLineStyle|GCFillStyle,
1054                 &mainGCV
1055                 );
1056 
1057         status = XSetCommand(mainDisplay, mainWindow, argv, argc);
1058 
1059         /* Set up the event for quitting the window */
1060         wm_delete_window = XInternAtom(
1061                 mainDisplay,
1062                 "WM_DELETE_WINDOW",     /* atom_name */
1063                 False                   /* only_if_exists */
1064                 );
1065         wm_protocols = XInternAtom(
1066                 mainDisplay,
1067                 "WM_PROTOCOLS",         /* atom_name */
1068                 False                   /* only_if_exists */
1069                 );
1070         status = XSetWMProtocols(
1071                 mainDisplay,
1072                 mainWindow,
1073                 &wm_delete_window,
1074                 1
1075                 );
1076         status = XSetWMProtocols(
1077                 mainDisplay,
1078                 iconWindow,
1079                 &wm_delete_window,
1080                 1
1081                 );
1082 
1083         WmHints.flags = StateHint | IconWindowHint;
1084         WmHints.initial_state =
1085                 withdrawn ? WithdrawnState :
1086                         iconic ? IconicState : NormalState;
1087         WmHints.icon_window = iconWindow;
1088         if ( withdrawn ) {
1089                 WmHints.window_group = mainWindow;
1090                 WmHints.flags |= WindowGroupHint;
1091         }
1092         if ( iconic || withdrawn ) {
1093                 WmHints.icon_x = SizeHints.x;
1094                 WmHints.icon_y = SizeHints.y;
1095                 WmHints.flags |= IconPositionHint;
1096         }
1097         XSetWMHints(
1098                 mainDisplay,
1099                 mainWindow,
1100                 &WmHints);
1101 
1102         /* Finally show the window */
1103         status = XMapWindow(
1104                 mainDisplay,
1105                 mainWindow
1106                 );
1107 
1108 	/* Get colors while waiting for Expose */
1109 	if (idle_color_defined)
1110 		pix[0] = GetColor(idle_color, mainDisplay, Root);
1111 	else
1112 		pix[0] = GetColor(state.bgcolor, mainDisplay, Root);
1113 	if (sys_color_defined)
1114 		pix[1] = GetColor(sys_color, mainDisplay, Root);
1115 	else
1116 		pix[1] = GetColor(state.fgcolor, mainDisplay, Root);
1117 	if (nice_color_defined)
1118 		pix[2] = GetColor(nice_color, mainDisplay, Root);
1119 	else
1120 		pix[2] = DarkenColor(state.fgcolor, 1.2, mainDisplay, Root);
1121 	if (user_color_defined)
1122 		pix[3] = GetColor(user_color, mainDisplay, Root);
1123 	else
1124 		pix[3] = DarkenColor(state.fgcolor, 1.4, mainDisplay, Root);
1125 
1126 	/*
1127 	 * We have to "reset" the values of load by reading it in advance
1128 	 */
1129 	average_history = malloc(sizeof(struct cpu_load_struct) *
1130 			state.avg_samples);
1131 	if (! average_history ) {
1132 		printf("ascpu: failed to malloc average list (%ld bytes)\n",
1133 			sizeof(struct cpu_load_struct) * state.avg_samples);
1134 		ascpu_cleanup();
1135 	}
1136 #ifdef	__FreeBSD__
1137 	size_t len = 2;
1138 	sysctlnametomib("kern.cp_times", cp_times_mib, &len);
1139 	len = 2;
1140 	sysctlnametomib("kern.cp_time", cp_time_mib, &len);
1141 	if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "kvm_open")) != NULL) {
1142 		kvm_nlist(kd, nlst);
1143 	}
1144 #endif
1145 
1146 #ifdef _AIX32   /* AIX > 3.1 */
1147         nlist ("/unix", namelist);
1148         if (namelist[0].n_value == 0) {
1149                 printf("ascpu : cannot get nlist\n");
1150                 exit(1);
1151         }
1152         sysinfo_offset = namelist[0].n_value;
1153         kmem = open (KMEM_FILE, O_RDONLY);
1154         if (kmem < 0) {
1155                 printf("ascpu : cannot open %s\n", KMEM_FILE);
1156                 exit(1);
1157         }
1158 #endif /* _AIX32 */
1159 
1160 	/*running_counter = state.hist_samples;*/
1161 	prepare_format();
1162 	read_stat();
1163 
1164         /* wait for the Expose event now */
1165         XNextEvent(mainDisplay, &Event);
1166         /* We 've got Expose -> draw the parts of the window. */
1167         ascpu_redraw();
1168         XFlush(mainDisplay);
1169 }
1170 
1171