1 /* Copyright (c) 2003 Thomas Runge (coto@core.de)
2 * Mach and Darwin specific code is Copyright (c) 2006 Eric Pooch (epooch@tenon.com)
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the author nor the names of its contributors
16 * may be used to endorse or promote products derived from this
17 * software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
35 #include <sys/types.h>
36 #include <sys/param.h>
42 #include <sys/sysctl.h>
43 #include <sys/dkstat.h>
44 #include <sys/ucred.h>
45 #include <sys/mount.h>
50 #include <sys/socket.h>
52 #include <net/if_mib.h>
53 #include <mach/mach.h>
58 #include "shared/LL.h"
60 #include <CoreFoundation/CoreFoundation.h>
61 #include <IOKit/ps/IOPowerSources.h>
62 #include <IOKit/ps/IOPSKeys.h>
65 #define pagetok(size) ((size) << pageshift)
66 static int swapmode(int *rettotal
, int *retfree
);
68 static mach_port_t lcdproc_port
;
71 int machine_init(void)
73 /* get the page size with "getpagesize" and calculate pageshift from it */
74 unsigned int pagesize
= 0;
77 lcdproc_port
= mach_host_self();
78 host_page_size(lcdproc_port
, &pagesize
);
86 /* we only need the amount of log(2)1024 for our conversion */
92 int machine_close(void)
97 int machine_get_battstat(int *acstat
, int *battflag
, int *percent
)
99 CFTypeRef blob
= IOPSCopyPowerSourcesInfo();
100 CFArrayRef sources
= IOPSCopyPowerSourcesList(blob
);
103 CFDictionaryRef pSource
= NULL
;
106 *acstat
= LCDP_AC_ON
;
107 *battflag
= LCDP_BATT_ABSENT
;
110 if (CFArrayGetCount(sources
) == 0) return(FALSE
);
112 for (i
= 0; i
< CFArrayGetCount(sources
); i
++)
114 pSource
= IOPSGetPowerSourceDescription(blob
, CFArrayGetValueAtIndex(sources
, i
));
117 psValue
= (CFStringRef
)CFDictionaryGetValue(pSource
, CFSTR(kIOPSNameKey
));
119 if (CFDictionaryGetValueIfPresent(pSource
, CFSTR(kIOPSIsPresentKey
), &psValue
) && (CFBooleanGetValue(psValue
) > 0))
121 psValue
= (CFStringRef
)CFDictionaryGetValue(pSource
, CFSTR(kIOPSPowerSourceStateKey
));
123 if (CFStringCompare(psValue
,CFSTR(kIOPSBatteryPowerValue
),0)==kCFCompareEqualTo
)
125 /* We are running on a battery power source. */
126 *battflag
= LCDP_BATT_UNKNOWN
;
127 *acstat
= LCDP_AC_OFF
;
129 else if (CFDictionaryGetValueIfPresent(pSource
, CFSTR(kIOPSIsChargingKey
), &psValue
))
131 /* We are running on an AC power source,
132 but we also have a battery power source present. */
134 if (CFBooleanGetValue(psValue
) > 0)
135 *battflag
= LCDP_BATT_CHARGING
;
137 *battflag
= LCDP_BATT_UNKNOWN
;
140 if (*battflag
!= LCDP_BATT_ABSENT
)
145 psValue
= CFDictionaryGetValue(pSource
, CFSTR(kIOPSCurrentCapacityKey
));
146 CFNumberGetValue(psValue
, kCFNumberSInt32Type
, &curCapacity
);
148 psValue
= CFDictionaryGetValue(pSource
, CFSTR(kIOPSMaxCapacityKey
));
149 CFNumberGetValue(psValue
, kCFNumberSInt32Type
, &maxCapacity
);
151 *percent
= (int)((double)curCapacity
/(double)maxCapacity
* 100);
153 /* There is a way to check this through the IOKit,
154 but I am not sure what gets returned for kIOPSLowWarnLevelKey and kIOPSDeadWarnLevelKey,
157 if (*battflag
== LCDP_BATT_UNKNOWN
) {
159 *battflag
= LCDP_BATT_HIGH
;
160 else if (*percent
> 2)
161 *battflag
= LCDP_BATT_LOW
;
163 *battflag
= LCDP_BATT_CRITICAL
;
165 /* printf ("powerSource %d of %d: percent: %d/%d %d\n", i, CFArrayGetCount(sources), curCapacity, maxCapacity, *percent);*/
177 int machine_get_fs(mounts_type fs
[], int *cnt
)
179 struct statfs
*mntbuf
;
181 int statcnt
, fscnt
, i
;
183 fscnt
= getmntinfo(&mntbuf
, MNT_WAIT
);
186 perror("getmntinfo");
189 for (statcnt
= 0, pp
= mntbuf
, i
= 0; i
< fscnt
; pp
++, i
++)
191 if ( strcmp(pp
->f_fstypename
, "procfs")
192 && strcmp(pp
->f_fstypename
, "kernfs")
193 && strcmp(pp
->f_fstypename
, "linprocfs")
195 && strcmp(pp
->f_fstypename
, "nfs")
198 && strcmp(pp
->f_fstypename
, "smbfs")
202 snprintf(fs
[statcnt
].dev
, 255, "%s", pp
->f_mntfromname
);
203 snprintf(fs
[statcnt
].mpoint
, 255, "%s", pp
->f_mntonname
);
204 snprintf(fs
[statcnt
].type
, 63, "%s", pp
->f_fstypename
);
206 fs
[statcnt
].blocks
= pp
->f_blocks
;
207 if (fs
[statcnt
].blocks
> 0)
209 fs
[statcnt
].bsize
= pp
->f_bsize
;
210 fs
[statcnt
].bfree
= pp
->f_bfree
;
211 fs
[statcnt
].files
= pp
->f_files
;
212 fs
[statcnt
].ffree
= pp
->f_ffree
;
223 int machine_get_load(load_type
*curr_load
)
225 static load_type last_load
= { 0, 0, 0, 0, 0 };
226 static load_type last_ret_load
;
229 host_cpu_load_info_data_t load_info
;
230 mach_msg_type_number_t info_count
;
232 info_count
= HOST_CPU_LOAD_INFO_COUNT
;
233 if (host_statistics(lcdproc_port
, HOST_CPU_LOAD_INFO
, (host_info_t
)&load_info
, &info_count
))
235 perror("host_statistics");
239 load
.user
= (unsigned long) (load_info
.cpu_ticks
[CPU_STATE_USER
]);
240 load
.nice
= (unsigned long) (load_info
.cpu_ticks
[CPU_STATE_NICE
]);
241 load
.system
= (unsigned long) (load_info
.cpu_ticks
[CPU_STATE_SYSTEM
]);
242 load
.idle
= (unsigned long) (load_info
.cpu_ticks
[CPU_STATE_IDLE
]);
243 load
.total
= load
.user
+ load
.nice
+ load
.system
+ load
.idle
;
245 if (load
.total
!= last_load
.total
)
247 curr_load
->user
= load
.user
- last_load
.user
;
248 curr_load
->nice
= load
.nice
- last_load
.nice
;
249 curr_load
->system
= load
.system
- last_load
.system
;
250 curr_load
->idle
= load
.idle
- last_load
.idle
;
251 curr_load
->total
= load
.total
- last_load
.total
;
252 last_ret_load
= *curr_load
;
257 *curr_load
= last_ret_load
;
264 int machine_get_loadavg(double *load
)
266 double loadavg
[LOADAVG_NSTATS
];
268 if (getloadavg(loadavg
, LOADAVG_NSTATS
) <= LOADAVG_1MIN
)
271 *load
= loadavg
[LOADAVG_1MIN
];
276 int machine_get_meminfo(meminfo_type
*result
)
278 int total_pages
, free_pages
;
280 vm_statistics_data_t vm_info
;
281 mach_msg_type_number_t info_count
;
283 info_count
= HOST_VM_INFO_COUNT
;
284 if (host_statistics(lcdproc_port
, HOST_VM_INFO
, (host_info_t
)&vm_info
, &info_count
))
286 perror("host_statistics");
290 result
[0].total
= pagetok(vm_info
.active_count
+ vm_info
.inactive_count
+
291 vm_info
.free_count
+ vm_info
.wire_count
);
292 result
[0].free
= pagetok(vm_info
.free_count
);
293 result
[0].buffers
= 0;
295 result
[0].shared
= 0;
301 if (swapmode(&total_pages
, &free_pages
) != -1)
303 result
[1].total
= total_pages
;
304 result
[1].free
= free_pages
;
310 int machine_get_procs(LinkedList
*procs
)
312 struct kinfo_proc
*kprocs
;
316 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_ALL
, 0 };
320 /* Call sysctl with a NULL buffer as a dry run. */
321 if ( sysctl(mib
, 4, NULL
, &size
, NULL
, 0 ) < 0)
323 perror("Failure calling sysctl");
326 /* Allocate a buffer based on previous results of sysctl. */
327 kprocs
= (struct kinfo_proc
*)alloca(size
);
330 perror("mem_alloca");
333 /* Call sysctl again with the new buffer. */
334 if ( sysctl(mib
, 4, kprocs
, &size
, NULL
, 0 ) < 0)
336 perror("Failure calling sysctl");
340 nproc
= size
/ sizeof(struct kinfo_proc
);
342 for (i
= 0; i
< nproc
; i
++, kprocs
++)
345 unsigned int status
= kprocs
->kp_proc
.p_stat
;
347 if (status
== SIDL
||status
== SZOMB
)
350 p
= malloc(sizeof(procinfo_type
));
353 perror("mem_malloc");
356 strncpy(p
->name
, kprocs
->kp_proc
.p_comm
, 15);
359 p
->number
= kprocs
->kp_proc
.p_pid
;
361 LL_Push(procs
, (void *)p
);
363 /* Normal user can't get tasks for processes owned by root. */
364 if (kprocs
->kp_eproc
.e_pcred
.p_ruid
== 0)
367 /* Get the memory data for each pid from Mach. */
368 if (task_for_pid(mach_task_self(), kprocs
->kp_proc
.p_pid
, &task
) == KERN_SUCCESS
)
370 task_basic_info_data_t info
;
371 mach_msg_type_number_t count
= TASK_BASIC_INFO_COUNT
;
373 if (task_info(task
, TASK_BASIC_INFO
, (task_info_t
)&info
, &count
) == KERN_SUCCESS
) {
374 p
->totl
= (unsigned long)(/*info.virtual_size*/ info
.resident_size
/ 1024);
377 /* This error pops up very often because of Mac OS X security fixes.
378 It might pop up all of the time on an intel Mac.
379 Basically, we cannot get many tasks unless we are root.
380 perror("task_for_pid");
381 printf("process: %s, owner:%d\n", kprocs->kp_proc.p_comm, kprocs->kp_eproc.e_pcred.p_ruid); */
391 int machine_get_smpload(load_type
*result
, int *numcpus
)
398 if (sysctlbyname("hw.ncpu", &num
, &size
, NULL
, 0) < 0) {
399 perror("sysctl hw.ncpu");
403 if (machine_get_load(&curr_load
) == FALSE
)
409 /* restrict #CPUs to max. *numcpus */
410 num
= (*numcpus
>= num
) ? num
: *numcpus
;
413 /* Don't know how to get per-cpu-load values */
414 for (i
= 0; i
< num
; i
++) {
415 result
[i
] = curr_load
;
421 int machine_get_uptime(double *up
, double *idle
)
425 struct timeval boottime
;
430 mib
[1] = KERN_BOOTTIME
;
431 size
= sizeof(boottime
);
434 if (sysctl(mib
, 2, &boottime
, &size
, NULL
, 0) != -1 &&
435 boottime
.tv_sec
!= 0)
437 *up
= (double)(now
- boottime
.tv_sec
);
439 if (machine_get_load(&curr_load
) == FALSE
)
442 *idle
= 100.*curr_load
.idle
/curr_load
.total
;
447 static int swapmode(int *rettotal
, int *retfree
)
451 struct xsw_usage xsu
;
455 mib
[1] = VM_SWAPUSAGE
;
461 if (sysctl(mib
, 2, &xsu
, &size
, NULL
, 0) != 0)
467 *rettotal
= (xsu
.xsu_total
/1024);
468 *retfree
= ((xsu
.xsu_total
-xsu
.xsu_used
)/1024);
479 /* Get network statistics */
480 int machine_get_iface_stats (IfaceInfo
*interface
)
482 static int first_time
= 1; /* is it first time we call this function? */
484 int name
[6] = {CTL_NET
, PF_LINK
, NETLINK_GENERIC
, IFMIB_SYSTEM
, IFMIB_IFCOUNT
};
486 struct ifmibdata ifmd
; /* ifmibdata contains the network statistics */
489 /* get number of interfaces */
490 //if (sysctlbyname("net.link.generic.system.ifcount", &rows, &len, NULL, 0) == 0) {
491 if (sysctl(name
, 5, &rows
, &len
, 0, 0) == 0) {
492 interface
->status
= down
; /* set status down by default */
494 name
[3] = IFMIB_IFDATA
;
496 name
[5] = IFDATA_GENERAL
;
499 /* walk through all interfaces in the ifmib table from last to first */
500 for ( ; rows
> 0; rows
--) {
501 name
[4] = rows
; /* set the interface index */
502 /* retrive the ifmibdata for the current index */
503 if (sysctl(name
, 6, &ifmd
, &len
, NULL
, 0) == -1) {
504 perror("read sysctl");
507 /* check if its interface name matches */
508 if (strcmp(ifmd
.ifmd_name
, interface
->name
) == 0) {
509 interface
->last_online
= time(NULL
); /* save actual time */
511 if ((ifmd
.ifmd_flags
& IFF_UP
) == IFF_UP
)
512 interface
->status
= up
; /* is up */
514 interface
->rc_byte
= ifmd
.ifmd_data
.ifi_ibytes
;
515 interface
->tr_byte
= ifmd
.ifmd_data
.ifi_obytes
;
516 interface
->rc_pkt
= ifmd
.ifmd_data
.ifi_ipackets
;
517 interface
->tr_pkt
= ifmd
.ifmd_data
.ifi_opackets
;
520 interface
->rc_byte_old
= interface
->rc_byte
;
521 interface
->tr_byte_old
= interface
->tr_byte
;
522 interface
->rc_pkt_old
= interface
->rc_pkt
;
523 interface
->tr_pkt_old
= interface
->tr_pkt
;
524 first_time
= 0; /* now it isn't first time */
529 /* if we are here there is no interface with the given name */
532 perror("read sysctl IFMIB_IFCOUNT");
535 } /* get_iface_stats() */
538 #endif /* __APPLE__ */