1#
2# pystatgrab
3# https://libstatgrab.org/pystatgrab/
4# Copyright (C) 2004-2019 Tim Bishop
5# Copyright (C) 2005-2013 Adam Sampson
6#
7# This library is free software; you can redistribute it and/or
8# modify it under the terms of the GNU Lesser General Public
9# License as published by the Free Software Foundation; either
10# version 2.1 of the License, or (at your option) any later version.
11#
12# This library is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15# Lesser General Public License for more details.
16#
17# You should have received a copy of the GNU Lesser General Public
18# License along with this library; if not, write to the Free Software
19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20# 02110-1301, USA.
21#
22# $Id$
23#
24
25from libc.stdlib cimport malloc, free
26from sys import version_info
27
28cimport libstatgrab as sg
29
30# Helper code
31
32class StatgrabError(Exception):
33    """Exception representing a libstatgrab error."""
34    def __init__(self):
35        cdef sg.sg_error_details details
36        sg.sg_get_error_details(&details)
37        self.error = details.error
38        self.errno_value = details.errno_value
39        self.error_arg = details.error_arg
40
41        cdef char *msg = NULL
42        sg.sg_strperror(&msg, &details)
43        super(StatgrabError, self).__init__("statgrab error: " + msg)
44        free(msg)
45
46cdef int _not_error(sg.sg_error code) except -1:
47    """Raise StatgrabError if code is not SG_ERROR_NONE."""
48    if code != sg.SG_ERROR_NONE:
49        raise StatgrabError()
50    return 0
51
52cdef int _not_null(const void *value) except -1:
53    """Raise StatgrabError if value is NULL."""
54    if value == NULL:
55        raise StatgrabError()
56    return 0
57
58cdef int _vector_not_null(const void *value, size_t entries) except -1:
59    """Check the return value from a vector-returning function.
60    Raise StatgrabError if:
61    - entries > 0 and value is NULL, or
62    - entries == 0 and sg_get_error indicates an error has occurred."""
63    # FIXME: this is an API oddity; libstatgrab issue #17 discusses it
64    if entries > 0 and value == NULL:
65        raise StatgrabError()
66    elif entries == 0 and sg.sg_get_error() != sg.SG_ERROR_NONE:
67        raise StatgrabError()
68    return 0
69
70class Result(dict):
71    def __init__(self, attrs):
72        super(Result, self).__init__(attrs)
73
74        # For compatibility with older pystatgrab.
75        self.attrs = attrs
76
77    def __getattr__(self, item):
78        try:
79            return self.__getitem__(item)
80        except KeyError:
81            raise AttributeError(item)
82
83def _str_to_bytes(s):
84    if version_info[0] >= 3 and isinstance(s, str):
85        return bytes(s, 'utf-8')
86    else:
87        return s
88
89def _bytes_to_str(b):
90    if version_info[0] >= 3 and isinstance(b, bytes):
91        return str(b, 'utf-8')
92    else:
93        return b
94
95# libstatgrab constants exported
96
97ERROR_NONE = sg.SG_ERROR_NONE
98ERROR_INVALID_ARGUMENT = sg.SG_ERROR_INVALID_ARGUMENT
99ERROR_ASPRINTF = sg.SG_ERROR_ASPRINTF
100ERROR_SPRINTF = sg.SG_ERROR_SPRINTF
101ERROR_DEVICES = sg.SG_ERROR_DEVICES
102ERROR_DEVSTAT_GETDEVS = sg.SG_ERROR_DEVSTAT_GETDEVS
103ERROR_DEVSTAT_SELECTDEVS = sg.SG_ERROR_DEVSTAT_SELECTDEVS
104ERROR_DISKINFO = sg.SG_ERROR_DISKINFO
105ERROR_ENOENT = sg.SG_ERROR_ENOENT
106ERROR_GETIFADDRS = sg.SG_ERROR_GETIFADDRS
107ERROR_GETMNTINFO = sg.SG_ERROR_GETMNTINFO
108ERROR_GETPAGESIZE = sg.SG_ERROR_GETPAGESIZE
109ERROR_HOST = sg.SG_ERROR_HOST
110ERROR_KSTAT_DATA_LOOKUP = sg.SG_ERROR_KSTAT_DATA_LOOKUP
111ERROR_KSTAT_LOOKUP = sg.SG_ERROR_KSTAT_LOOKUP
112ERROR_KSTAT_OPEN = sg.SG_ERROR_KSTAT_OPEN
113ERROR_KSTAT_READ = sg.SG_ERROR_KSTAT_READ
114ERROR_KVM_GETSWAPINFO = sg.SG_ERROR_KVM_GETSWAPINFO
115ERROR_KVM_OPENFILES = sg.SG_ERROR_KVM_OPENFILES
116ERROR_MALLOC = sg.SG_ERROR_MALLOC
117ERROR_MEMSTATUS = sg.SG_ERROR_MEMSTATUS
118ERROR_OPEN = sg.SG_ERROR_OPEN
119ERROR_OPENDIR = sg.SG_ERROR_OPENDIR
120ERROR_READDIR = sg.SG_ERROR_READDIR
121ERROR_PARSE = sg.SG_ERROR_PARSE
122ERROR_PDHADD = sg.SG_ERROR_PDHADD
123ERROR_PDHCOLLECT = sg.SG_ERROR_PDHCOLLECT
124ERROR_PDHOPEN = sg.SG_ERROR_PDHOPEN
125ERROR_PDHREAD = sg.SG_ERROR_PDHREAD
126ERROR_PERMISSION = sg.SG_ERROR_PERMISSION
127ERROR_PSTAT = sg.SG_ERROR_PSTAT
128ERROR_SETEGID = sg.SG_ERROR_SETEGID
129ERROR_SETEUID = sg.SG_ERROR_SETEUID
130ERROR_SETMNTENT = sg.SG_ERROR_SETMNTENT
131ERROR_SOCKET = sg.SG_ERROR_SOCKET
132ERROR_SWAPCTL = sg.SG_ERROR_SWAPCTL
133ERROR_SYSCONF = sg.SG_ERROR_SYSCONF
134ERROR_SYSCTL = sg.SG_ERROR_SYSCTL
135ERROR_SYSCTLBYNAME = sg.SG_ERROR_SYSCTLBYNAME
136ERROR_SYSCTLNAMETOMIB = sg.SG_ERROR_SYSCTLNAMETOMIB
137ERROR_SYSINFO = sg.SG_ERROR_SYSINFO
138ERROR_MACHCALL = sg.SG_ERROR_MACHCALL
139ERROR_IOKIT = sg.SG_ERROR_IOKIT
140ERROR_UNAME = sg.SG_ERROR_UNAME
141ERROR_UNSUPPORTED = sg.SG_ERROR_UNSUPPORTED
142ERROR_XSW_VER_MISMATCH = sg.SG_ERROR_XSW_VER_MISMATCH
143ERROR_GETMSG = sg.SG_ERROR_GETMSG
144ERROR_PUTMSG = sg.SG_ERROR_PUTMSG
145ERROR_INITIALISATION = sg.SG_ERROR_INITIALISATION
146ERROR_MUTEX_LOCK = sg.SG_ERROR_MUTEX_LOCK
147ERROR_MUTEX_UNLOCK = sg.SG_ERROR_MUTEX_UNLOCK
148
149entire_cpu_percent = sg.sg_entire_cpu_percent
150last_diff_cpu_percent = sg.sg_last_diff_cpu_percent
151new_diff_cpu_percent = sg.sg_new_diff_cpu_percent
152
153IFACE_DUPLEX_FULL = sg.SG_IFACE_DUPLEX_FULL
154IFACE_DUPLEX_HALF = sg.SG_IFACE_DUPLEX_HALF
155IFACE_DUPLEX_UNKNOWN = sg.SG_IFACE_DUPLEX_UNKNOWN
156
157IFACE_DOWN = sg.SG_IFACE_DOWN
158IFACE_UP = sg.SG_IFACE_UP
159
160PROCESS_STATE_RUNNING = sg.SG_PROCESS_STATE_RUNNING
161PROCESS_STATE_SLEEPING = sg.SG_PROCESS_STATE_SLEEPING
162PROCESS_STATE_STOPPED = sg.SG_PROCESS_STATE_STOPPED
163PROCESS_STATE_ZOMBIE = sg.SG_PROCESS_STATE_ZOMBIE
164PROCESS_STATE_UNKNOWN = sg.SG_PROCESS_STATE_UNKNOWN
165
166entire_process_count = sg.sg_entire_process_count
167last_process_count = sg.sg_last_process_count
168
169# libstatgrab functions exported
170
171# FIXME: docstrings
172
173def init(ignore_init_errors=False):
174    _not_error(sg.sg_init(ignore_init_errors))
175
176def snapshot():
177    _not_error(sg.sg_snapshot())
178
179def shutdown():
180    _not_error(sg.sg_shutdown())
181
182def drop_privileges():
183    _not_error(sg.sg_drop_privileges())
184
185def get_host_info():
186    cdef const sg.sg_host_info *s = sg.sg_get_host_info(NULL)
187    _not_null(s)
188    return Result({
189        'os_name': s.os_name,
190        'os_release': s.os_release,
191        'os_version': s.os_version,
192        'platform': s.platform,
193        'hostname': s.hostname,
194        'bitwidth': s.bitwidth,
195        'host_state': s.host_state,
196        'ncpus': s.ncpus,
197        'maxcpus': s.maxcpus,
198        'uptime': s.uptime,
199        'systime': s.systime,
200        })
201
202cdef _make_cpu_stats(const sg.sg_cpu_stats *s):
203    return Result({
204        'user': s.user,
205        'kernel': s.kernel,
206        'idle': s.idle,
207        'iowait': s.iowait,
208        'swap': s.swap,
209        'nice': s.nice,
210        'total': s.total,
211        'context_switches': s.context_switches,
212        'voluntary_context_switches': s.voluntary_context_switches,
213        'involuntary_context_switches': s.involuntary_context_switches,
214        'syscalls': s.syscalls,
215        'interrupts': s.interrupts,
216        'soft_interrupts': s.soft_interrupts,
217        'systime': s.systime,
218        })
219
220def get_cpu_stats():
221    cdef const sg.sg_cpu_stats *s = sg.sg_get_cpu_stats(NULL)
222    _not_null(s)
223    return _make_cpu_stats(s)
224
225def get_cpu_stats_diff():
226    cdef const sg.sg_cpu_stats *s = sg.sg_get_cpu_stats_diff(NULL)
227    _not_null(s)
228    return _make_cpu_stats(s)
229
230def get_cpu_percents(cps=new_diff_cpu_percent):
231    cdef const sg.sg_cpu_percents *s = sg.sg_get_cpu_percents(NULL)
232    _not_null(s)
233    return Result({
234        'user': s.user,
235        'kernel': s.kernel,
236        'idle': s.idle,
237        'iowait': s.iowait,
238        'swap': s.swap,
239        'nice': s.nice,
240        'time_taken': s.time_taken,
241        })
242
243def get_mem_stats():
244    cdef const sg.sg_mem_stats *s = sg.sg_get_mem_stats(NULL)
245    _not_null(s)
246    return Result({
247        'total': s.total,
248        'free': s.free,
249        'used': s.used,
250        'cache': s.cache,
251        'systime': s.systime,
252        })
253
254def get_load_stats():
255    cdef const sg.sg_load_stats *s = sg.sg_get_load_stats(NULL)
256    _not_null(s)
257    return Result({
258        'min1': s.min1,
259        'min5': s.min5,
260        'min15': s.min15,
261        'systime': s.systime,
262        })
263
264cdef _make_user_stats(const sg.sg_user_stats *s):
265    return Result({
266        'login_name': s.login_name,
267        # Note special handling for record_id.
268        'record_id': s.record_id[:s.record_id_size],
269        'device': s.device,
270        'hostname': s.hostname,
271        'pid': s.pid,
272        'login_time': s.login_time,
273        'systime': s.systime,
274        })
275
276def get_user_stats():
277    cdef size_t n
278    cdef const sg.sg_user_stats *s = sg.sg_get_user_stats(&n)
279    _vector_not_null(s, n)
280    return [_make_user_stats(&s[i]) for i in range(n)]
281
282def get_swap_stats():
283    cdef const sg.sg_swap_stats *s = sg.sg_get_swap_stats(NULL)
284    _not_null(s)
285    return Result({
286        'total': s.total,
287        'used': s.used,
288        'free': s.free,
289        'systime': s.systime,
290        })
291
292def get_valid_filesystems():
293    cdef size_t n
294    cdef const char **s = sg.sg_get_valid_filesystems(&n)
295    _vector_not_null(s, n)
296    return [_bytes_to_str(s[i]) for i in range(n)]
297
298def set_valid_filesystems(valid_fs):
299    cdef int num_fs = len(valid_fs)
300    cdef const char **vs
301    vs = <const char **>malloc((num_fs + 1) * sizeof(const char *))
302    if vs == NULL:
303        raise MemoryError("malloc failed")
304    valid_fs = [_str_to_bytes(x) for x in valid_fs]
305    for i in range(num_fs):
306        vs[i] = valid_fs[i]
307    vs[num_fs] = NULL
308    _not_error(sg.sg_set_valid_filesystems(vs))
309    free(vs)
310
311cdef _make_fs_stats(const sg.sg_fs_stats *s):
312    return Result({
313        'device_name': s.device_name,
314        'device_canonical': s.device_canonical,
315        'fs_type': s.fs_type,
316        'mnt_point': s.mnt_point,
317        'device_type': s.device_type,
318        'size': s.size,
319        'used': s.used,
320        'free': s.free,
321        'avail': s.avail,
322        'total_inodes': s.total_inodes,
323        'used_inodes': s.used_inodes,
324        'free_inodes': s.free_inodes,
325        'avail_inodes': s.avail_inodes,
326        'io_size': s.io_size,
327        'block_size': s.block_size,
328        'total_blocks': s.total_blocks,
329        'free_blocks': s.free_blocks,
330        'used_blocks': s.used_blocks,
331        'avail_blocks': s.avail_blocks,
332        'systime': s.systime,
333        })
334
335def get_fs_stats():
336    cdef size_t n
337    cdef const sg.sg_fs_stats *s = sg.sg_get_fs_stats(&n)
338    _vector_not_null(s, n)
339    return [_make_fs_stats(&s[i]) for i in range(n)]
340
341def get_fs_stats_diff():
342    cdef size_t n
343    cdef const sg.sg_fs_stats *s = sg.sg_get_fs_stats_diff(&n)
344    _vector_not_null(s, n)
345    return [_make_fs_stats(&s[i]) for i in range(n)]
346
347cdef _make_disk_io_stats(const sg.sg_disk_io_stats *s):
348    return Result({
349        'disk_name': s.disk_name,
350        'read_bytes': s.read_bytes,
351        'write_bytes': s.write_bytes,
352        'systime': s.systime,
353    })
354
355def get_disk_io_stats():
356    cdef size_t n
357    cdef const sg.sg_disk_io_stats *s = sg.sg_get_disk_io_stats(&n)
358    _vector_not_null(s, n)
359    return [_make_disk_io_stats(&s[i]) for i in range(n)]
360
361def get_disk_io_stats_diff():
362    cdef size_t n
363    cdef const sg.sg_disk_io_stats *s = sg.sg_get_disk_io_stats_diff(&n)
364    _vector_not_null(s, n)
365    return [_make_disk_io_stats(&s[i]) for i in range(n)]
366
367cdef _make_network_io_stats(const sg.sg_network_io_stats *s):
368    return Result({
369        'interface_name': s.interface_name,
370        'tx': s.tx,
371        'rx': s.rx,
372        'ipackets': s.ipackets,
373        'opackets': s.opackets,
374        'ierrors': s.ierrors,
375        'oerrors': s.oerrors,
376        'collisions': s.collisions,
377        'systime': s.systime,
378        })
379
380def get_network_io_stats():
381    cdef size_t n
382    cdef const sg.sg_network_io_stats *s = sg.sg_get_network_io_stats(&n)
383    _vector_not_null(s, n)
384    return [_make_network_io_stats(&s[i]) for i in range(n)]
385
386def get_network_io_stats_diff():
387    cdef size_t n
388    cdef const sg.sg_network_io_stats *s = sg.sg_get_network_io_stats_diff(&n)
389    _vector_not_null(s, n)
390    return [_make_network_io_stats(&s[i]) for i in range(n)]
391
392cdef _make_network_iface_stats(const sg.sg_network_iface_stats *s):
393    return Result({
394        'interface_name': s.interface_name,
395        'speed': s.speed,
396        'factor': s.factor,
397        'duplex': s.duplex,
398        'up': s.up,
399        'systime': s.systime,
400        })
401
402def get_network_iface_stats():
403    cdef size_t n
404    cdef const sg.sg_network_iface_stats *s = sg.sg_get_network_iface_stats(&n)
405    _vector_not_null(s, n)
406    return [_make_network_iface_stats(&s[i]) for i in range(n)]
407
408cdef _make_page_stats(const sg.sg_page_stats *s):
409    return Result({
410        'pages_pagein': s.pages_pagein,
411        'pages_pageout': s.pages_pageout,
412        'systime': s.systime,
413        })
414
415def get_page_stats():
416    cdef const sg.sg_page_stats *s = sg.sg_get_page_stats(NULL)
417    _not_null(s)
418    return _make_page_stats(s)
419
420def get_page_stats_diff():
421    cdef const sg.sg_page_stats *s = sg.sg_get_page_stats_diff(NULL)
422    _not_null(s)
423    return _make_page_stats(s)
424
425cdef _make_process_stats(const sg.sg_process_stats *s):
426    return Result({
427        'process_name': s.process_name,
428        'proctitle': s.proctitle,
429        'pid': s.pid,
430        'parent': s.parent,
431        'pgid': s.pgid,
432        'sessid': s.sessid,
433        'uid': s.uid,
434        'euid': s.euid,
435        'gid': s.gid,
436        'egid': s.egid,
437        'context_switches': s.context_switches,
438        'voluntary_context_switches': s.voluntary_context_switches,
439        'involuntary_context_switches': s.involuntary_context_switches,
440        'proc_size': s.proc_size,
441        'proc_resident': s.proc_resident,
442        'start_time': s.start_time,
443        'time_spent': s.time_spent,
444        'cpu_percent': s.cpu_percent,
445        'nice': s.nice,
446        'state': s.state,
447        'systime': s.systime,
448        })
449
450def get_process_stats():
451    cdef size_t n
452    cdef const sg.sg_process_stats *s = sg.sg_get_process_stats(&n)
453    _vector_not_null(s, n)
454    return [_make_process_stats(&s[i]) for i in range(n)]
455
456def get_process_count(pcs=entire_process_count):
457    cdef const sg.sg_process_count *s = sg.sg_get_process_count_of(pcs)
458    _not_null(s)
459    return Result({
460        'total': s.total,
461        'running': s.running,
462        'sleeping': s.sleeping,
463        'stopped': s.stopped,
464        'zombie': s.zombie,
465        'unknown': s.unknown,
466        'systime': s.systime,
467        })
468
469# Backwards compatibility with pystatgrab <= 0.5
470
471SG_ERROR_NONE = ERROR_NONE
472SG_ERROR_INVALID_ARGUMENT = ERROR_INVALID_ARGUMENT
473SG_ERROR_ASPRINTF = ERROR_ASPRINTF
474SG_ERROR_SPRINTF = ERROR_SPRINTF
475SG_ERROR_DEVICES = ERROR_DEVICES
476SG_ERROR_DEVSTAT_GETDEVS = ERROR_DEVSTAT_GETDEVS
477SG_ERROR_DEVSTAT_SELECTDEVS = ERROR_DEVSTAT_SELECTDEVS
478SG_ERROR_DISKINFO = ERROR_DISKINFO
479SG_ERROR_ENOENT = ERROR_ENOENT
480SG_ERROR_GETIFADDRS = ERROR_GETIFADDRS
481SG_ERROR_GETMNTINFO = ERROR_GETMNTINFO
482SG_ERROR_GETPAGESIZE = ERROR_GETPAGESIZE
483SG_ERROR_HOST = ERROR_HOST
484SG_ERROR_KSTAT_DATA_LOOKUP = ERROR_KSTAT_DATA_LOOKUP
485SG_ERROR_KSTAT_LOOKUP = ERROR_KSTAT_LOOKUP
486SG_ERROR_KSTAT_OPEN = ERROR_KSTAT_OPEN
487SG_ERROR_KSTAT_READ = ERROR_KSTAT_READ
488SG_ERROR_KVM_GETSWAPINFO = ERROR_KVM_GETSWAPINFO
489SG_ERROR_KVM_OPENFILES = ERROR_KVM_OPENFILES
490SG_ERROR_MALLOC = ERROR_MALLOC
491SG_ERROR_MEMSTATUS = ERROR_MEMSTATUS
492SG_ERROR_OPEN = ERROR_OPEN
493SG_ERROR_OPENDIR = ERROR_OPENDIR
494SG_ERROR_READDIR = ERROR_READDIR
495SG_ERROR_PARSE = ERROR_PARSE
496SG_ERROR_PDHADD = ERROR_PDHADD
497SG_ERROR_PDHCOLLECT = ERROR_PDHCOLLECT
498SG_ERROR_PDHOPEN = ERROR_PDHOPEN
499SG_ERROR_PDHREAD = ERROR_PDHREAD
500SG_ERROR_PERMISSION = ERROR_PERMISSION
501SG_ERROR_PSTAT = ERROR_PSTAT
502SG_ERROR_SETEGID = ERROR_SETEGID
503SG_ERROR_SETEUID = ERROR_SETEUID
504SG_ERROR_SETMNTENT = ERROR_SETMNTENT
505SG_ERROR_SOCKET = ERROR_SOCKET
506SG_ERROR_SWAPCTL = ERROR_SWAPCTL
507SG_ERROR_SYSCONF = ERROR_SYSCONF
508SG_ERROR_SYSCTL = ERROR_SYSCTL
509SG_ERROR_SYSCTLBYNAME = ERROR_SYSCTLBYNAME
510SG_ERROR_SYSCTLNAMETOMIB = ERROR_SYSCTLNAMETOMIB
511SG_ERROR_SYSINFO = ERROR_SYSINFO
512SG_ERROR_MACHCALL = ERROR_MACHCALL
513SG_ERROR_IOKIT = ERROR_IOKIT
514SG_ERROR_UNAME = ERROR_UNAME
515SG_ERROR_UNSUPPORTED = ERROR_UNSUPPORTED
516SG_ERROR_XSW_VER_MISMATCH = ERROR_XSW_VER_MISMATCH
517SG_ERROR_GETMSG = ERROR_GETMSG
518SG_ERROR_PUTMSG = ERROR_PUTMSG
519SG_ERROR_INITIALISATION = ERROR_INITIALISATION
520SG_ERROR_MUTEX_LOCK = ERROR_MUTEX_LOCK
521SG_ERROR_MUTEX_UNLOCK = ERROR_MUTEX_UNLOCK
522
523SG_IFACE_DUPLEX_FULL = IFACE_DUPLEX_FULL
524SG_IFACE_DUPLEX_HALF = IFACE_DUPLEX_HALF
525SG_IFACE_DUPLEX_UNKNOWN = IFACE_DUPLEX_UNKNOWN
526
527SG_IFACE_DOWN = IFACE_DOWN
528SG_IFACE_UP = IFACE_UP
529
530SG_PROCESS_STATE_RUNNING = PROCESS_STATE_RUNNING
531SG_PROCESS_STATE_SLEEPING = PROCESS_STATE_SLEEPING
532SG_PROCESS_STATE_STOPPED = PROCESS_STATE_STOPPED
533SG_PROCESS_STATE_ZOMBIE = PROCESS_STATE_ZOMBIE
534SG_PROCESS_STATE_UNKNOWN = PROCESS_STATE_UNKNOWN
535
536# Some functions returned True/False for success/failure.
537def _wrap_success(func, *args):
538    try:
539        func(*args)
540        return True
541    except StatgrabError:
542        return False
543
544def sg_init():
545    return _wrap_success(init)
546def sg_snapshot():
547    return _wrap_success(snapshot)
548def sg_shutdown():
549    return _wrap_success(shutdown)
550def sg_drop_privileges():
551    return _wrap_success(drop_privileges)
552
553# The error-handling functions just wrapped those from libstatgrab.
554def sg_get_error():
555    return sg.sg_get_error()
556def sg_get_error_arg():
557    return sg.sg_get_error_arg()
558def sg_get_error_errno():
559    return sg.sg_get_error_errno()
560def sg_str_error(code):
561    return _bytes_to_str(sg.sg_str_error(code))
562
563# Some functions work the same way, just with a different name.
564sg_get_host_info = get_host_info
565sg_get_cpu_stats = get_cpu_stats
566sg_get_cpu_stats_diff = get_cpu_stats_diff
567sg_get_cpu_percents = get_cpu_percents
568sg_get_mem_stats = get_mem_stats
569sg_get_load_stats = get_load_stats
570sg_get_swap_stats = get_swap_stats
571sg_get_fs_stats = get_fs_stats
572sg_get_disk_io_stats = get_disk_io_stats
573sg_get_disk_io_stats_diff = get_disk_io_stats_diff
574sg_get_network_io_stats = get_network_io_stats
575sg_get_network_io_stats_diff = get_network_io_stats_diff
576sg_get_network_iface_stats = get_network_iface_stats
577sg_get_page_stats = get_page_stats
578sg_get_page_stats_diff = get_page_stats_diff
579sg_get_process_stats = get_process_stats
580sg_get_process_count = get_process_count
581
582# User stats used to be a single result rather than a list.
583def sg_get_user_stats():
584    cdef size_t n
585    cdef const sg.sg_user_stats *s = sg.sg_get_user_stats(&n)
586    _vector_not_null(s, n)
587    return Result({
588        'name_list': " ".join([_bytes_to_str(s[i].login_name) for i in range(n)]),
589        'num_entries': n,
590        })
591