File pg_top-4.1.1.obscpio of Package pg_top

07070100000000000081A400000000000000000000000166622DE800000082000000000000000000000000000000000000002200000000pg_top-4.1.1/.containerfileignore**/*.sw?

builds/
cmake_install.cmake
CMakeCache.txt
Containerfile*
.containerignore
.git
.gitignore
install_manifest.txt
.vscode
07070100000001000081A400000000000000000000000166622DE80000012E000000000000000000000000000000000000001800000000pg_top-4.1.1/.gitignore*.sw?
*.o
*.in
.deps
config.h
!config.h.in
depcomp
pg_top
pg_top.1
tags

*.BAK

cscope.in.out
cscope.out
cscope.po.out

# cmake

/builds

CMakeCache.txt
CMakeFiles/
cmake_install.cmake
install_manifest.txt

CPackConfig.cmake
CPackSourceConfig.cmake
_CPack_Packages/
*.tar.bz2
*.tar.gz
*.tar.xz
*.tar.Z
07070100000002000081A400000000000000000000000166622DE80000270B000000000000000000000000000000000000001C00000000pg_top-4.1.1/CMakeLists.txtcmake_minimum_required(VERSION 3.1.0)

include(CheckFunctionExists)
include(CheckLibraryExists)
include(CheckIncludeFiles)
include(CheckCSourceCompiles)

project(pg_top C)

set(PROJECT_VERSION_MAJOR 4)
set(PROJECT_VERSION_MINOR 1)
set(PROJECT_VERSION_PATCH 1)
set(PROJECT_VERSION
    ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})

set(CMAKE_C_STANDARD 99)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_C_FLAGS_DEBUG "-Wall")

# Handle cmake -D command line options

if (NOT DEFINED ENABLE_COLOR)
    set(ENABLE_COLOR 1)
elseif(ENABLE_COLOR EQUAL 0)
    unset(ENABLE_COLOR)
else()
    set(ENABLE_COLOR "${ENABLE_COLOR}")
endif (NOT DEFINED ENABLE_COLOR)

# Determine which machine module to build

execute_process(
    COMMAND uname -s
    OUTPUT_VARIABLE MACHINE
    OUTPUT_STRIP_TRAILING_WHITESPACE
)
string(TOLOWER "${MACHINE}" MACHINE)
message("-- machine - ${MACHINE}")

execute_process(
    COMMAND uname -m
    OUTPUT_VARIABLE ARCH
    OUTPUT_STRIP_TRAILING_WHITESPACE
)
message("-- arch - ${ARCH}")

# Some machines modules need additional information.

execute_process(
    COMMAND uname -r
    OUTPUT_VARIABLE OSREV
    OUTPUT_STRIP_TRAILING_WHITESPACE
)
string(REGEX MATCH "^([0-9]*)" OSMAJOR "${OSREV}")
string(REPLACE "." "" OSREV "${OSREV}")
string(REPLACE "-" "" OSREV "${OSREV}")
string(REPLACE "_" "" OSREV "${OSREV}")

# Get PostgreSQL include and library directories.

execute_process(
    COMMAND pg_config --includedir
    OUTPUT_VARIABLE PGINCLUDEDIR
    OUTPUT_STRIP_TRAILING_WHITESPACE
)
message("-- pg_config --includedir - ${PGINCLUDEDIR}")
if(PGINCLUDEDIR)
    set(PGINCLUDE "-I${PGINCLUDEDIR}")
endif(PGINCLUDEDIR)

execute_process(
    COMMAND pg_config --libdir
    OUTPUT_VARIABLE PGLIBDIR
    OUTPUT_STRIP_TRAILING_WHITESPACE
)
message("-- pg_config --libdir - ${PGLIBDIR}")

# Check for include files.

check_include_files("stdlib.h;stdarg.h;string.h;float.h" STDC_HEADERS)

check_include_files("string.h" HAVE_STRING_H)
check_include_files("strings.h" HAVE_STRINGS_H)
check_include_files("sys/time.h;time.h" TIME_WITH_SYS_TIME)
check_include_files("sys/time.h" HAVE_SYS_TIME_H)
check_include_files("sys/resource.h" HAVE_SYS_RESOURCE_H)
check_include_files("unistd.h" HAVE_UNISTD_H)

# Check for library functions.

check_function_exists(getopt HAVE_GETOPT)
check_function_exists(memcpy HAVE_MEMCPY)
check_function_exists(setpriority HAVE_SETPRIORITY)
check_function_exists(sigaction HAVE_SIGACTION)
check_function_exists(sighold HAVE_SIGHOLD)
check_function_exists(sigprocmask HAVE_SIGPROCMASK)
check_function_exists(sigrelse HAVE_SIGRELSE)
check_function_exists(snprintf HAVE_SNPRINTF)
check_function_exists(strchr HAVE_STRCHR)
check_function_exists(strerror HAVE_STRERROR)

# Test return type of signal().

check_c_source_compiles("
#include <sys/types.h>
#include <signal.h>
int main () { return *(signal (0, 0)) (0) == 1; return 0; }
" SIGNAL_RETURN)
if(SIGNAL_RETURN)
  set(RETSIGTYPE "int")
else()
  set(RETSIGTYPE "void")
endif(SIGNAL_RETURN)

# Test whether time_t is defined.

check_c_source_compiles("
#include <sys/types.h>
int main () { time_t a; return 0; }
" TIME_T_DEFINED)
if(NOT TIME_T_DEFINED)
    set(time_t "long")
endif(NOT TIME_T_DEFINED)

# Build config.h header.

configure_file(
    ${CMAKE_SOURCE_DIR}/config.h.in
    ${CMAKE_BINARY_DIR}/config.h
)

# Build manual page with appropriate machine specific notes.

configure_file(
    ${CMAKE_SOURCE_DIR}/pg_top.1.rst.in
    ${CMAKE_BINARY_DIR}/pg_top.1.rst
)
if(EXISTS "${CMAKE_SOURCE_DIR}/machine/m_${MACHINE}.man.rst")
    execute_process(
        COMMAND cat ${CMAKE_SOURCE_DIR}/machine/m_${MACHINE}.man.rst
        OUTPUT_VARIABLE MANUAL_SUPPLIMENT
    )
    file(APPEND ${CMAKE_BINARY_DIR}/pg_top.1.rst "${MANUAL_SUPPLIMENT}")
endif(EXISTS "${CMAKE_SOURCE_DIR}/machine/m_${MACHINE}.man.rst")
file(APPEND ${CMAKE_BINARY_DIR}/pg_top.1 "${MANUAL_SUPPLIMENT}")
execute_process(
    COMMAND rst2man.py ${CMAKE_BINARY_DIR}/pg_top.1.rst
    OUTPUT_FILE ${CMAKE_BINARY_DIR}/pg_top.1
)

# Set appropriate compile flags.

set_source_files_properties(
    color.c
    commands.c
    display.c
    pg.c
    pg_top.c
    screen.c
    sprompt.c
    utils.c
    PROPERTIES COMPILE_FLAGS "${PGINCLUDE}"
)
set_source_files_properties(
    version.c
    PROPERTIES COMPILE_FLAGS
    "-DPACKAGE_VERSION=\\\"${PROJECT_VERSION}\\\" ${PGINCLUDE}"
)
set_source_files_properties(
    machine/m_common.c
    machine/m_remote.c
    machine/m_${MACHINE}.c
    PROPERTIES COMPILE_FLAGS "-I${CMAKE_HOME_DIRECTORY} ${PGINCLUDE}"
)

if (NOT ${HAVE_GETOPT})
    set (EXTRAFILES "${EXTRAFILES} getopt.c")
endif (NOT ${HAVE_GETOPT})

add_executable(
    ${PROJECT_NAME}
    color.c
    commands.c
    display.c
    screen.c
    sprompt.c
    pg.c
    pg_top.c
    utils.c
    version.c
    machine/m_remote.c
    machine/m_common.c
    machine/m_${MACHINE}.c
    ${EXTRAFILES}
)

# Determine appropriate linker flags.

find_library(LIBPQ pq PATHS ${PGLIBDIR})
if(LIBPQ)
    target_link_libraries(${PROJECT_NAME} ${LIBPQ})
endif(LIBPQ)

find_library(LIBM m)
if(LIBM)
    target_link_libraries(${PROJECT_NAME} ${LIBM})
endif(LIBM)

find_library(LIBDL dl)
if(LIBDL)
    target_link_libraries(${PROJECT_NAME} ${LIBDL})
endif(LIBDL)

find_library(LIBINTL intl)
if(LIBINTL)
    target_link_libraries(${PROJECT_NAME} ${LIBINTL})
endif(LIBINTL)

find_library(LIBTINFO tinfo)
if(LIBTINFO)
    target_link_libraries(${PROJECT_NAME} ${LIBTINFO})
endif(LIBTINFO)

find_library(LIBTERMCAP termcap)
if(LIBTERMCAP)
    target_link_libraries(${PROJECT_NAME} ${LIBTERMCAP})
else(LIBTERMCAP)
    find_library(LIBCURSES curses)
    if(LIBCURSES)
        target_link_libraries(${PROJECT_NAME} ${LIBCURSES})
    else(LIBCURSES)
        find_library(LIBNCURSES ncurses REQUIRED)
        if(LIBNCURSES)
            target_link_libraries(${PROJECT_NAME} ${LIBNCURSES})
        endif(LIBNCURSES)
    endif(LIBCURSES)
endif(LIBTERMCAP)

find_library(LIBKSTAT kstat)
if(LIBKSTAT)
    target_link_libraries(${PROJECT_NAME} ${LIBKSTAT})
endif(LIBKSTAT)

find_library(LIBMACH mach)
if(LIBMACH)
    target_link_libraries(${PROJECT_NAME} ${LIBMACH})
endif(LIBMACH)

find_library(LIBBSD bsd REQUIRED)
if(LIBBSD)
    target_link_libraries(${PROJECT_NAME} ${LIBBSD})
endif(LIBBSD)

# FreeBSD specific libraries

if(${MACHINE} STREQUAL freebsd)
    find_library(LIBKVM kvm)
    if(LIBKVM)
        target_link_libraries(${PROJECT_NAME} ${LIBKVM})
    endif(LIBKVM)
endif(${MACHINE} STREQUAL freebsd)

install(
    PROGRAMS
    ${CMAKE_BINARY_DIR}/${PROJECT_NAME}
    DESTINATION "bin"
)
install(
    FILES
    ${CMAKE_BINARY_DIR}/pg_top.1
    DESTINATION "share/man/man1"
)

# CPack rules for creating distribution files.

include(InstallRequiredSystemLibraries)

set(CPACK_CMAKE_GENERATOR "Unix Makefiles")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "pg_top is 'top' for PostgreSQL")
set(CPACK_PACKAGE_VENDOR "pg_top")
set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README.rst")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}")
set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}")
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}")
set(
    CPACK_SOURCE_IGNORE_FILES
    "\\\\.bz2\\$;\\\\.gz\\$;\\\\.xz\\$;\\\\.Z\\$;\\\\.zip\\$"
)
set(
    CPACK_SOURCE_IGNORE_FILES
    "${CPACK_SOURCE_IGNORE_FILES};/\\\\.gitignore\\$"
)
set(
    CPACK_SOURCE_IGNORE_FILES
    "${CPACK_SOURCE_IGNORE_FILES};/CMakeCache\\\\.txt\\$"
)
set(
    CPACK_SOURCE_IGNORE_FILES
    "${CPACK_SOURCE_IGNORE_FILES};/CMakeFiles/"
)
set(
    CPACK_SOURCE_IGNORE_FILES
    "${CPACK_SOURCE_IGNORE_FILES};/CMakeCache\\\\.txt\\$"
)
set(
    CPACK_SOURCE_IGNORE_FILES
    "${CPACK_SOURCE_IGNORE_FILES};/cmake_install\\\\.cmake\\$"
)
set(
    CPACK_SOURCE_IGNORE_FILES
    "${CPACK_SOURCE_IGNORE_FILES};/config\\\\.h\\$"
)
set(
    CPACK_SOURCE_IGNORE_FILES
    "${CPACK_SOURCE_IGNORE_FILES};/CPackConfig\\\\.cmake\\$"
)
set(
    CPACK_SOURCE_IGNORE_FILES
    "${CPACK_SOURCE_IGNORE_FILES};/\\\\_CPack\\\\_Packages/"
)
set(
    CPACK_SOURCE_IGNORE_FILES
    "${CPACK_SOURCE_IGNORE_FILES};/CPackSourceConfig\\\\.cmake\\$"
)
set(
    CPACK_SOURCE_IGNORE_FILES
    "${CPACK_SOURCE_IGNORE_FILES};/Makefile\\$"
)
set(
    CPACK_SOURCE_IGNORE_FILES
    "${CPACK_SOURCE_IGNORE_FILES};/pg_top\\\\.1\\$"
)
set(
    CPACK_SOURCE_IGNORE_FILES
    "${CPACK_SOURCE_IGNORE_FILES};/sigdesc\\\\.h\\$"
)

include(CPack)

set(OUTPUT "${PROJECT_VERSION}")

add_custom_target(appimage
    COMMAND cp -aL ${CMAKE_SOURCE_DIR}/appimage/activate
                   ${CMAKE_SOURCE_DIR}/appimage/AppRun
                   ${CMAKE_SOURCE_DIR}/appimage/pg_top.desktop
                   ${CMAKE_SOURCE_DIR}/appimage/pg_top.svg
            AppDir
    COMMAND mkdir -p AppDir/usr/share/metainfo
    COMMAND cp -aL ${CMAKE_SOURCE_DIR}/appimage/pg_top.appdata.xml
            AppDir/usr/share/metainfo
    COMMAND mkdir -p AppDir/usr/lib
    COMMAND cp -aL `ldd pg_top | grep libpq\\\\\\\\. | cut -d \" \" -f 3`
                   `ldd pg_top | grep libtinfo\\\\\\\\. | cut -d \" \" -f 3`
                   `ldd pg_top | grep libbsd\\\\\\\\. | cut -d \" \" -f 3`
                   `ldd pg_top | grep libssl\\\\\\\\. | cut -d \" \" -f 3`
                   `ldd pg_top | grep libcrypto\\\\\\\\. | cut -d \" \" -f 3`
                   `ldd pg_top | grep libgssapi_krb5\\\\\\\\. | cut -d \" \" -f 3`
                   `ldd pg_top | grep libkrb5\\\\\\\\. | cut -d \" \" -f 3`
                   `ldd pg_top | grep libk5crypto\\\\\\\\. | cut -d \" \" -f 3`
                   `ldd pg_top | grep libkrb5support\\\\\\\\. | cut -d \" \" -f 3`
                   `ldd pg_top | grep libkeyutils\\\\\\\\. | cut -d \" \" -f 3`
                   `ldd pg_top | grep libselinux\\\\\\\\. | cut -d \" \" -f 3`
            AppDir/usr/lib
    COMMAND VERSION=${PROJECT_VERSION} /usr/local/squashfs-root/AppRun
            --comp=xz AppDir
)
07070100000003000081A400000000000000000000000166622DE8000009E1000000000000000000000000000000000000002400000000pg_top-4.1.1/Containerfile.appimage# Build an environment for creating an AppImage.

# Use a distro with an old libc to maximize support on as many linux distros
# and architectures as possible.
FROM ubuntu:14.04

ARG ARCH

RUN apt-get -qq -y update && \
    apt-get -qq -y dist-upgrade && \
    apt-get -qq -y install bison \
                   bzip2 \
                   curl \
                   file \
                   flex \
                   gcc \
                   libc6-dev \
                   libbsd-dev \
                   libncurses5-dev \
                   make \
                   wget \
                   xz-utils && \
    apt-get -qq -y clean && \
    apt-get -qq -y autoclean

WORKDIR /usr/local
ARG APPIMAGEPATH="AppImage/AppImageKit/releases/download/13"
ARG APPIMAGEFILENAME="appimagetool-${ARCH}.AppImage"
ARG APPIMAGEURL="https://github.com/${APPIMAGEPATH}/${APPIMAGEFILENAME}"
RUN curl -OL ${APPIMAGEURL} && \
    chmod +x appimagetool-${ARCH}.AppImage && \
    ./appimagetool-${ARCH}.AppImage --appimage-extract && \
    chmod 0755 squashfs-root && \
    chmod 0755 squashfs-root/usr && \
    chmod 0755 squashfs-root/usr/bin && \
    chmod 0755 squashfs-root/usr/lib && \
    chmod 0755 squashfs-root/usr/lib/appimagekit && \
    chmod 0755 squashfs-root/usr/share

ARG OPENSSLVER="1.1.1s"
RUN wget -O /tmp/openssl-${OPENSSLVER}.tar.gz -q --no-check-certificate \
         https://www.openssl.org/source/openssl-${OPENSSLVER}.tar.gz && \
    tar -C /usr/local/src -xf /tmp/openssl-${OPENSSLVER}.tar.gz
WORKDIR /usr/local/src/openssl-${OPENSSLVER}
RUN ./config -fPIC shared && \
    make -s -j$(nproc) && \
    make -s install

# PostgreSQL 10 is the first release supporting multiple hosts in the
# connection string.
ARG PGVER="10.23"
ARG PGFILENAME="postgresql-${PGVER}.tar.bz2"
ARG PGURL="https://ftp.postgresql.org/pub/source/v${PGVER}/${PGFILENAME}"
RUN wget -O /tmp/postgresql-${PGVER}.tar.bz2 -q --no-check-certificate \
         ${PGURL}
RUN tar -C /usr/local/src -xf /tmp/postgresql-${PGVER}.tar.bz2
WORKDIR /usr/local/src/postgresql-${PGVER}
RUN ./configure --silent --without-ldap --without-readline --without-zlib \
    --without-gssapi --with-openssl --prefix=/usr && \
    make -s -j $(nproc) install && \
    ldconfig

ARG CMAKEVER="3.1.3"
RUN curl -o /tmp/cmake-${CMAKEVER}-linux-${ARCH}.tar.gz -SsOL \
         https://github.com/Kitware/CMake/releases/download/v${CMAKEVER}/cmake-${CMAKEVER}-linux-${ARCH}.tar.gz && \
    tar -C /usr --strip-components=1 \
        -xf /tmp/cmake-${CMAKEVER}-linux-${ARCH}.tar.gz
07070100000004000081A400000000000000000000000166622DE800000F44000000000000000000000000000000000000001900000000pg_top-4.1.1/HISTORY.rstRelease Notes
=============

2024-06-06 v4.1.1
-----------------

* Fix remote query display to query in a single line
* Remove requirement for unused libelf

2023-07-01 v4.1.0
-----------------

* Set CMake minimum version to 2.8.12
* Add container files for creating an AppImage using AppImageKit v13
* Update AppImage notes
* Fix string truncation warnings in byte and kilobyte pretty functions
* Replace reallocarray() with POSIX realloc()
* Fix escaping in CMakeLists.txt AppImage target
* Removed building of sigdesc.h, since kill commands was removed in v4.0.0
* Fix CMake handling of building getopt.c when not found
* Bump CMake minimum to 3.1.0

2020-08-05 v4.0.0
-----------------

* Replace autoconf with cmake
* Remove table stats monitoring, use pg_systat instead
  https://pg_systat.gitlab.io/
* Remove index stats monitoring, use pg_systat instead
  https://pg_systat.gitlab.io/
* Remove kill and renice command, and stop displaying nice priority
* Show backend state instead of operating system state
* Show database username instead of operating system username
* Make connections with password persistent and clear connection memory in
  these scenarios
* Fix sorting when specified from the command line and in interactive mode
* Add display for top replication processes using 'R' command line argument or
  'R' command
* Add 'a' command to switch to top 'activity' processes, the default, while
  removing the 'I' for the top I/O display as a toggle
* Show swapping activity
* Simplify top I/O display
* Remove toggle for display raw I/O statistics
* Fix bug for specifing number of displays to show on the command line without
  -x flag
* Fix view of locks held by process.  Separate table and index locks.  Also
  showing schema.
* Handle longer pids on Linux
* Updated for FreeBSD 12.
* Updated for OpenBSD 6.7.

2013-07-31 v3.7.0
-----------------

* Added support for monitoring databases on remote systems.
* Added support for monitoring i/o statistics on Linux.
* Updated for changed introduced in PostgreSQL 9.2.
* Updated for OpenBSD 5.2.
* Updated for FreeBSD 9.1.
* Updated for OS X Mountain Lion (10.8).
* Add monitoring for database activity
* Add monitoring for disk activity
* Add monitoring for disk space
* Add long options

2008-05-03 v3.6.2
-----------------

* Add 'A' command to re-run SQL statement and show actual execution plan
  (EXPLAIN ANALYZE) of a running query.
* Fixed 'E' command (EXPLAIN) to be UPDATE and INSERT safe.
* Updated the automake file so other targets like 'make dist' and 'make
  distdir' work.
* Fixed a bug so user table statistics can be sorted.
* Added a 't' command so that user table and index statistics can display
  either cumulative or differential statistics.
* Fixed support for OS X, tested on v10.4.x, v10.5.x.
* Added support for OpenBSD, tested on v4.2.
* Rename 'ptop' to 'pg_top' to fit PostgreSQL naming conventions and avoid
  naming conflict with free pascal's source formatter 'ptop'.
* Recognize PGDATABASE, PGHOST, PGUSER, and PGPORT environment variables.

2008-03-05 v3.6.1
-----------------

* Add -h command line option to specify a socket file when connected to the
  database..
* Use the same -p PORT, -U USER, and -d DBNAME options as other PostgreSQL
  programs.
* Change unixtop's original -d to -x, and -U to -z.
* Add 'X' command to view user index statistics.
* Add 'R' command to view user table statistics.
* Add support for Solaris 10.
* Add support for FreeBSD.
* Add 'E' command to re-determine and show execution plan of a running SQL
  statement.
* Add parameters to specify database connection information.
* Add 'L' command to show locks held by a process.
* Add 'Q' command to show current query of a process.
* Rename 'top' to 'ptop'.
* Add support for Linux.
* Configure support for PostgreSQL libpq client libraries.
* Remove old_modules directory.
* Update RES calculation for Linux 2.6.x.
07070100000005000081A400000000000000000000000166622DE8000002D1000000000000000000000000000000000000001900000000pg_top-4.1.1/INSTALL.rstpg_top
======

Installation
------------

Configuring
~~~~~~~~~~~

::

  cmake [options] CMakeLists.txt

options:

-DCMAKE_INSTALL_PREFIX=PREFIX   Install files in PREFIX.  Default is
                                '/usr/local'.
-DENABLE_COLOR=0                Default on.  Include code that allows for the
                                use of color in the output display.  Use
                                -DENABLE_COLOR=0 if you do not want this
                                feature compiled in to the code.  The configure
                                script also recognizes the spelling "colour".

Installing
~~~~~~~~~~

::

  make install

Uninstalling
~~~~~~~~~~~~

::

  xargs rm < install_manifest.txt
07070100000006000081A400000000000000000000000166622DE8000005EC000000000000000000000000000000000000001500000000pg_top-4.1.1/LICENSECopyright (c) 2005, William LeFebvre
Copyright (c) 2007-2013, Mark Wong

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.  Redistributions in binary
* form must reproduce the above copyright notice, this list of conditions and
* the following disclaimer in the documentation and/or other materials provided
* with the distribution.  Neither the name of Mark Wong nor the names
* of its contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
07070100000007000081A400000000000000000000000166622DE8000002AD000000000000000000000000000000000000001C00000000pg_top-4.1.1/Makefile.cmake.PHONY: appimage clean default debug package release

default:
	@echo "targets: appimage (Linux only), clean, debug, package, release"

appimage:
	cmake -H. -Bbuilds/appimage -DCMAKE_INSTALL_PREFIX=/usr
	cd builds/appimage && make
	cd builds/appimage && sed -i -e 's#/usr#././#g' pg_top
	cd builds/appimage && make install DESTDIR=AppDir
	cd builds/appimage && make appimage

clean:
	-rm -rf builds

debug:
	cmake -H. -Bbuilds/debug -DCMAKE_BUILD_TYPE=Debug
	cd builds/debug && make

package:
	git checkout-index --prefix=builds/source/ -a
	cmake -Hbuilds/source -Bbuilds/source
	cd builds/source && make package_source

release:
	cmake -H. -Bbuilds/release
	cd builds/release && make
07070100000008000081A400000000000000000000000166622DE80000213B000000000000000000000000000000000000001500000000pg_top-4.1.1/PortingInstructions for porting pg_top to other architectures.

This is still a preliminary document.  Suggestions for improvement are
most welcome.

Before you embark on a port, please send me a mail message telling me
what platform you are porting pg_top to.  There are three reasons for
this: (1) I may already have a port, (2) module naming needs to be
centralized, (3) I want to loosely track the various porting efforts.
You do not need to wait for an "okay", but I do want to know that you
are working on it.  And of course, once it is finished, please send me
the module files so that I can add them to the main distribution!

----------

There is one set of functions which extract all the information that
pg_top needs for display.  These functions are collected in to one file.
To make pg_top work on a different architecture simply requires a
different implementation of these functions.  The functions for a
given architecture "foo" are stored in a file called "m_foo.c".  The
Configure script looks for these files and lets the configurer choose
one of them.  This file is called a "module".  The idea is that making
pg_top work on a different machine only requires one additional file and
does not require changes to any existing files.

A module template is included in the distribution, called "m-template".
To write your own module, it is a good idea to start with this template.
If you architecture is similar to one for which a module already
exists, then you can start with that module instead.  If you do so,
remember to change the "AUTHOR" section at the top!

The first comment in a module contains information which is extracted
and used by Configure.  This information is marked with words in all
capitals (such as "SYNOPSIS:" and "LIBS:").  Go look at m-template: it
is fairly self-explanatory.  The text after "LIBS:" (on the same line)
is extracted and included in the LIBS definition of the Makefile so
that extra libraries which may be necessary on some machines (such as
"-lkvm") can be specified in the module.  The text after "CFLAGS:"
(on the same line) is extracted and included as flags in the "CFLAGS"
definition of the Makefile (thus in every compilation step).  This is
used for rare circumstances only:  please don't abuse this hook.

Some operating systems have idiosyncrasies which will affect the form
and/or content of the information pg_top displays.  You may wish to
document such anomalies in the pg_top man page.  This can be done by adding
a file called m_{modulename}.man (where {modulename} is replaced with
the name of the module).  Configure will automatically add this file to
the end of the man page.  See m_sunos4.man for an example.

A module is concerned with two structures:

The statics struct is filled in by machine_init.  Each item is a
pointer to a list of character pointers.  The list is terminated 
with a null pointer.

struct statics
{
    char **procstate_names;	/* process state names */
    char **cpustate_names;	/* cpu state names */
    char **memory_names;	/* memory information names */
};

The system_info struct is filled in by get_system_info and
get_process_info.

struct system_info
{
    int    last_pid;     /* last pid assigned (0 means non-sequential assignment) */
    double load_avg[NUM_AVERAGES];     /* see below */
    int    p_total;      /* total number of processes */
    int    p_active;     /* number of procs considered "active" */
    int    *procstates;  /* array of process state counters */
    int    *cpustates;   /* array of cpustate counters */
    int    *memory;      /* memory information */
};

The io_info struct is filled in by get_io_info.
struct io_info {
    int64_t reads;
    int64_t readsectors;
    int64_t writes;
    int64_t writesectors;
};


The last three pointers each point to an array of integers.  The
length of the array is determined by the length of the corresponding
_names array in the statics structure.  Furthermore, if an entry in a
_names array is the empty string ("") then the corresponding value in
the value array will be skipped over.  The display routine displays,
for example, the string procstate_names[0] then the number
procstates[0], then procstate_names[1], procstates[1], etc. until
procstate_names[N] == NULL.  This allows for a tremendous amount of
flexibility in labeling the displayed values.

"procstates" and "memory" are displayed as straight integer values.
Values in "cpustates" are displayed as a percentage * 10.  For
example, the (integer) value 105 is displayed as 10.5%.

These routines must be defined by the machine dependent module.

int machine_init(struct statics *)

	returns 0 on success and -1 on failure,
	prints error messages

char *format_header(char *)

	Returns a string which should be used as the header for the
	process display area.  The argument is a string used to label
	the username column (either "USERNAME" or "UID") and is always
	8 characters in length.

void get_system_info(struct system_info *)

caddr_t get_process_info(struct system_info *, int, int, int (*func)())

	returns a handle to use with format_next_process

char *format_next_process(caddr_t, char *(*func)())

	returns string which describes next process

int proc_compare(caddr_t, caddr_t)

	qsort comparison function

void get_io_info(struct io_info *)

get_process_info is called immediately after get_system_info.  In
fact, the two functions could be rolled in to one.  The reason they
are not is mostly historical.

The file "machine.h" contains definitions which are useful to modules
and to pg_top.c (such as the structure definitions).  You SHOULD NOT need
to change it when porting to a new platform.

Porting to a new platform should NOT require any changes to existing
files.  You should only need to add m_ files.  If you feel you need a
change in one of the existing files, please contact me so that we can
discuss the details.  I want to keep such changes as general as
possible.

--------

Changes were made to the module interface between 3.5 and 3.6.  Here are
the changes that need to be made to port a 3.5 module to 3.6:

The array that stores memory statistics and is passed back in the system
information structure as "memory" must now be an array of (signed) longs.
This was done to more easily accomodate systems that have gigabytes of
memory.  Since the numbers are supposed to be kilobytes, a long can still
represent up to 2 terabytes.  Look for "int memory_stats[X]" (where "X"
is some arbitrary number) and change it to "long memory_stats[X]".  If
the module support reporting swap information on a separate line, then
its "swap_stats" array also needs to be an array of longs.

The argument to proc_owner should be an int, as in "int pid".  When it is
used in proc_owner it should be cast as necessary.  Many operating systems
will require it to be cast to a pid_t before being compared to the appropriate
element in the proc structure.

In the function format_next_process, the last argument in the main call
to sprintf is the string that contains the command for the process.
Make sure that this last argument is enclosed in a call to "printable".
For example:  "printable(MPP(pp, p_comm))".

The third argument to "get_process_info" needs to be changed to an integer,
typically "int compare_index".  The call to qsort in get_process_info may
be guarded by "if (compare != NULL)".  If it is, remove the if statement.

The other changes to get_process_info depends on whether or not the module
supports multiple sort orders.

To support multiple keys:

Create an array int (*proc_compares[])() and assign to it the list of
comparison functions, NULL terminated.  For example:

int (*proc_compares[])() = {
    compare_cpu,
    compare_size,
    compare_res,
    compare_time,
    NULL };

In get_process_info there is a call to qsort which uses one of the
functions in proc_compares.  It should be changed so that its fourth
argument is "proc_compares[compare_index]".

If the module contains the function "proc_compare", it should be removed.

There should also be a NULL-terminated array of strings which list the names
for the sort keys, for example:

char *ordernames[] = 
{"cpu", "size", "res", "time", NULL};

To indicate that this module supports multiple sort keys, add the following
line in machine_init:

	statics->order_names = ordernames;

If there is no support for multiple keys:

Leave statics->order_names alone and call the comparison function of
your choice in get_process_info, ignoring the third argument.
07070100000009000081A400000000000000000000000166622DE800000583000000000000000000000000000000000000002100000000pg_top-4.1.1/README.appimage.rstAppImages are only for Linux based systems:

  https://appimage.org/

The AppImageKit AppImage can be downloaded from:

  https://github.com/AppImage/AppImageKit/releases

It is recommended to build AppImages on older distributions:

  https://docs.appimage.org/introduction/concepts.html#build-on-old-systems-run-on-newer-systems

At the time of this document, CentOS 7 is the one of the oldest supported Linux
distributions with the oldest libc version.

Use a custom configured PostgreSQL build with minimal options enabled to reduce
library dependency support.  Part of this reason is to make it easier to
include libraries with compatible licences.  Part of the reason is to be able
to build libpq with minimal development dependencies for PostgreSQL, since the
purpose is just to build a pg_top AppImage.  At least version PostgreSQL 10
should be used so that PGHOST can be defined in the AppRun script to search
multiple potential unixsock directories if no PGHOST is defined in the user's
environment.

At the time of this document, PostgreSQL 10 was configured with the following
options::

  ./configure --without-ldap --without-readline --without-zlib \
        --without-gssapi --with-openssl

Don't forget you may have to set both PATH and LD_LIBRARY_PATH appropriately
depending on where the custom build of PostgreSQL is installed.

See the README.rst in the container/ directory for an example.
0707010000000A000081A400000000000000000000000166622DE80000067D000000000000000000000000000000000000001800000000pg_top-4.1.1/README.rstpg_top
======

pg_top is 'top' for PostgreSQL. It is derived from Unix Top. Similar to top,
pg_top allows you to monitor PostgreSQL processes. It also allows you to:

* View currently running SQL statement of a process.
* View query plan of a currently running SELECT statement.
* View locks held by a process.
* View I/O statistics per process.
* View replication statistics for downstream nodes.

To compile and install "pg_top", read the file "INSTALL.rst" and follow the
directions and advice contained therein.

If you make any kind of change to "pg_top" that you feel would be
beneficial to others who use this program, or if you find and fix a bug,
please submit the change to the pg_top issue tracker:

  https://gitlab.com/pg_top/pg_top/issues

In order to monitor a remote database, the pg_proctab extension needs to be
created on the database to be monitored.  Any operating system that pg_proctab
supports can be monitored remotely on any operating system.  See details for
pg_proctab here:

  https://gitlab.com/pg_proctab/pg_proctab

Availability
------------

Project home page:

  https://pg_top.gitlab.io/


If you have git, you can download the source code::

  git clone git@gitlab.com:pg_top/pg_top.git

Logo
----

The logo is just a pipe (|) and a capital V using the free Ampad Brush true
type font.

Gratitude
---------

Selena Deckelmann & Gabrielle Roth, and the beer & free wi-fi at County Cork
pub in Portland, OR, USA.

License
-------

pg_top is distributed free of charge under the same terms as the BSD
license.  For an official statement, please refer to the file "LICENSE"
which should be included with the source distribution.
0707010000000B000081A400000000000000000000000166622DE80000034D000000000000000000000000000000000000001200000000pg_top-4.1.1/TODO* Test on more platforms.  Development has primarily been on Linux and some
  attempts have been made on FreeBSD, OpenBSD, and OS X.

* Display status in the executor using pstack/dtract/gstack.

* Display elapsed time of currently running query.

* Display SQL statement type currently executing, i.e. SELECT, UPDATE,
  VACUUM, etc.

* Display summary statistics for average query cpu time, average query elapsed
  time, etc.

* Display summary statistics for locks such as total number of granted locks,
  total number of ungranted locks, etc.

* Make new TPS and i/o activity and disk space usage work on remote
  connections.

* Add warning when user running pg_top does not have OS privileges to view i/o
  statistics.  /proc/*/io may only be readable by process owner.

* Add warning when user i/o stats are not avaialble from OS. (Linux)
0707010000000C000081A400000000000000000000000166622DE8000004CD000000000000000000000000000000000000001100000000pg_top-4.1.1/Y2Kpg_top and the Year 2000

The software package pg_top will not be affected by years numbering
between 2000 and 2037.  No portion of the pg_top code stores dates on
disk.  All date processing in pg_top is performed with functions from the
Unix C library and Unix kernel.  The specific functions are: time(2)
and ctime(3S).  These functions deal exclusively with conventional
Unix time values (number of seconds since Midnight January 1, 1970
GMT) and produce strings with a 4-digit year.  At no point in the code
for pg_top are the last two digits used to represent a year.

Top and the Year 2038

In the year 2038 pg_top will fail to represent the time of day correctly
on 32-bit Unix operating systems.  This is due to a limitation in the
way Unix represents time.  Top will only work on systems whose kernel
call "time" and C library call "ctime" have been adjusted to represent
time with a value greater than 32 bits.  The exact date and time of
this failure is 3:14:08 January 19, 2038 GMT.  Note that this failure
will only affect the display of the current time in the output from
pg_top.


THERE IS ABSOLUTELY NO WARRANTY PROVIDED WITH THIS SOFTWARE.
Please see the contents of the file "LICENSE" for further
information.
0707010000000D000041ED00000000000000000000000266622DE800000000000000000000000000000000000000000000001600000000pg_top-4.1.1/appimage0707010000000E000081ED00000000000000000000000166622DE8000000B1000000000000000000000000000000000000001D00000000pg_top-4.1.1/appimage/AppRun#!/bin/sh

export LD_LIBRARY_PATH="${APPDIR}/usr/lib:${LD_LIBRARY_PATH}"
if [ "x$PGHOST" = "x" ]; then
	export PGHOST="/tmp,/var/run/postgresql"
fi

${APPDIR}/usr/bin/pg_top $@
0707010000000F000081A400000000000000000000000166622DE8000003E4000000000000000000000000000000000000001F00000000pg_top-4.1.1/appimage/activateif [ "${BASH}" = "" ]; then
	echo "sourcing activate currently requires a bash shell"
	return
fi

APPIMAGE_NAME="pg_top"

deactivate()
{
	PATH="${APPIMAGE_OLD_PATH}"
	export PATH
	unset APPIMAGE_OLD_PATH

	if [ "${APPIMAGE_OLD_LD_LIBRARY_PATH}" = "" ]; then
		unset LD_LIBRARY_PATH
	else
		LD_LIBRARY_PATH="${APPIMAGE_OLD_LD_LIBRARY_PATH}"
		export LD_LIBRARY_PATH
	fi
	unset APPIMAGE_OLD_LD_LIBRARY_PATH

	PS1="${APPIMAGE_OLD_PS1}"
	export PS1
	unset APPIMAGE_OLD_PS1
}

APPIMAGE_ENV="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"

APPIMAGE_OLD_PS1="${PS1}"
export APPIMAGE_OLD_PS1

APPIMAGE_OLD_PATH="${PATH}"
export APPIMAGE_OLD_PATH

APPIMAGE_OLD_LD_LIBRARY_PATH="${LD_LIBRARY_PATH}"
export APPIMAGE_OLD_LD_LIBRARY_PATH

PATH="${APPIMAGE_ENV}/usr/bin:${PATH}"
export PATH

if [ "${LD_LIBRARY_PATH}" = "" ]; then
	LD_LIBRARY_PATH="${APPIMAGE_ENV}/usr/lib"
else
	LD_LIBRARY_PATH="${APPIMAGE_ENV}/usr/lib:${LD_LIBRARY_PATH}"
fi
export LD_LIBRARY_PATH

PS1="<${APPIMAGE_NAME}> ${PS1}"
export PS1
07070100000010000081A400000000000000000000000166622DE800000406000000000000000000000000000000000000002900000000pg_top-4.1.1/appimage/pg_top.appdata.xml<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2019 PostgreSQL Global Development Group-->
<component type="desktop-application">
  <id>org.postgresql.pg_top</id>
  <metadata_license>PostgreSQL</metadata_license>
  <project_license>PostgreSQL</project_license>
  <name>pg_top</name>
  <summary>'top' for PostgreSQL</summary>

  <description>
    <p>
	  pg_top is a terminal program used to view top PostgreSQL processes.
    </p>
  </description>

  <launchable type="desktop-id">org.postgresql.pg_top</launchable>

  <screenshots>
   <screenshot type="default">
      <caption>Screenshot</caption>
      <image>https://pg_top.gitlab.io/pg_top-2.png</image>
    </screenshot>
  </screenshots>

  <url type="homepage">https://pg_top.gitlab.io/</url>
  <project_group>PostgreSQL</project_group>

  <provides>
    <binary>pg_top</binary>
  </provides>

  <releases>
    <release version="4.0.0" date="2020-10-8">
      <description>
        <p>4.0.0 release.</p>
      </description>
    </release>
  </releases>
</component>
07070100000011000081A400000000000000000000000166622DE800000070000000000000000000000000000000000000002500000000pg_top-4.1.1/appimage/pg_top.desktop[Desktop Entry]
Name=pg_top
Exec=pg_top
Icon=pg_top
Type=Application
Categories=Utility;Database;
Terminal=true
07070100000012000081A400000000000000000000000166622DE800000B87000000000000000000000000000000000000002100000000pg_top-4.1.1/appimage/pg_top.svg<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   width="512"
   height="512"
   viewBox="0 0 135.46666 135.46666"
   version="1.1"
   id="svg8"
   inkscape:version="0.92.4 5da689c313, 2019-01-14"
   sodipodi:docname="pg_top.svg"
   inkscape:export-filename="pg_top.png"
   inkscape:export-xdpi="96.000008"
   inkscape:export-ydpi="96.000008">
  <defs
     id="defs2" />
  <sodipodi:namedview
     id="base"
     pagecolor="#336791"
     bordercolor="#666666"
     borderopacity="1.0"
     inkscape:pageopacity="1"
     inkscape:pageshadow="2"
     inkscape:zoom="1.6679688"
     inkscape:cx="151.38174"
     inkscape:cy="256"
     inkscape:document-units="mm"
     inkscape:current-layer="layer1"
     showgrid="false"
     units="px"
     inkscape:pagecheckerboard="true"
     inkscape:window-width="2554"
     inkscape:window-height="1028"
     inkscape:window-x="0"
     inkscape:window-y="24"
     inkscape:window-maximized="0" />
  <metadata
     id="metadata5">
    <rdf:RDF>
      <cc:Work
         rdf:about="">
        <dc:format>image/svg+xml</dc:format>
        <dc:type
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
        <dc:title />
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <g
     inkscape:label="Layer 1"
     inkscape:groupmode="layer"
     id="layer1"
     transform="translate(0,-161.53335)">
    <flowRoot
       xml:space="preserve"
       id="flowRoot3713"
       style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       transform="matrix(0.52916751,0,0,0.52916652,-13.638613,165.01507)"><flowRegion
         id="flowRegion3715"><rect
           id="rect3717"
           width="197.2459"
           height="185.25526"
           x="55.15691"
           y="68.946136" /></flowRegion><flowPara
         id="flowPara3719"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:192px;font-family:'Ampad Brush';-inkscape-font-specification:'Ampad Brush';text-align:center;text-anchor:middle;fill:#ffffff">|</flowPara><flowPara
         id="flowPara3721"
         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:192px;font-family:'Ampad Brush';-inkscape-font-specification:'Ampad Brush';text-align:center;text-anchor:middle;fill:#ffffff">V</flowPara></flowRoot>  </g>
</svg>
07070100000013000081A400000000000000000000000166622DE8000000C7000000000000000000000000000000000000001700000000pg_top-4.1.1/boolean.h#ifndef _BOOLEAN_H_
#define _BOOLEAN_H_

/* My favorite names for boolean values */
#define  No 0
#define  Yes	1
#define  Maybe	2				/* tri-state boolean, actually */

#endif							/* _BOOLEAN_H_ */
07070100000014000081A400000000000000000000000166622DE8000009DE000000000000000000000000000000000000001100000000pg_top-4.1.1/c.h/*-------------------------------------------------------------------------
 *
 * c.h
 *	  Fundamental C definitions.  This is included by every .c file in
 *	  PostgreSQL (via either postgres.h or postgres_fe.h, as appropriate).
 *
 *	  Note that the definitions here are not intended to be exposed to clients
 *	  of the frontend interface libraries --- so we don't worry much about
 *	  polluting the namespace with lots of stuff...
 *
 *
 * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * $PostgreSQL: pgsql/src/include/c.h,v 1.214.2.1 2007/01/11 02:40:12 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
/*
 *----------------------------------------------------------------
 *	 TABLE OF CONTENTS
 *
 *		When adding stuff to this file, please try to put stuff
 *		into the relevant section, or add new sections as appropriate.
 *
 *	  section	description
 *	  -------	------------------------------------------------
 *		2)		bool, true, false, TRUE, FALSE, NULL
 *
 * NOTE: since this file is included by both frontend and backend modules, it's
 * almost certainly wrong to put an "extern" declaration here.	typedefs and
 * macros are the kind of thing that might go here.
 *
 *----------------------------------------------------------------
 */
#ifndef C_H
#define C_H

/*
 * We have to include stdlib.h here because it defines many of these macros
 * on some platforms, and we only want our definitions used if stdlib.h doesn't
 * have its own.  The same goes for stddef and stdarg if present.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define _(x) gettext((x))

#ifdef ENABLE_NLS
#include <libintl.h>
#else
#define gettext(x) (x)
#endif

/* ----------------------------------------------------------------
 *				Section 2:	bool, true, false, TRUE, FALSE, NULL
 * ----------------------------------------------------------------
 */

/*
 * bool
 *		Boolean value, either true or false.
 *
 * XXX for C++ compilers, we assume the compiler has a compatible
 * built-in definition of bool.
 */

#ifndef __cplusplus

#ifndef bool
typedef char bool;
#endif

#ifndef true
#define true	((bool) 1)
#endif

#ifndef false
#define false	((bool) 0)
#endif
#endif							/* not C++ */

typedef bool *BoolPtr;

#ifndef TRUE
#define TRUE	1
#endif

#ifndef FALSE
#define FALSE	0
#endif

/* /port compatibility functions */
#include "port.h"

#endif							/* C_H */
07070100000015000081A400000000000000000000000166622DE800001796000000000000000000000000000000000000001500000000pg_top-4.1.1/color.c/*
 *	Top users/processes display for Unix
 *	Version 3
 *
 *	This program may be freely redistributed,
 *	but this entire comment MUST remain intact.
 */

/*
 * This file handles color definitions and access for augmenting
 * the output with ansi color sequences.
 *
 * The definition of a color setting is as follows, separated by
 * colons:
 *
 * tag=minimum,maximum#code
 *
 * "tag" is the name of the value to display with color.
 *
 * "minimum" and "maximum" are positive integer values defining a range:
 * when the value is within this range it will be shown with the
 * specified color.  A missing value indicates that no check should be
 * made (i.e.: ",25" is n <= 25; "25,50" is 25 <= n <= 50; and "50,"
 * is 50 <= n).
 *
 * "code" is the ansi sequence that defines the color to use with the
 * escape sequence "[m".  Semi-colons are allowed in this string to
 * combine attributes.
 */

#include "os.h"
#include "message.h"
#include "display.h"

typedef struct color_entry
{
	char	   *tag;
	int			min;
	int			max;
	char color;
	struct color_entry *next;
	struct color_entry *tagnext;
}			color_entry;

static color_entry * entries = NULL;

static color_entry * *bytag = NULL;
static char **bytag_names = NULL;
static int	totaltags = 0;
static int	tagcnt = 0;

static char **color_ansi = NULL;
static int	num_color_ansi = 0;
static int	max_color_ansi = 0;

#define COLOR_ANSI_SLOTS 20

static int
color_slot(char *str)
{
	int			i;

	for (i = 0; i < num_color_ansi; i++)
	{
		if (strcmp(color_ansi[i], str) == 0)
		{
			return i;
		}
	}

	/* need a new slot */
	if (num_color_ansi >= max_color_ansi)
	{
		max_color_ansi += COLOR_ANSI_SLOTS;
		color_ansi = (char **) realloc(color_ansi, max_color_ansi * sizeof(char *));
	}
	color_ansi[num_color_ansi] = strdup(str);
	return num_color_ansi++;
}

/*
 * int color_env_parse(char *env)
 *
 * Parse a color specification "env" (such as one found in the environment) and
 * add them to the list of entries.  Always returns 0.	Should only be called
 * once.
 */

int
color_env_parse(char *env)
{
	char	   *p;
	char	   *min;
	char	   *max;
	char	   *str;
	int			len;
	color_entry *ce;

	/* initialization */
	color_ansi = (char **) malloc(COLOR_ANSI_SLOTS * sizeof(char *));
	max_color_ansi = COLOR_ANSI_SLOTS;

	/* color slot 0 is always "0" */
	color_slot("0");

	if (env != NULL)
	{
		p = strtok(env, ":");
		while (p != NULL)
		{
			if ((min = strchr(p, '=')) != NULL &&
				(max = strchr(min, ',')) != NULL &&
				(str = strchr(max, '#')) != NULL)
			{
				ce = (color_entry *) malloc(sizeof(color_entry));
				len = min - p;
				ce->tag = (char *) malloc(len + 1);
				strncpy(ce->tag, p, len);
				ce->tag[len] = '\0';
				ce->min = atoi(++min);
				ce->max = atoi(++max);
				ce->color = color_slot(++str);

				ce->next = entries;
				entries = ce;
			}
			else
			{
				if (min != NULL)
				{
					len = min - p;
				}
				else
				{
					len = strlen(p);
				}
				display_error_message(" %.*s: bad color entry", len, p);
			}
			p = strtok(NULL, ":");
		}
	}
	return 0;
}

/*
 * int color_tag(char *tag)
 *
 * Declare "tag" as a color tag.  Return a tag index to use when testing
 * a valuse against the tests for this tag.  Should not be called before
 * color_env_parse.
 */

int
color_tag(char *tag)
{
	color_entry *entryp;
	color_entry *tp;

	if (tag == NULL || *tag == '\0')
	{
		return -1;
	}

	if (bytag == NULL)
	{
		totaltags = 10;
		bytag = (color_entry * *) malloc(totaltags * sizeof(color_entry *));
		bytag_names = (char **) malloc(totaltags * sizeof(char *));
	}

	if (tagcnt >= totaltags)
	{
		totaltags *= 2;
		bytag = (color_entry * *) realloc(bytag, totaltags * sizeof(color_entry *));
		bytag_names = (char **) realloc(bytag_names, totaltags * sizeof(char *));
	}

	entryp = entries;
	tp = NULL;

	while (entryp != NULL)
	{
		if (strcmp(entryp->tag, tag) == 0)
		{
			entryp->tagnext = tp;
			tp = entryp;
		}
		entryp = entryp->next;
	}

	bytag[tagcnt] = tp;
	bytag_names[tagcnt] = strdup(tag);
	return (tagcnt++);
}

/*
 * int color_test(int tagidx, int value)
 *
 * Test "value" against tests for tag "tagidx", a number previously returned
 * by color_tag.  Return the correct color number to use when highlighting.
 * If there is no match, return 0 (color 0).
 */

int
color_test(int tagidx, int value)
{
	color_entry *ce;

	/* sanity check */
	if (tagidx < 0 || tagidx >= tagcnt)
	{
		return 0;
	}

	ce = bytag[tagidx];

	while (ce != NULL)
	{
		if ((!ce->min || ce->min <= value) &&
			(!ce->max || ce->max >= value))
		{
			return ce->color;
		}
		ce = ce->tagnext;
	}

	return 0;
}

/*
 * char *color_set(int color)
 *
 * Return ANSI string to set the terminal for color number "color".
 */

char *
color_set(int color)
{
	static char v[32];

	v[0] = '\0';
	if (color >=0 && color <num_color_ansi)
	{
		snprintf(v, sizeof(v), "\033[%sm", color_ansi[color]);
	}
	return v;
}

void
color_dump(FILE *f)
{
	color_entry *ep;
	int			i;
	int			col;
	int			len;

	fputs("These color tags are available:", f);
	col = 81;
	for (i = 0; i < tagcnt; i++)
	{
		len = strlen(bytag_names[i]) + 1;
		if (len + col > 79)
		{
			fputs("\n  ", f);
			col = 2;
		}
		fprintf(f, " %s", bytag_names[i]);
		col += len;
	}

	fputs("\n\nTop color settings:\n", f);

	for (i = 0; i < tagcnt; i++)
	{
		ep = bytag[i];
		while (ep != NULL)
		{
			fprintf(f, "   %s (%d-", ep->tag, ep->min);
			if (ep->max != 0)
			{
				fprintf(f, "%d", ep->max);
			}
			fprintf(f, "): ansi color %s, %sSample Text",
					color_ansi[(int) ep->color],
					color_set(ep->color));
			fprintf(f, "%s\n", color_set(0));
			ep = ep->tagnext;
		}
	}
}

void
color_debug(FILE *f)
{
	color_entry *ep;
	int			i;

	printf("color debug dump\n");
	ep = entries;
	while (ep != NULL)
	{
		printf("%s(%d,%d): slot %d, ansi %s, %sSample Text",
			   ep->tag, ep->min, ep->max, ep->color, color_ansi[(int) ep->color],
			   color_set(ep->color));
		printf("%s\n", color_set(0));
		ep = ep->next;
	}

	printf("\ntags:");
	for (i = 0; i < tagcnt; i++)
	{
		printf(" %s", bytag_names[i]);
	}
	printf("\n");
}
07070100000016000081A400000000000000000000000166622DE80000034A000000000000000000000000000000000000001500000000pg_top-4.1.1/color.h/*
 * Top - a top users display for Unix
 *
 * Definition of the color interface.
 */

#ifndef _COLOR_H_
#define _COLOR_H_

int			color_env_parse(char *env);
int			color_tag(char *tag);
int			color_test(int tagidx, int value);
char	   *color_set(int color);
void		color_dump(FILE *f);


/*
 * These color tag names are currently in use
 * (or reserved for future use):
 *
 * cpu, size, res, time, 1min, 5min, 15min, host
 */

/*
 * Valid ANSI values for colors are:
 *
 * 0	Reset all attributes
 * 1	Bright
 * 2	Dim
 * 4	Underscore
 * 5	Blink
 * 7	Reverse
 * 8	Hidden
 *
 *	Foreground Colours
 * 30	Black
 * 31	Red
 * 32	Green
 * 33	Yellow
 * 34	Blue
 * 35	Magenta
 * 36	Cyan
 * 37	White
 *
 *	Background Colours
 * 40	Black
 * 41	Red
 * 42	Green
 * 43	Yellow
 * 44	Blue
 * 45	Magenta
 * 46	Cyan
 * 47	White
 */

#endif							/* _COLOR_H_ */
07070100000017000081A400000000000000000000000166622DE800003BED000000000000000000000000000000000000001800000000pg_top-4.1.1/commands.c/*
 *	Top users/processes display for Unix
 *	Version 3
 *
 *	This program may be freely redistributed,
 *	but this entire comment MUST remain intact.
 *
 *	Copyright (c) 1984, 1989, William LeFebvre, Rice University
 *	Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
 *	Copyright (c) 2007-2019, Mark Wong
 */

/*
 *	This file contains the routines that implement some of the interactive
 *	mode commands.	Note that some of the commands are implemented in-line
 *	in "main".	This is necessary because they change the global state of
 *	"top" (i.e.:  changing the number of processes to display).
 */

#include "os.h"
#include <ctype.h>
#include <signal.h>
#include <errno.h>
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
#include <unistd.h>

#include "pg_top.h"
#include "boolean.h"
#include "utils.h"
#include "version.h"
#include "machine.h"
#include "help.h"
#include "display.h"
#include "pg.h"
#include "commands.h"
#include "screen.h"

extern int	errno;

extern char *copyright;

/* imported from screen.c */
extern int	overstrike;

extern int	max_topn;

#define BEGIN "BEGIN;"
#define ROLLBACK "ROLLBACK;"

struct cmd	cmd_map[] = {
	{'\014', cmd_redraw},
	{'#', cmd_number},
	{' ', cmd_update},
	{'?', cmd_help},
	{'A', cmd_explain_analyze},
	{'a', cmd_activity},
	{'c', cmd_cmdline},
#ifdef ENABLE_COLOR
	{'C', cmd_color},
#endif							/* ENABLE_COLOR */
	{'d', cmd_displays},
	{'E', cmd_explain},
	{'h', cmd_help},
	{'i', cmd_idletog},
	{'I', cmd_io},
	{'L', cmd_locks},
	{'n', cmd_number},
	{'o', cmd_order},
	{'q', cmd_quit},
	{'R', cmd_replication},
	{'Q', cmd_current_query},
	{'s', cmd_delay},
	{'u', cmd_user},
	{'\0', NULL},
};

int
cmd_activity(struct pg_top_context *pgtctx)
{
	pgtctx->mode = MODE_PROCESSES;
	pgtctx->header_text =
		pgtctx->header_options[pgtctx->mode_remote][pgtctx->mode];
	reset_display(pgtctx);
	return No;
}

#ifdef ENABLE_COLOR
int
cmd_color(struct pg_top_context *pgtctx)
{
	reset_display(pgtctx);
	if (pgtctx->color_on)
	{
		pgtctx->color_on = 0;
		display_resize();		/* To realloc screenbuf */
		new_message(MT_standout | MT_delayed, " Color off");
	}
	else
	{
		if (!smart_terminal)
		{
			new_message(MT_standout | MT_delayed,
						" Sorry, cannot do colors on this terminal type");
		}
		else
		{
			pgtctx->color_on = 1;
			new_message(MT_standout | MT_delayed, " Color on");
		}
	}
	return No;
}
#endif							/* ENABLE_COLOR */

int
cmd_cmdline(struct pg_top_context *pgtctx)
{
	if (pgtctx->statics.flags.fullcmds)
	{
		pgtctx->ps.fullcmd = (pgtctx->ps.fullcmd + 1) % 3;
		switch (pgtctx->ps.fullcmd)
		{
			case 2:
				new_message(MT_standout | MT_delayed, " Displaying current query.");
				break;
			case 1:
				new_message(MT_standout | MT_delayed,
							" Displaying full command lines.");
				break;
			case 0:
			default:
				new_message(MT_standout | MT_delayed,
							" Not displaying full command lines.");
		}
	}
	else
	{
		new_message(MT_standout, " Full command display not supported.");
		/* no_command = Yes; */
	}
	putchar('\r');
	return No;
}

int
cmd_current_query(struct pg_top_context *pgtctx)
{
	int			newval;
	char		tempbuf1[50];

	new_message(MT_standout, "Current query of process: ");
	newval = readline(tempbuf1, 8, Yes);
	reset_display(pgtctx);
	display_pagerstart();
	show_current_query(&pgtctx->conninfo, newval);
	display_pagerend();
	return No;
}

int
cmd_delay(struct pg_top_context *pgtctx)
{
	int			i;
	char		tempbuf[50];

	new_message(MT_standout, "Seconds to delay: ");
	if ((i = readline(tempbuf, 8, Yes)) > -1)
	{
		if ((pgtctx->delay = i) == 0 && getuid() != 0)
		{
			pgtctx->delay = 1;
		}
	}
	clear_message();
	return No;
}

int
cmd_displays(struct pg_top_context *pgtctx)
{
	int			i;
	char		tempbuf[50];

	new_message(MT_standout, "Displays to show (currently %s): ",
				pgtctx->displays == -1 ? "infinite" : itoa(pgtctx->displays));
	if ((i = readline(tempbuf, 10, Yes)) > 0)
	{
		pgtctx->displays = i;
	}
	else if (i == 0)
	{
		quit(0);
	}
	clear_message();
	return No;
}

int
cmd_explain(struct pg_top_context *pgtctx)
{
	int			newval;
	char		tempbuf1[50];

	new_message(MT_standout, "Re-determine execution plan: ");
	newval = readline(tempbuf1, 8, Yes);
	reset_display(pgtctx);
	display_pagerstart();
	show_explain(&pgtctx->conninfo, newval, EXPLAIN);
	display_pagerend();
	return No;
}

int
cmd_explain_analyze(struct pg_top_context *pgtctx)
{
	int			newval;
	char		tempbuf1[50];

	new_message(MT_standout, "Re-run SQL for analysis: ");
	newval = readline(tempbuf1, 8, Yes);
	reset_display(pgtctx);
	display_pagerstart();
	show_explain(&pgtctx->conninfo, newval, EXPLAIN_ANALYZE);
	display_pagerend();
	return No;
}

int
cmd_help(struct pg_top_context *pgtctx)
{
	reset_display(pgtctx);
	display_pagerstart();
	show_help(&pgtctx->statics);
	display_pagerend();
	return No;
}

int
cmd_idletog(struct pg_top_context *pgtctx)
{
	pgtctx->ps.idle = !pgtctx->ps.idle;
	new_message(MT_standout | MT_delayed, " %sisplaying idle processes.",
				pgtctx->ps.idle ? "D" : "Not d");
	putchar('\r');
	return No;
}

int
cmd_io(struct pg_top_context *pgtctx)
{
	pgtctx->mode = MODE_IO_STATS;
	pgtctx->header_text =
		pgtctx->header_options[pgtctx->mode_remote][pgtctx->mode];
	reset_display(pgtctx);
	return No;
}

int
cmd_locks(struct pg_top_context *pgtctx)
{
	int			newval;
	char		tempbuf1[50];

	new_message(MT_standout, "Show locks held by process: ");
	newval = readline(tempbuf1, 8, Yes);
	reset_display(pgtctx);
	display_pagerstart();
	show_locks(&pgtctx->conninfo, newval);
	display_pagerend();
	return No;
}

int
cmd_number(struct pg_top_context *pgtctx)
{
	int			newval;
	char		tempbuf[50];

	new_message(MT_standout, "Number of processes to show: ");
	newval = readline(tempbuf, 8, Yes);
	if (newval > -1)
	{
		if (newval > max_topn)
		{
			new_message(MT_standout | MT_delayed,
						" This terminal can only display %d processes.",
						max_topn);
			putchar('\r');
		}

		if (newval == 0)
		{
			/* inhibit the header */
			display_header(No);
		}
		else if (newval > pgtctx->topn && pgtctx->topn == 0)
		{
			/* redraw the header */
			display_header(Yes);
			pgtctx->d_header = i_header;
		}
		pgtctx->topn = newval;
	}
	return No;
}

int
cmd_quit(struct pg_top_context *pgtctx)
{
	quit(0);
	/* NOT REACHED */
	return No;
}

int
cmd_replication(struct pg_top_context *pgtctx)
{
	pgtctx->mode = MODE_REPLICATION;
	pgtctx->header_text =
		pgtctx->header_options[pgtctx->mode_remote][pgtctx->mode];
	reset_display(pgtctx);
	return No;
}

int
cmd_order(struct pg_top_context *pgtctx)
{
	int			i;
	int			no_command = No;
	char		tempbuf[50];

	if (pgtctx->statics.order_names == NULL)
	{
		new_message(MT_standout, " Ordering not supported.");
		putchar('\r');
		no_command = Yes;
	}
	else
	{
		new_message(MT_standout, "Order to sort: ");
		if (readline(tempbuf, sizeof(tempbuf), No) > 0)
		{
			i = string_index(tempbuf, pgtctx->statics.order_names);
			if (i == -1)
			{
				new_message(MT_standout, " %s: unrecognized sorting order",
							tempbuf);
				no_command = Yes;
			}
			else
			{
				pgtctx->order_index = i;
			}
			putchar('\r');
		}
		else
		{
			clear_message();
		}
	}
	return no_command;
}

int
cmd_order_cpu(struct pg_top_context *pgtctx)
{
	int			i;

	if ((i = string_index("cpu", pgtctx->statics.order_names)) == -1)
	{
		new_message(MT_standout, " Unrecognized sorting order");
		putchar('\r');
		/* no_command = Yes; */
	}
	else
	{
		pgtctx->order_index = i;
	}
	return No;
}

int
cmd_order_mem(struct pg_top_context *pgtctx)
{
	int			i;

	if ((i = string_index("size", pgtctx->statics.order_names)) == -1)
	{
		new_message(MT_standout, " Unrecognized sorting order");
		putchar('\r');
		return Yes;
	}
	else
	{
		pgtctx->order_index = i;
	}
	return No;
}

int
cmd_redraw(struct pg_top_context *pgtctx)
{
	reset_display(pgtctx);
	return No;
}

int
cmd_update(struct pg_top_context *pgtctx)
{
	/* go home for visual feedback */
	go_home();
	fflush(stdout);
	return No;
}

int
cmd_user(struct pg_top_context *pgtctx)
{
	new_message(MT_standout, "Username to show: ");
	if (readline(pgtctx->ps.usename, sizeof(pgtctx->ps.usename), No) > 0)
	{
		putchar('\r');
	}
	else
	{
		clear_message();
	}
	return No;
}

int
execute_command(struct pg_top_context *pgtctx, char ch)
{
	struct cmd *cmap;

	cmap = cmd_map;

	while (cmap->func != NULL)
	{
		if (cmap->ch == ch)
		{
			return (cmap->func) (pgtctx);
		}
		++cmap;
	}
	return No;
}

/*
 *	str_adderr(str, len, err) - add an explanation of error "err" to
 *	the string "str".
 */

int
str_adderr(char *str, int len, int err)
{
	register char *msg;
	register int msglen;

	msg = err == 0 ? "Not a number" : errmsg(err);
	msglen = strlen(msg) + 2;
	if (len <= msglen)
	{
		return (0);
	}
	(void) strcat(str, ": ");
	(void) strcat(str, msg);
	return (len - msglen);
}

/*
 *	str_addarg(str, len, arg, first) - add the string argument "arg" to
 *	the string "str".  This is the first in the group when "first"
 *	is set (indicating that a comma should NOT be added to the front).
 */

int
str_addarg(char *str, int len, char *arg, int first)
{
	register int arglen;

	arglen = strlen(arg);
	if (!first)
	{
		arglen += 2;
	}
	if (len <= arglen)
	{
		return (0);
	}
	if (!first)
	{
		(void) strcat(str, ", ");
	}
	(void) strcat(str, arg);
	return (len - arglen);
}

/*
 *	show_help() - display the help screen; invoked in response to
 *		either 'h' or '?'.
 */

void
show_help(struct statics *stp)
{
	static char *fullhelp;
	char	   *p = NULL;

	if (fullhelp == NULL)
	{
		/* set it up first time thru */
		if (stp->order_names != NULL)
		{
			p = string_list(stp->order_names);
		}
		if (p == NULL)
		{
			p = "not supported";
		}
		fullhelp = (char *) malloc(strlen(help_text) + strlen(p) + 2);
		sprintf(fullhelp, help_text, p);
	}

	display_pager("pg_top version ");
	display_pager(version_string());
	display_pager(", ");
	display_pager(copyright);
	display_pager("\n");
	display_pager(fullhelp);
}

/*
 *	Utility routines that help with some of the commands.
 */

char *
next_field(char *str)
{
	if ((str = strchr(str, ' ')) == NULL)
	{
		return (NULL);
	}
	*str = '\0';
	while (*++str == ' ') /* loop */ ;

	/* if there is nothing left of the string, return NULL */
	/* This fix is dedicated to Greg Earle */
	return (*str == '\0' ? NULL : str);
}

int
scanint(char *str, int *intp)
{
	register int val = 0;
	register char ch;

	/* if there is nothing left of the string, flag it as an error */
	/* This fix is dedicated to Greg Earle */
	if (*str == '\0')
	{
		return (-1);
	}

	while ((ch = *str++) != '\0')
	{
		if (isdigit(ch))
		{
			val = val * 10 + (ch - '0');
		}
		else if (isspace(ch))
		{
			break;
		}
		else
		{
			return (-1);
		}
	}
	*intp = val;
	return (0);
}

void
show_current_query(struct pg_conninfo_ctx *conninfo, int procpid)
{
	int			i;
	int			rows;
	char		info[64];
	PGresult   *pgresult = NULL;

	sprintf(info, "Current query for procpid %d:\n\n", procpid);
	display_pager(info);

	/* Get the currently running query. */
	connect_to_db(conninfo);
	if (conninfo->connection != NULL)
	{
		pgresult = pg_query(conninfo->connection, procpid);
		rows = PQntuples(pgresult);
	}
	else
	{
		rows = 0;
	}
	for (i = 0; i < rows; i++)
	{
		display_pager(PQgetvalue(pgresult, i, 0));
	}
	display_pager("\n\n");

	if (pgresult != NULL)
		PQclear(pgresult);
	disconnect_from_db(conninfo);
}

void
show_explain(struct pg_conninfo_ctx *conninfo, int procpid, int analyze)
{
	int			i,
				j;
	int			rows,
				r;
	char		sql[4096];
	char		info[1024];
	PGresult   *pgresult_query = NULL;
	PGresult   *pgresult_explain = NULL;

	sprintf(info,
			"Current query plan for procpid %d:\n\n Statement:\n\n",
			procpid);
	display_pager(info);

	/* Get the currently running query. */
	connect_to_db(conninfo);
	if (conninfo->connection != NULL)
	{
		pgresult_query = pg_query(conninfo->connection, procpid);
		rows = PQntuples(pgresult_query);
	}
	else
	{
		rows = 0;
	}
	for (i = 0; i < rows; i++)
	{
		/* Display the query before the query plan. */
		display_pager(PQgetvalue(pgresult_query, i, 0));

		/* Execute the EXPLAIN. */
		if (analyze == EXPLAIN_ANALYZE)
		{
			sprintf(sql, "EXPLAIN (ANALYZE, VERBOSE, BUFFERS)\n%s",
					PQgetvalue(pgresult_query, i, 0));
		}
		else
		{
			sprintf(sql, "EXPLAIN\n%s", PQgetvalue(pgresult_query, i, 0));
		}
		PQexec(conninfo->connection, BEGIN);
		pgresult_explain = PQexec(conninfo->connection, sql);
		PQexec(conninfo->connection, ROLLBACK);
		r = PQntuples(pgresult_explain);
		/* This will display an error if the EXPLAIN fails. */
		display_pager("\n\nQuery Plan:\n\n");
		display_pager(PQresultErrorMessage(pgresult_explain));
		for (j = 0; j < r; j++)
		{
			display_pager(PQgetvalue(pgresult_explain, j, 0));
			display_pager("\n");
		}
		if (pgresult_explain != NULL)
			PQclear(pgresult_explain);
	}
	display_pager("\n\n");

	if (pgresult_query != NULL)
		PQclear(pgresult_query);
	disconnect_from_db(conninfo);
}

void
show_locks(struct pg_conninfo_ctx *conninfo, int procpid)
{
	int			i,
				j,
				k;
	int			rows;
	char		info[64];
	int			width[7] = {1, 8, 6, 5, 5, 4, 7};
	PGresult   *pgresult = NULL;
	char		header_format[1024];
	char		line_format[1024];
	char		prefix[21];		/* Should hold any 64 bit integer. */
	char		line[1024];

	sprintf(info, "Locks held by procpid %d:\n\n", procpid);
	display_pager(info);

	/* Get the locks helf by the process. */
	connect_to_db(conninfo);
	if (conninfo->connection == NULL)
	{
		disconnect_from_db(conninfo);
		return;
	}

	pgresult = pg_locks(conninfo->connection, procpid);
	rows = PQntuples(pgresult);

	/* Determine column sizes. */
	sprintf(prefix, "%d", rows);
	width[0] = strlen(prefix);
	for (i = 0; i < rows; i++)
	{
		if (strlen(PQgetvalue(pgresult, i, 0)) > width[1])
			width[1] = strlen(PQgetvalue(pgresult, i, 0));
		if (strlen(PQgetvalue(pgresult, i, 1)) > width[2])
			width[2] = strlen(PQgetvalue(pgresult, i, 1));
		if (strlen(PQgetvalue(pgresult, i, 2)) > width[3])
			width[3] = strlen(PQgetvalue(pgresult, i, 2));
		if (strlen(PQgetvalue(pgresult, i, 3)) > width[4])
			width[4] = strlen(PQgetvalue(pgresult, i, 3));
		if (strlen(PQgetvalue(pgresult, i, 4)) > width[5])
			width[5] = strlen(PQgetvalue(pgresult, i, 4));
		if (strlen(PQgetvalue(pgresult, i, 5)) > width[6])
			width[6] = strlen(PQgetvalue(pgresult, i, 5));
	}
	sprintf(header_format,
			"%%-%ds | %%-%ds | %%-%ds | %%-%ds | %%-%ds | %%-%ds | %%-%ds\n",
			width[0], width[1], width[2], width[3], width[4], width[5],
			width[6]);
	sprintf(line_format,
			"%%%dd | %%-%ds | %%-%ds | %%-%ds | %%-%ds | %%-%ds | %%-%ds\n",
			width[0], width[1], width[2], width[3], width[4], width[5],
			width[6]);

	/* Display the header. */
	sprintf(line, header_format, "", "database", "schema", "table", "index",
			"type", "granted");
	display_pager(line);
	for (i = 0, k = 0; i < 7; i++)
	{
		for (j = 0; j < width[i]; j++, k++)
		{
			line[k] = '-';
		}
		line[k++] = '-';
		line[k++] = '+';
		line[k++] = '-';
	}
	line[k - 3] = '\n';
	line[k - 2] = '\0';
	display_pager(line);

	/* Display data. */
	for (i = 0; i < rows; i++)
	{
		sprintf(line, line_format, i + 1, PQgetvalue(pgresult, i, 0),
				PQgetvalue(pgresult, i, 1), PQgetvalue(pgresult, i, 2),
				PQgetvalue(pgresult, i, 3), PQgetvalue(pgresult, i, 4),
				PQgetvalue(pgresult, i, 5));
		display_pager(line);
	}
	display_pager("\n");

	PQclear(pgresult);
	disconnect_from_db(conninfo);
}
07070100000018000081A400000000000000000000000166622DE80000061B000000000000000000000000000000000000001800000000pg_top-4.1.1/commands.h/*
 * call specifications for commands.c
 *
 *	Copyright (c) 2007-2019, Mark Wong
 */

#ifndef _COMMANDS_H_
#define _COMMANDS_H_

#include "pg_top.h"

struct cmd
{
	int			ch;
	int			(*func) (struct pg_top_context *);
};

#define EXPLAIN 0
#define EXPLAIN_ANALYZE 1

int			cmd_activity(struct pg_top_context *);
#ifdef ENABLE_COLOR
int			cmd_color(struct pg_top_context *);
#endif							/* ENABLE_COLOR */
int			cmd_cmdline(struct pg_top_context *);
int			cmd_current_query(struct pg_top_context *);
int			cmd_delay(struct pg_top_context *);
int			cmd_displays(struct pg_top_context *);
int			cmd_explain(struct pg_top_context *);
int			cmd_explain_analyze(struct pg_top_context *);
int			cmd_help(struct pg_top_context *);
int			cmd_idletog(struct pg_top_context *);
int			cmd_indexes(struct pg_top_context *);
int			cmd_io(struct pg_top_context *);
int			cmd_locks(struct pg_top_context *);
int			cmd_number(struct pg_top_context *);
int			cmd_quit(struct pg_top_context *);
int			cmd_replication(struct pg_top_context *);
int			cmd_order(struct pg_top_context *);
int			cmd_redraw(struct pg_top_context *);
int			cmd_statements(struct pg_top_context *);
int			cmd_update(struct pg_top_context *);
int			cmd_user(struct pg_top_context *);

int			execute_command(struct pg_top_context *, char);

void		show_help(struct statics *);
int			scanint(char *str, int *intp);
void		show_current_query(struct pg_conninfo_ctx *, int);
void		show_explain(struct pg_conninfo_ctx *, int, int);
void		show_locks(struct pg_conninfo_ctx *, int);

#endif							/* _COMMANDS_H_ */
07070100000019000081A400000000000000000000000166622DE8000002D0000000000000000000000000000000000000001900000000pg_top-4.1.1/config.h.in#ifndef _CONFIG_H_
#define _CONFIG_H_
#cmakedefine ENABLE_COLOR 1
#cmakedefine HAVE_GETOPT 1
#cmakedefine HAVE_MEMCPY 1
#cmakedefine HAVE_SETPRIORITY 1
#cmakedefine HAVE_SIGACTION 1
#cmakedefine HAVE_SIGHOLD 1
#cmakedefine HAVE_SIGPROCMASK 1
#cmakedefine HAVE_SIGRELSE 1
#cmakedefine HAVE_SNPRINTF 1
#cmakedefine HAVE_STRCHR 1
#cmakedefine HAVE_STRERROR 1
#cmakedefine HAVE_STRING_H 1
#cmakedefine HAVE_STRINGS_H 1
#cmakedefine HAVE_SYS_TIME_H 1
#cmakedefine HAVE_SYS_RESOURCE_H 1
#cmakedefine HAVE_UNISTD_H 1
#cmakedefine OSMAJOR @OSMAJOR@
#cmakedefine OSREV @OSREV@
#cmakedefine STDC_HEADERS 1
#cmakedefine TIME_WITH_SYS_TIME 1
#cmakedefine RETSIGTYPE @RETSIGTYPE@
#cmakedefine time_t @time_t@
#endif /* _CONFIG_H_ */
0707010000001A000081A400000000000000000000000166622DE800007CBE000000000000000000000000000000000000001700000000pg_top-4.1.1/display.c/*
 *	Top users/processes display for Unix
 *	Version 3
 *
 *	This program may be freely redistributed,
 *	but this entire comment MUST remain intact.
 *
 *	Copyright (c) 1984, 1989, William LeFebvre, Rice University
 *	Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
 */

/*
 *	This file contains the routines that display information on the screen.
 *	Each section of the screen has two routines:  one for initially writing
 *	all constant and dynamic text, and one for only updating the text that
 *	changes.  The prefix "i_" is used on all the "initial" routines and the
 *	prefix "u_" is used for all the "updating" routines.
 *
 *	ASSUMPTIONS:
 *		  None of the "i_" routines use any of the termcap capabilities.
 *		  In this way, those routines can be safely used on terminals that
 *		  have minimal (or nonexistant) terminal capabilities.
 *
 *		  The routines are called in this order:  *_loadave, i_timeofday,
 *		  *_procstates, *_cpustates, *_memory, *_message, *_header,
 *		  *_process, u_endscreen.
 */

#include "os.h"
#include <ctype.h>
#include <stdarg.h>
#include <unistd.h>

#include "pg_top.h"
#include "machine.h"
#include "screen.h"				/* interface to screen package */
#include "layout.h"				/* defines for screen position layout */
#include "display.h"
#include "boolean.h"
#include "utils.h"

#ifdef ENABLE_COLOR
#include "color.h"
#endif

#define CURSOR_COST 8

/* imported from screen.c */
extern int	overstrike;

static int	lmpid = -1;
static int	display_width = MAX_COLS;

/* cursor positions of key points on the screen are maintained here */
/* layout.h has static definitions, but we may change our minds on some
   of the positions as we make decisions about what needs to be displayed */

static int	x_lastpid = X_LASTPID;
static int	y_lastpid = Y_LASTPID;
static int	x_loadave = X_LOADAVE;
static int	y_loadave = Y_LOADAVE;
static int	x_minibar = X_MINIBAR;
static int	y_minibar = Y_MINIBAR;
static int	x_uptime = X_UPTIME;
static int	y_uptime = Y_UPTIME;
static int	x_procstate = X_PROCSTATE;
static int	y_procstate = Y_PROCSTATE;
static int	x_cpustates = X_CPUSTATES;
static int	y_cpustates = Y_CPUSTATES;
static int	x_mem = X_MEM;
static int	y_mem = Y_MEM;
static int	x_swap = -1;
static int	y_swap = -1;
static int	y_message = Y_MESSAGE;
static int	x_db = X_DB;
static int	y_db = Y_DB;
static int	x_io = X_IO;
static int	y_io = Y_IO;
static int	x_header = X_HEADER;
static int	y_header = Y_HEADER;
static int	x_idlecursor = X_IDLECURSOR;
static int	y_idlecursor = Y_IDLECURSOR;
static int	y_procs = Y_PROCS;

/* buffer and colormask that describes the content of the screen */
/* these are singly dimensioned arrays -- the row boundaries are
   determined on the fly.
*/
static char *screenbuf = NULL;
static char *colorbuf = NULL;
static char scratchbuf[MAX_COLS];
static int	bufsize = 0;

/* lineindex tells us where the beginning of a line is in the buffer */
#define lineindex(l) ((l)*MAX_COLS)

/* screen's cursor */
static int	curr_x,
			curr_y;
static int	curr_color;

/* virtual cursor */
static int	virt_x,
			virt_y;

static char **procstate_names;
static char **cpustate_names;
static char **memory_names;
static char **swap_names;

static int	num_procstates;
static int	num_cpustates;
static int	num_memory;
static int	num_swap;

static int *lprocstates;
static int *lcpustates;

static int *cpustate_columns;
static int	cpustate_total_length;

static enum
{
	OFF, ON, ERASE
}			header_status = ON;

#ifdef ENABLE_COLOR
static int	load_cidx[3];
static int	header_cidx;
static int *cpustate_cidx;
static int *memory_cidx;
static int *swap_cidx;
#endif
static int	header_color = 0;

/* internal support routines */

/*
 * static int string_count(char **pp)
 *
 * Pointer "pp" points to an array of string pointers, which is
 * terminated by a NULL.  Return the number of string pointers in
 * this array.
 */

static int
string_count(char **pp)
{
	register int cnt = 0;

	if (pp != NULL)
	{
		while (*pp++ != NULL)
		{
			cnt++;
		}
	}
	return (cnt);
}

void
display_clear()
{
#ifdef DEBUG
	dprintf("display_clear\n");
#endif							/* DEBUG */
	clear();
	memzero(screenbuf, bufsize);
	memzero(colorbuf, bufsize);
	curr_x = curr_y = 0;
}

/*
 * void display_move(int x, int y)
 *
 * Efficiently move the cursor to x, y.  This assumes the cursor is
 * currently located at curr_x, curr_y, and will only use cursor
 * addressing when it is less expensive than overstriking what's
 * already on the screen.
 */

void
display_move(int x, int y)
{
	char		buff[128];
	char	   *p;
	char	   *bufp;
	char	   *colorp;
	int			cnt = 0;
	int color = curr_color;

#ifdef DEBUG
	dprintf("display_move(%d, %d): curr_x %d, curr_y %d\n", x, y, curr_x, curr_y);
#endif							/* DEBUG */

	/* are we in a position to do this without cursor addressing? */
	if (curr_y < y || (curr_y == y && curr_x <= x))
	{
		/*
		 * start buffering up what it would take to move there by rewriting
		 * what's on the screen
		 */
		cnt = CURSOR_COST;
		p = buff;

		/* one newline for every line */
		while (cnt > 0 && curr_y < y)
		{
			if (color !=0)
			{
				p = strecpy(p, color_set(0));
				color = 0;

				cnt -= 5;
			}
			*p++ = '\n';
			curr_y++;
			curr_x = 0;
			cnt--;
		}

		/* write whats in the screenbuf */
		bufp = &screenbuf[lineindex(curr_y) + curr_x];
		colorp = &colorbuf[lineindex(curr_y) + curr_x];
		while (cnt > 0 && curr_x < x)
		{
			if (color !=*colorp)
			{
				color = *colorp;

				p = strecpy(p, color_set(color));
				cnt -= 5;
			}
			if ((*p = *bufp) == '\0')
			{
				/* somwhere on screen we haven't been before */
				*p = *bufp = ' ';
			}
			p++;
			bufp++;
			colorp++;
			curr_x++;
			cnt--;
		}
	}

	/* move the cursor */
	if (cnt > 0)
	{
		/* screen rewrite is cheaper */
		*p = '\0';
		fputs(buff, stdout);
		curr_color = color;
	}
	else
	{
		Move_to(x, y);
	}

	/* update our position */
	curr_x = x;
	curr_y = y;
}

/*
 * display_write(int x, int y, int newcolor, int eol, char *new)
 *
 * Optimized write to the display.	This writes characters to the
 * screen in a way that optimizes the number of characters actually
 * sent, by comparing what is being written to what is already on
 * the screen (according to screenbuf and colorbuf).  The string to
 * write is "new", the first character of "new" should appear at
 * screen position x, y.  If x is -1 then "new" begins wherever the
 * cursor is currently positioned.	The string is written with color
 * "newcolor".	If "eol" is true then the remainder of the line is
 * cleared.  It is expected that "new" will have no newlines and no
 * escape sequences.
 */

void
display_write(int x, int y, int newcolor, int eol, char *new)
{
	char	   *bufp;
	char	   *colorp;
	int			ch;
	int			diff;

#ifdef DEBUG
	dprintf("display_write(%d, %d, %d, %d, \"%s\")\n",
			x, y, newcolor, eol, new);
#endif							/* DEBUG */

	/* dumb terminal handling here */
	if (!smart_terminal)
	{
		if (x != -1)
		{
			/* make sure we are on the right line */
			while (curr_y < y)
			{
				putchar('\n');
				curr_y++;
				curr_x = 0;
			}

			/* make sure we are on the right column */
			while (curr_x < x)
			{
				putchar(' ');
				curr_x++;
			}
		}

		/* write */
		if (new != NULL)
		{
			fputs(new, stdout);
			curr_x += strlen(new);
		}

		return;
	}

	/* adjust for "here" */
	if (x == -1)
	{
		x = virt_x;
		y = virt_y;
	}
	else
	{
		virt_x = x;
		virt_y = y;
	}

	/* a pointer to where we start */
	bufp = &screenbuf[lineindex(y) + x];
	colorp = &colorbuf[lineindex(y) + x];

	/* main loop */
	while (new != NULL && (ch = *new++) != '\0')
	{
		/* if either character or color are different, an update is needed */
		/* but only when the screen is wide enough */
		if (x < display_width && (ch != *bufp || newcolor != *colorp))
		{
			/* check cursor */
			if (y != curr_y || x != curr_x)
			{
				/* have to move the cursor */
				display_move(x, y);
			}

			/* write character */
			if (curr_color != newcolor)
			{
				fputs(color_set(newcolor), stdout);
				curr_color = newcolor;
			}
			putchar(ch);
			*bufp = ch;
			*colorp = curr_color;
			curr_x++;
		}

		/* move */
		x++;
		virt_x++;
		bufp++;
		colorp++;
	}

	/* eol handling */
	if (eol && *bufp != '\0')
	{
#ifdef DEBUG
		dprintf("display_write: clear-eol (bufp = \"%s\")\n", bufp);
#endif							/* DEBUG */
		/* make sure we are color 0 */
		if (curr_color != 0)
		{
			fputs(color_set(0), stdout);
			curr_color = 0;
		}

		/* make sure we are at the end */
		if (x != curr_x || y != curr_y)
		{
			Move_to(x, y);
			curr_x = x;
			curr_y = y;
		}

		/* clear to end */
		clear_eol(strlen(bufp));

		/* clear out whats left of this line's buffer */
		diff = display_width - x;
		if (diff > 0)
		{
			memzero(bufp, diff);
			memzero(colorp, diff);
		}
	}
}

void
display_fmt(int x, int y, int newcolor, int eol, char *fmt,...)
{
	va_list		argp;

	va_start(argp, fmt);

	vsnprintf(scratchbuf, MAX_COLS, fmt, argp);
	display_write(x, y, newcolor, eol, scratchbuf);
}

void
display_cte()
{
	int			len;
	int			y;
	char	   *p;
	int			need_clear = 0;

	/* is there anything out there that needs to be cleared? */
	p = &screenbuf[lineindex(virt_y) + virt_x];
	if (*p != '\0')
	{
		need_clear = 1;
	}
	else
	{
		/* this line is clear, what about the rest? */
		y = virt_y;
		while (++y < screen_length)
		{
			if (screenbuf[lineindex(y)] != '\0')
			{
				need_clear = 1;
				break;
			}
		}
	}

	if (need_clear)
	{
#ifdef DEBUG
		dprintf("display_cte: clearing\n");
#endif							/* DEBUG */

		/* different method when there's no clear_to_end */
		if (clear_to_end)
		{
			display_move(virt_x, virt_y);
			putcap(clear_to_end);
		}
		else
		{
			if (++virt_y < screen_length)
			{
				display_move(0, virt_y);
				virt_x = 0;
				while (virt_y < screen_length)
				{
					p = &screenbuf[lineindex(virt_y)];
					len = strlen(p);
					if (len > 0)
					{
						clear_eol(len);
					}
					virt_y++;
				}
			}
		}

		/* clear the screenbuf */
		len = lineindex(virt_y) + virt_x;
		memzero(&screenbuf[len], bufsize - len);
		memzero(&colorbuf[len], bufsize - len);
	}
}

static void
summary_format(int x, int y, int *numbers, char **names)
{
	register int num;
	register char *thisname;
	register char *lastname = NULL;

	/* format each number followed by its string */
	while ((thisname = *names++) != NULL)
	{
		/* get the number to format */
		num = *numbers++;

		/* display only non-zero numbers */
		if (num != 0)
		{
			/* write the previous name */
			if (lastname != NULL)
			{
				display_write(-1, -1, 0, 0, lastname);
			}

			/* write this number if positive */
			if (num > 0)
			{
				display_write(x, y, 0, 0, itoa(num));
			}

			/* defer writing this name */
			lastname = thisname;

			/* next iteration will not start at x, y */
			x = y = -1;
		}
	}

	/*
	 * if the last string has a separator on the end, it has to be written
	 * with care
	 */
	if (lastname != NULL && (num = strlen(lastname)) > 1 &&
		lastname[num - 2] == ',' && lastname[num - 1] == ' ')
	{
		display_fmt(-1, -1, 0, 1, "%.*s", num - 2, lastname);
	}
	else
	{
		display_write(-1, -1, 0, 1, lastname);
	}
}

static void
summary_format_memory(int x, int y, long *numbers, char **names, int *cidx)
{
	register long num;
	register int color;
	register char *thisname;
	register char *lastname = NULL;

	/* format each number followed by its string */
	while ((thisname = *names++) != NULL)
	{
		/* get the number to format */
		num = *numbers++;
		color = 0;

		/* display non-negative numbers */
		if (num >= 0)
		{
			/* write the previous name */
			if (lastname != NULL)
			{
				display_write(-1, -1, 0, 0, lastname);
			}

			/* defer writing this name */
			lastname = thisname;

#ifdef ENABLE_COLOR
			/* choose a color */
			color = color_test(*cidx++, num);
#endif

			/* is this number in kilobytes? */
			if (thisname[0] == 'K')
			{
				display_write(x, y, color, 0, format_k(num));
				lastname++;
			}
			else
			{
				display_write(x, y, color, 0, itoa((int) num));
			}

			/* next iteration will not start at x, y */
			x = y = -1;
		}
	}

	/*
	 * if the last string has a separator on the end, it has to be written
	 * with care
	 */
	if (lastname == NULL)
	{
		/*
		 * Don't show anything if the data doesn't exist.
		 */
		return;
	}
	else if ((num = strlen(lastname)) > 1 &&
			 lastname[num - 2] == ',' && lastname[num - 1] == ' ')
	{
		display_fmt(-1, -1, 0, 1, "%.*s", num - 2, lastname);
	}
	else
	{
		display_write(-1, -1, 0, 1, lastname);
	}
}

/*
 * int display_resize()
 *
 * Reallocate buffer space needed by the display package to accomodate
 * a new screen size.  Must be called whenever the screen's size has
 * changed.  Returns the number of lines available for displaying
 * processes or -1 if there was a problem allocating space.
 */

int
display_resize()
{
	register int lines;
	register int newsize;

	/* calculate the current dimensions */
	/* if operating in "dumb" mode, we only need one line */
	lines = smart_terminal ? screen_length : 1;

	/*
	 * we don't want more than MAX_COLS columns, since the machine-dependent
	 * modules make static allocations based on MAX_COLS and we don't want to
	 * run off the end of their buffers
	 */
	display_width = screen_width;
	if (display_width >= MAX_COLS)
	{
		display_width = MAX_COLS - 1;
	}

	/* see how much space we need */
	newsize = lines * (MAX_COLS + 1);

	/* reallocate only if we need more than we already have */
	if (newsize > bufsize)
	{
		/* deallocate any previous buffer that may have been there */
		if (screenbuf != NULL)
		{
			free(screenbuf);
		}
		if (colorbuf != NULL)
		{
			free(colorbuf);
		}

		/* allocate space for the screen and color buffers */
		bufsize = newsize;
		screenbuf = (char *) calloc(bufsize, sizeof(char));
		colorbuf = (char *) calloc(bufsize, sizeof(char));
		if (screenbuf == NULL || colorbuf == NULL)
		{
			/* oops! */
			return (-1);
		}
	}
	else
	{
		/* just clear them out */
		memzero(screenbuf, bufsize);
		memzero(colorbuf, bufsize);
	}

	/* adjust total lines on screen to lines available for procs */
	lines -= y_procs;

	/* return number of lines available */
	/* for dumb terminals, pretend like we can show any amount */
	return (smart_terminal ? lines : Largest);
}

/*
 * int display_init(struct statics *statics)
 *
 * Initialize the display system based on information in the statics
 * structure.  Returns the number of lines available for displaying
 * processes or -1 if there was an error.
 */

int
display_init(struct statics *statics)
{
	register int lines;
	register char **pp;
	register char *p;
	register int *ip;
	register int i;

	/*
	 * certain things may influence the screen layout, so look at those first
	 */
	/* a swap line shifts parts of the display down one */
	swap_names = statics->swap_names;
	if ((num_swap = string_count(swap_names)) > 0)
	{
		/* adjust screen placements */
		y_message++;
		y_header++;
		y_idlecursor++;
		y_procs++;
		x_swap = X_SWAP;
		y_swap = Y_SWAP;
	}

	/* call resize to do the dirty work */
	lines = display_resize();

	/* only do the rest if we need to */
	if (lines > -1)
	{
		/* save pointers and allocate space for names */
		procstate_names = statics->procstate_names;
		num_procstates = string_count(procstate_names);
		lprocstates = (int *) malloc(num_procstates * sizeof(int));

		cpustate_names = statics->cpustate_names;
		num_cpustates = string_count(cpustate_names);
		lcpustates = (int *) malloc(num_cpustates * sizeof(int));
		cpustate_columns = (int *) malloc(num_cpustates * sizeof(int));
		memory_names = statics->memory_names;
		num_memory = string_count(memory_names);

		/* calculate starting columns where needed */
		cpustate_total_length = 0;
		pp = cpustate_names;
		ip = cpustate_columns;
		while (*pp != NULL)
		{
			*ip++ = cpustate_total_length;
			if ((i = strlen(*pp++)) > 0)
			{
				cpustate_total_length += i + 8;
			}
		}
	}

#ifdef ENABLE_COLOR
	/* set up color tags for loadavg */
	load_cidx[0] = color_tag("1min");
	load_cidx[1] = color_tag("5min");
	load_cidx[2] = color_tag("15min");

	/* find header color */
	header_cidx = color_tag("header");
	header_color = color_test(header_cidx, 0);

	/* color tags for cpu states */
	cpustate_cidx = (int *) malloc(num_cpustates * sizeof(int));
	i = 0;
	p = strecpy(scratchbuf, "cpu.");
	while (i < num_cpustates)
	{
		strcpy(p, cpustate_names[i]);
		cpustate_cidx[i++] = color_tag(scratchbuf);
	}

	/* color tags for memory */
	memory_cidx = (int *) malloc(num_memory * sizeof(int));
	i = 0;
	p = strecpy(scratchbuf, "memory.");
	while (i < num_memory)
	{
		strcpy(p, homogenize(memory_names[i] + 1));
		memory_cidx[i++] = color_tag(scratchbuf);
	}

	/* color tags for swap */
	swap_cidx = (int *) malloc(num_swap * sizeof(int));
	i = 0;
	p = strecpy(scratchbuf, "swap.");
	while (i < num_swap)
	{
		strcpy(p, homogenize(swap_names[i] + 1));
		swap_cidx[i++] = color_tag(scratchbuf);
	}
#endif

	/* return number of lines available (or error) */
	return (lines);
}

static void
pr_loadavg(double avg, int i)
{
	int color = 0;

#ifdef ENABLE_COLOR
	color = color_test(load_cidx[i], (int) (avg * 100));
#endif
	display_fmt(x_loadave + X_LOADAVEWIDTH * i, y_loadave, color, 0,
				avg < 10.0 ? " %5.2f" : " %5.1f", avg);
	display_write(-1, -1, 0, 0, (i < 2 ? "," : ";"));
}

void
i_loadave(int mpid, double *avenrun)
{
	register int i;

	/* i_loadave also clears the screen, since it is first */
	display_clear();

	/* mpid == -1 implies this system doesn't have an _mpid */
	if (mpid != -1)
	{
		display_fmt(0, 0, 0, 0,
					"last pid: %5d;  load avg:", mpid);
		x_loadave = X_LOADAVE;
	}
	else
	{
		display_write(0, 0, 0, 0, "load averages:");
		x_loadave = X_LOADAVE - X_LASTPIDWIDTH;
	}
	for (i = 0; i < 3; i++)
	{
		pr_loadavg(avenrun[i], i);
	}

	lmpid = mpid;
}

void
u_loadave(int mpid, double *avenrun)
{
	register int i;

	if (mpid != -1)
	{
		/* change screen only when value has really changed */
		if (mpid != lmpid)
		{
			display_fmt(x_lastpid, y_lastpid, 0, 0,
						"%5d", mpid);
			lmpid = mpid;
		}
	}

	/* display new load averages */
	for (i = 0; i < 3; i++)
	{
		pr_loadavg(avenrun[i], i);
	}
}

static char minibar_buffer[64];

#define MINIBAR_WIDTH 20

void
i_minibar(int (*formatter) (char *, int))
{
	(void) ((*formatter) (minibar_buffer, MINIBAR_WIDTH));

	display_write(x_minibar, y_minibar, 0, 0, minibar_buffer);
}

void
u_minibar(int (*formatter) (char *, int))
{
	(void) ((*formatter) (minibar_buffer, MINIBAR_WIDTH));

	display_write(x_minibar, y_minibar, 0, 0, minibar_buffer);
}

static int	uptime_days;
static int	uptime_hours;
static int	uptime_mins;
static int	uptime_secs;

void
i_uptime(time_t *bt, time_t *tod)
{
	time_t		uptime;

	if (*bt != -1)
	{
		uptime = *tod - *bt;
		uptime += 30;
		uptime_days = uptime / 86400;
		uptime %= 86400;
		uptime_hours = uptime / 3600;
		uptime %= 3600;
		uptime_mins = uptime / 60;
		uptime_secs = uptime % 60;

		/*
		 * Display the uptime.
		 */

		display_fmt(x_uptime, y_uptime, 0, 0,
					"  up %d+%02d:%02d:%02d",
					uptime_days, uptime_hours, uptime_mins, uptime_secs);
	}
}

void
u_uptime(time_t *bt, time_t *tod)
{
	i_uptime(bt, tod);
}

void
i_timeofday(time_t *tod)
{
	/*
	 * Display the current time. "ctime" always returns a string that looks
	 * like this:
	 *
	 * Sun Sep 16 01:03:52 1973 012345678901234567890123 1		   2
	 *
	 * We want indices 11 thru 18 (length 8).
	 */

	display_fmt((smart_terminal ? screen_width : 79) - 8, 0, 0, 1,
				"%-8.8s", &(ctime(tod)[11]));
}

static int	ltotal = 0;

/*
 *	*_procstates(total, brkdn, names) - print the process summary line
 */

void
i_procstates(int total, int *brkdn)
{
	/* write current number of processes and remember the value */
	display_fmt(0, y_procstate, 0, 0,
				"%d processes: ", total);
	ltotal = total;

	/* remember where the summary starts */
	x_procstate = virt_x;

	/* format and print the process state summary */
	summary_format(-1, -1, brkdn, procstate_names);

	/* save the numbers for next time */
	memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
}

void
u_procstates(int total, int *brkdn)
{
	/* update number of processes only if it has changed */
	if (ltotal != total)
	{
		display_fmt(0, y_procstate, 0, 0,
					"%d", total);

		/* if number of digits differs, rewrite the label */
		if (digits(total) != digits(ltotal))
		{
			display_write(-1, -1, 0, 0, " processes: ");
			x_procstate = virt_x;
		}

		/* save new total */
		ltotal = total;
	}

	/* see if any of the state numbers has changed */
	if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0)
	{
		/* format and update the line */
		summary_format(x_procstate, y_procstate, brkdn, procstate_names);
		memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
	}
}

/*
 *	*_cpustates(states, names) - print the cpu state percentages
 */

/* cpustates_tag() calculates the correct tag to use to label the line */

char *
cpustates_tag()
{
	register char *use;

	static char *short_tag = "CPU: ";
	static char *long_tag = "CPU states: ";

	/*
	 * if length + strlen(long_tag) >= screen_width, then we have to use the
	 * shorter tag (we subtract 2 to account for ": ")
	 */
	if (cpustate_total_length + (int) strlen(long_tag) - 2 >= screen_width)
	{
		use = short_tag;
	}
	else
	{
		use = long_tag;
	}

	/* set x_cpustates accordingly then return result */
	x_cpustates = strlen(use);
	return (use);
}

void
i_cpustates(int64_t * states)
{
	int			value;
	char	  **names;
	char	   *thisname;
	int		   *colp;
	int color = 0;

#ifdef ENABLE_COLOR
	int		   *cidx = cpustate_cidx;
#endif

	/* initialize */
	names = cpustate_names;
	colp = cpustate_columns;

	/* print tag */
	display_write(0, y_cpustates, 0, 0, cpustates_tag());

	/* now walk thru the names and print the line */
	while ((thisname = *names++) != NULL)
	{
		if (*thisname != '\0')
		{
			/* retrieve the value and remember it */
			value = *states;

#ifdef ENABLE_COLOR
			/* determine color number to use */
			color = color_test(*cidx++, value / 10);
#endif

			/* if percentage is >= 1000, print it as 100% */
			display_fmt(x_cpustates + *colp, y_cpustates,
						color, 0,
						(value >= 1000 ? "%4.0f%% %s" : "%4.1f%% %s"),
						((float) value) / 10.,
						thisname);
			if (*names != NULL)
				display_write(-1, -1, 0, 0, ",");

		}
		/* increment */
		colp++;
		states++;
	}

	/* copy over values into "last" array */
	memcpy(lcpustates, states, num_cpustates * sizeof(int));
}

void
u_cpustates(int64_t * states)
{
	int			value;
	char	  **names = cpustate_names;
	char	   *thisname;
	int		   *lp;
	int		   *colp;
	int color = 0;

#ifdef ENABLE_COLOR
	int		   *cidx = cpustate_cidx;
#endif

	lp = lcpustates;
	colp = cpustate_columns;

	/* we could be much more optimal about this */
	while ((thisname = *names++) != NULL)
	{
		if (*thisname != '\0')
		{
			/* did the value change since last time? */
			if (*lp != *states)
			{
				/* yes, change it */
				/* retrieve value and remember it */
				value = *states;

#ifdef ENABLE_COLOR
				/* determine color number to use */
				color = color_test(*cidx, value / 10);
#endif

				/* if percentage is >= 1000, print it as 100% */
				display_fmt(x_cpustates + *colp, y_cpustates,
							color, 0,
							(value >= 1000 ? "%4.0f%% %s" : "%4.1f%% %s"),
							((float) value) / 10.,
							thisname);
				if (*names != NULL)
					display_write(-1, -1, 0, 0, ",");

				/* remember it for next time */
				*lp = value;
			}
#ifdef ENABLE_COLOR
			cidx++;
#endif
		}

		/* increment and move on */
		lp++;
		states++;
		colp++;
	}
}

void
z_cpustates()
{
	register int i = 0;
	register char **names = cpustate_names;
	register char *thisname;
	register int *lp;

	/* print tag */
	display_write(0, y_cpustates, 0, 0, cpustates_tag());

	while ((thisname = *names++) != NULL)
	{
		if (*thisname != '\0')
		{
			display_fmt(-1, -1, 0, 0, "%s    %% %s", i++ == 0 ? "" : ", ",
						thisname);
		}
	}

	/* fill the "last" array with all -1s, to insure correct updating */
	lp = lcpustates;
	i = num_cpustates;
	while (--i >= 0)
	{
		*lp++ = -1;
	}
}

/*
 *	*_memory(stats) - print "Memory: " followed by the memory summary string
 *
 *	Assumptions:  cursor is on "lastline", the previous line
 */

void
i_memory(long *stats)
{
	display_write(0, y_mem, 0, 0, "Memory: ");

	/* format and print the memory summary */
	summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx);
}

void
u_memory(long *stats)
{
	/* format the new line */
	summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx);
}

/*
 *	*_swap(stats) - print "Swap: " followed by the swap summary string
 *
 *	Assumptions:  cursor is on "lastline", the previous line
 *
 *	These functions only print something when num_swap > 0
 */

void
i_swap(long *stats)
{
	if (num_swap > 0)
	{
		/* print the tag */
		display_write(0, y_swap, 0, 0, "Swap: ");

		/* format and print the swap summary */
		summary_format_memory(x_swap, y_swap, stats, swap_names,
				swap_cidx);
	}
}

void
u_swap(long *stats)
{
	if (num_swap > 0)
	{
		/* format the new line */
		summary_format_memory(x_swap, y_swap, stats, swap_names,
				swap_cidx);
	}
}

/*
 *     *_db(db_info) - print "DB activity: " followed by database activity
 */
void
i_db(struct db_info *db_info)
{
	u_db(db_info);
}

void
u_db(struct db_info *db_info)
{
	char buf[128];

	display_write(x_db, y_db, 0, 0, "DB activity: ");
	snprintf(buf, sizeof(buf),
			"%3ld tps, %2ld rollbs/s, %3ld buffer r/s, %2ld hit%%, %6ld row r/s, %4ld row w/s ",
			 db_info->numXact, db_info->numRollback,
			 db_info->numBlockRead,
			 db_info->numBlockRead + db_info->numBlockHit > 0 ?
					((int64_t)(db_info->numBlockHit * 100 /
					(db_info->numBlockRead + db_info->numBlockHit))) : 0,
			 db_info->numTupleFetched,
			 db_info->numTupleAltered);
	display_write(-1, -1, 0, 0, buf);
}

/*
 *     *_io(io_info) - print "DB I/O: " followed by IO summary
 */
void
i_io(struct io_info *io_info)
{
	char buf[128];
	display_write(x_io, y_io, 0, 0, "DB I/O: ");

	snprintf(buf, sizeof(buf),
			"%5ld reads/s, %5ld KB/s, %5ld writes/s, %5ld KB/s  ",
			io_info->reads,
			io_info->readsectors / 2,
			io_info->writes,
			io_info->writesectors / 2);
	display_write(-1, -1, 0, 0, buf);
}

void
u_io(struct io_info *io_info)
{
	i_io(io_info);
}

/*
 *	*_message() - print the next pending message line, or erase the one
 *				  that is there.
 *
 *	Note that u_message is (currently) the same as i_message.
 *
 *	Assumptions:  lastline is consistent
 */

/*
 *	i_message is funny because it gets its message asynchronously (with
 *	respect to screen updates).
 */

static char next_msg[MAX_COLS + 8];
static int	msglen = 0;

/* Invariant: msglen is always the length of the message currently displayed
   on the screen (even when next_msg doesn't contain that message). */

void
i_message()
{
	if (smart_terminal)
	{
		if (next_msg[0] != '\0')
		{
			display_move(0, y_message);
			standout(next_msg);
			msglen = strlen(next_msg);
			next_msg[0] = '\0';
		}
		else if (msglen > 0)
		{
			display_move(0, y_message);
			(void) clear_eol(msglen);
			msglen = 0;
		}
	}
}

void
u_message()
{
	i_message();
}

static int	header_length;

/*
 *	*_header(text) - print the header for the process area
 *
 *	Assumptions:  cursor is on the previous line and lastline is consistent
 */

void
i_header(char *text)
{
	if (text == NULL)
		return;
	header_length = strlen(text);
	if (header_status == ON)
	{
		display_write(x_header, y_header, header_color, 1, text);
	}
	else if (header_status == ERASE)
	{
		header_status = OFF;
	}
}

/*ARGSUSED*/
void
u_header(char *text)
{
	if (header_status == ERASE)
	{
		display_write(x_header, y_header, header_color, 1, "");
		header_status = OFF;
	}
}

/*
 *	*_process(line, thisline) - print one process line
 *
 *	Assumptions:  lastline is consistent
 */

void
i_process(int line, char *thisline)
{
	if (thisline == NULL)
		return;

	/* truncate the line to conform to our current screen width */
	if (strlen(thisline) > display_width)
		thisline[display_width] = '\0';

	/* write the line out */
	display_write(0, y_procs + line, 0, 1, thisline);
}

void
u_process(int line, char *newline)
{
	i_process(line, newline);
}

void
u_endscreen(int hi)
{
	if (smart_terminal)
	{
		/* clear-to-end the display */
		display_cte();

		/* move the cursor to a pleasant place */
		/* does this need to be a display_move??? */
		Move_to(x_idlecursor, y_idlecursor);
	}
	else
	{
		/* separate this display from the next with some vertical room */
		fputs("\n\n", stdout);
	}
}

void
display_header(int t)
{
	if (t)
	{
		header_status = ON;
	}
	else if (header_status == ON)
	{
		header_status = ERASE;
	}
}

void
new_message_v(int type, char *msgfmt, va_list ap)
{
	register int i;

	/* first, format the message */
	(void) vsnprintf(next_msg, sizeof(next_msg), msgfmt, ap);

	if (msglen > 0)
	{
		/* message there already -- can we clear it? */
		if (!overstrike)
		{
			/* yes -- write it and clear to end */
			i = strlen(next_msg);
			if ((type & MT_delayed) == 0)
			{
				if ((type & MT_standout) != 0)
					standout(next_msg);
				else
					fputs(next_msg, stdout);
				(void) clear_eol(msglen - i);
				msglen = i;
				next_msg[0] = '\0';
			}
		}
	}
	else
	{
		if ((type & MT_delayed) == 0)
		{
			if ((type & MT_standout) != 0)
				standout(next_msg);
			else
				fputs(next_msg, stdout);
			msglen = strlen(next_msg);
			next_msg[0] = '\0';
		}
	}
}

void
new_message(int type, char *msgfmt,...)
{
	va_list		ap;

	va_start(ap, msgfmt);
	new_message_v(type, msgfmt, ap);
	va_end(ap);
}

void
display_error_message(char *msgfmt,...)
{
	va_list		ap;

	va_start(ap, msgfmt);
	new_message_v(MT_standout | MT_delayed, msgfmt, ap);
	va_end(ap);
}

void
clear_message()
{
#ifdef DEBUG
	dprintf("clear_message: msglen = %d, x = %d, y = %d\n", msglen, curr_x, curr_y);
#endif							/* DEBUG */
	if (clear_eol(msglen) == 1)
	{
		putchar('\r');
	}
}

int
readline(char *buffer, int size, int numeric)
{
	register char *ptr = buffer;
	register char ch;
	register char cnt = 0;
	register char maxcnt = 0;

	/* allow room for null terminator */
	size -= 1;

	/* read loop */
	while ((fflush(stdout), read(0, ptr, 1) > 0))
	{
		/* newline or return means we are done */
		if ((ch = *ptr) == '\n' || ch == '\r')
		{
			break;
		}

		/* handle special editing characters */
		if (ch == ch_kill)
		{
			/* kill line -- account for overstriking */
			if (overstrike)
			{
				msglen += maxcnt;
			}

			/* return null string */
			*buffer = '\0';
			putchar('\r');
			return (-1);
		}
		else if (ch == ch_erase)
		{
			/* erase previous character */
			if (cnt <= 0)
			{
				/* none to erase! */
				putchar('\7');
			}
			else
			{
				fputs("\b \b", stdout);
				ptr--;
				cnt--;
			}
		}
		/* check for character validity and buffer overflow */
		else if (cnt == size || (numeric &&!isdigit(ch)) ||
				 !isprint(ch))
		{
			/* not legal */
			putchar('\7');
		}
		else
		{
			/* echo it and store it in the buffer */
			putchar(ch);
			ptr++;
			cnt++;
			if (cnt > maxcnt)
			{
				maxcnt = cnt;
			}
		}
	}

	/* all done -- null terminate the string */
	*ptr = '\0';

	/* account for the extra characters in the message area */
	/* (if terminal overstrikes, remember the furthest they went) */
	msglen += overstrike ? maxcnt : cnt;

	/* return either inputted number or string length */
	putchar('\r');
	return (cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
}

void
display_pagerstart()
{
	display_clear();
}

void
display_pagerend()
{
	char		ch;

	standout("Hit any key to continue: ");
	fflush(stdout);
	(void) read(0, &ch, 1);
}

void
display_pager(char *data)
{
	int			ch;
	char		readch;

	while ((ch = *data++) != '\0')
	{
		putchar(ch);
		if (ch == '\n')
		{
			if (++curr_y >= screen_length - 1)
			{
				standout("...More...");
				fflush(stdout);
				(void) read(0, &readch, 1);
				putchar('\r');
				switch (readch)
				{
					case '\r':
					case '\n':
						curr_y--;
						break;

					case 'q':
						return;

					default:
						curr_y = 0;
				}
			}
		}
	}
}
0707010000001B000081A400000000000000000000000166622DE8000005EC000000000000000000000000000000000000001700000000pg_top-4.1.1/display.h/* interface declaration for display.c */

#ifndef _DISPLAY_H
#define _DISPLAY_H

#include "machine.h"

/* "type" argument for new_message function */

#define  MT_standout  1
#define  MT_delayed   2

int			display_resize();
int			display_init(struct statics *statics);
void		i_loadave(int mpid, double *avenrun);
void		u_loadave(int mpid, double *avenrun);
void		i_minibar(int (*) (char *, int));
void		u_minibar(int (*) (char *, int));
void		i_uptime(time_t *bt, time_t *tod);
void		u_uptime(time_t *bt, time_t *tod);
void		i_timeofday(time_t *tod);
void		i_procstates(int total, int *brkdn);
void		u_procstates(int total, int *brkdn);
void		i_cpustates(int64_t * states);
void		u_cpustates(int64_t * states);
void		z_cpustates();
void		i_memory(long *stats);
void		u_memory(long *stats);
void		i_swap(long *stats);
void		u_swap(long *stats);
void		i_db(struct db_info *db_info);
void		u_db(struct db_info *db_info);
void		i_io(struct io_info *io_info);
void		u_io(struct io_info *io_info);
void		i_message();
void		u_message();
void		i_header(char *text);
void		u_header(char *text);
void		i_process(int line, char *thisline);
void		u_process(int line, char *newline);
void		u_endscreen(int hi);
void		display_header(int t);
void		new_message(int type, char *msgfmt,...);
void		display_error_message(char *msgfmt,...);
void		clear_message();
int			readline(char *buffer, int size, int numeric);
void		display_pagerstart();
void		display_pagerend();
void		display_pager(char *data);

#endif							/* _DISPLAY_H */
0707010000001C000081A400000000000000000000000166622DE8000006D6000000000000000000000000000000000000001600000000pg_top-4.1.1/getopt.c/*
 * "getopt" routine customized for top.
 */

/*
 * Many modern-day Unix implementations already have this function
 * in libc.  The standard "getopt" is perfectly sufficient for top's
 * needs.  If such a function exists in libc then you certainly don't
 * need to compile this one in.  To prevent this function from being
 * compiled, define "HAVE_GETOPT".	This is usually done in the "CFLAGS"
 * line of the corresponding machine module.
 */

#include <config.h>

#ifndef HAVE_GETOPT

/*LINTLIBRARY*/

#include "os.h"
#ifndef NULL
#define NULL	0
#endif
#ifndef EOF
#define EOF (-1)
#endif
#define ERR(s, c)	if(opterr){\
	char errbuf[2];\
	errbuf[0] = c; errbuf[1] = '\n';\
	(void) write(2, argv[0], strlen(argv[0]));\
	(void) write(2, s, strlen(s));\
	(void) write(2, errbuf, 2);}


int			opterr = 1;
int			optind = 1;
int			optopt;
char	   *optarg;

int
getopt(int argc, char **argv, char *opts)
{
	static int	sp = 1;
	register int c;
	register char *cp;

	if (sp == 1)
	{
		if (optind >= argc ||
			argv[optind][0] != '-' || argv[optind][1] == '\0')
			return (EOF);
		else if (strcmp(argv[optind], "--") == 0)
		{
			optind++;
			return (EOF);
		}
	}
	optopt = c = argv[optind][sp];
	if (c == ':' || (cp = strchr(opts, c)) == NULL)
	{
		ERR(": unknown option, -", c);
		if (argv[optind][++sp] == '\0')
		{
			optind++;
			sp = 1;
		}
		return ('?');
	}
	if (*++cp == ':')
	{
		if (argv[optind][sp + 1] != '\0')
			optarg = &argv[optind++][sp + 1];
		else if (++optind >= argc)
		{
			ERR(": argument missing for -", c);
			sp = 1;
			return ('?');
		}
		else
			optarg = argv[optind++];
		sp = 1;
	}
	else
	{
		if (argv[optind][++sp] == '\0')
		{
			sp = 1;
			optind++;
		}
		optarg = NULL;
	}
	return (c);
}

#endif							/* HAVE_GETOPT */
0707010000001D000081A400000000000000000000000166622DE80000047F000000000000000000000000000000000000001400000000pg_top-4.1.1/help.h/* Simple help text displayed by "show_help" */

#ifndef _HELP_H_
#define _HELP_H_

char	   *help_text = "\n\
A top users display for PostgreSQL\n\
\n\
These single-character commands are available:\n\
\n\
^L      - redraw screen\n\
<sp>    - update screen\n\
A       - EXPLAIN ANALYZE (UPDATE/DELETE safe)\n\
a       - show PostgreSQL activity\n\
C       - toggle the use of color\n\
E       - show execution plan (UPDATE/DELETE safe)\n\
I       - show I/O statistics per process (Linux only)\n\
L       - show locks held by a process\n\
R       - show PostgreSQL replication subscriptions\n\
Q       - show current query of a process\n\
c       - toggle the display of process commands\n\
d       - change number of displays to show\n\
h or ?  - help; show this text\n\
i       - toggle the displaying of idle processes\n\
n or #  - change number of processes to display\n\
o       - specify sort order (%s)\n\
q       - quit\n\
s       - change number of seconds to delay between updates\n\
u       - display processes for only one user (+ selects all users)\n\
\n\
Not all commands are available on all systems.\n\
";

#endif							/* _HELP_H_ */
0707010000001E000081A400000000000000000000000166622DE80000039F000000000000000000000000000000000000001600000000pg_top-4.1.1/layout.h/*
 *	Top - a top users display for Unix
 *
 *	This file defines the default locations on the screen for various parts
 *	of the display.  These definitions are used by the routines in "display.c"
 *	for cursor addressing.
 */

#ifndef _LAYOUT_H_
#define _LAYOUT_H_

#define  X_LASTPID	10
#define  Y_LASTPID	0
#define  X_LASTPIDWIDTH 13
#define  X_LOADAVE	27
#define  Y_LOADAVE	0
#define  X_LOADAVEWIDTH 7
#define  X_MINIBAR		50
#define  Y_MINIBAR		0
#define  X_UPTIME		53
#define  Y_UPTIME		0
#define  X_PROCSTATE	15
#define  Y_PROCSTATE	1
#define  X_BRKDN	15
#define  Y_BRKDN	1
#define  X_CPUSTATES	0
#define  Y_CPUSTATES	2
#define  X_MEM		8
#define  Y_MEM		3
#define  X_DB		0
#define  Y_DB		4
#define  X_IO		0
#define  Y_IO		5
#define  X_SWAP		6
#define  Y_SWAP		6
#define  Y_MESSAGE	6
#define  X_HEADER	0
#define  Y_HEADER	7
#define  X_IDLECURSOR	0
#define  Y_IDLECURSOR	6
#define  Y_PROCS	8

#endif							/* _LAYOUT_H_ */
0707010000001F000081A400000000000000000000000166622DE800000678000000000000000000000000000000000000001700000000pg_top-4.1.1/loadavg.h/*
 *	Top - a top users display for Berkeley Unix
 *
 *	Defines required to access load average figures.
 *
 *	This include file sets up everything we need to access the load average
 *	values in the kernel in a machine independent way.	First, it sets the
 *	typedef "load_avg" to be either double or long (depending on what is
 *	needed), then it defines these macros appropriately:
 *
 *	loaddouble(la) - convert load_avg to double.
 *	intload(i)	   - convert integer to load_avg.
 */

#ifndef _LOADAVG_H_
#define _LOADAVG_H_

/*
 * We assume that if FSCALE is defined, then avenrun and ccpu are type long.
 * If your machine is an exception (mips, perhaps?) then make adjustments
 * here.
 *
 * Defined types:  load_avg for load averages, pctcpu for cpu percentages.
 */
#if defined(mips) && !defined(NetBSD) && !defined(OpenBSD)
#include <sys/fixpoint.h>
#if defined(FBITS) && !defined(FSCALE)
#define FSCALE (1 << FBITS)		/* mips */
#endif
#endif

#ifdef FSCALE
#define FIXED_LOADAVG FSCALE
#define FIXED_PCTCPU FSCALE
#endif

#ifdef ibm032
#undef FIXED_LOADAVG
#undef FIXED_PCTCPU
#define FIXED_PCTCPU PCT_SCALE
#endif


#ifdef FIXED_PCTCPU
typedef long pctcpu;

#define pctdouble(p) ((double)(p) / FIXED_PCTCPU)
#else
typedef double pctcpu;

#define pctdouble(p) (p)
#endif

#ifdef FIXED_LOADAVG
#if __FreeBSD__ == 9
typedef __uint32_t load_avg;
#else
typedef fixpt_t load_avg;
#endif							/* __FreeBSD__ == 9 */

#define loaddouble(la) ((double)(la) / FIXED_LOADAVG)
#define intload(i) ((int)((i) * FIXED_LOADAVG))
#else
typedef double load_avg;

#define loaddouble(la) (la)
#define intload(i) ((double)(i))
#endif

#endif							/* _LOADAVG_H_ */
07070100000020000041ED00000000000000000000000266622DE800000000000000000000000000000000000000000000001500000000pg_top-4.1.1/machine07070100000021000081A400000000000000000000000166622DE800000EC9000000000000000000000000000000000000001700000000pg_top-4.1.1/machine.h/*
 *	This file defines the interface between top and the machine-dependent
 *	module.  It is NOT machine dependent and should not need to be changed
 *	for any specific machine.
 *
 *	Copyright (c) 2007-2019, Mark Wong
 */

#ifndef _MACHINE_H_
#define _MACHINE_H_

#include <time.h>
#include <sys/types.h>

#include "pg.h"
#include "pg_config_manual.h"

/*
#ifdef CLK_TCK
#	define HZ CLK_TCK
# else
#	define HZ 60
# endif
*/

#ifndef HZ
#define HZ sysconf(_SC_CLK_TCK)
#endif

/* Display modes. */

enum DisplayMode
{
	MODE_PROCESSES,
	MODE_IO_STATS,
	MODE_REPLICATION,
	MODE_TYPES					/* number of modes */
};

/* Maximum number of columns allowed for display */
#define MAX_COLS	255

/*
 * The entire display is based on these next numbers being defined as is.
 */

#define NUM_AVERAGES	3

#define NPROCSTATES 7

/*
 * The statics struct is filled in by machine_init.  Fields marked as
 * "optional" are not filled in by every module.
 */
struct statics
{
	char	  **procstate_names;
	char	  **cpustate_names;
	char	  **memory_names;
	char	  **swap_names;		/* optional */
	char	  **order_names;	/* optional */
	char	  **color_names;	/* optional */
	time_t		boottime;		/* optional */
	int			ncpus;
	struct
	{
		unsigned int fullcmds:1;
		unsigned int idle:1;
		unsigned int warmup:1;
	}			flags;
};

/*
 * the system_info struct is filled in by a machine dependent routine.
 */

#ifdef p_active					/* uw7 define macro p_active */
#define P_ACTIVE p_pactive
#else
#define P_ACTIVE p_active
#endif

struct system_info
{
	int			last_pid;
	double		load_avg[NUM_AVERAGES];
	int			p_total;
	int			P_ACTIVE;		/* number of procs considered "active" */
	int		   *procstates;
	int64_t    *cpustates;
	long	   *memory;
	long	   *swap;
};

/* cpu_states is an array of percentages * 10.	For example,
   the (integer) value 105 is 10.5% (or .105).
 */

/*
 * Database activity information
 */
struct db_info {
	int numDb;
	int64_t numXact;
	int64_t numRollback;
	int64_t numBlockRead;
	int64_t numBlockHit;
	int64_t numTupleFetched;
	int64_t numTupleAltered;
	int64_t numConflict;
};

/*
 * Info on reads/writes happening on disk.
 * On Linux, this can be obtained from /proc/diskstats.
 */
struct io_info {
	int64_t reads;
	int64_t readsectors;
	int64_t writes;
	int64_t writesectors;
};

/*
 * the process_select struct tells get_process_info what processes we
 * are interested in seeing
 */

struct process_select
{
	int			idle;			/* show idle processes */
	int			fullcmd;		/* show full command */
	char	   *command;		/* only this command (unless == NULL) */
	char		usename[NAMEDATALEN + 1];	/* only this postgres usename */
};

/* routines defined by the machine dependent module */
int			machine_init(struct statics *);
void		get_system_info(struct system_info *);
caddr_t		get_process_info(struct system_info *, struct process_select *, int,
							 struct pg_conninfo_ctx *, int);
#ifdef __linux__
caddr_t get_process_info(struct system_info *, struct process_select *, int,
				 struct pg_conninfo_ctx *, int);
#else
caddr_t get_process_info(struct system_info *, struct process_select *, int,
				 char *);
#endif /* __linux__ */
void		get_database_info(struct db_info *, struct pg_conninfo_ctx *);
void		get_io_info(struct io_info *);
char	   *format_header(char *);
#if defined(__linux__) || defined (__FreeBSD__)
char	   *format_next_io(caddr_t);
#endif /* defined(__linux__) || defined (__FreeBSD__) */
char	   *format_next_process(caddr_t);
char	   *format_next_replication(caddr_t);
uid_t		proc_owner(pid_t);
void		update_state(int *pgstate, char *state);
void		update_str(char **, char *);

extern int	mode_stats;

extern char *backendstatenames[];
extern char *procstatenames[];
extern char fmt_header_io[];
extern char fmt_header_replication[];

#endif							/* _MACHINE_H_ */
07070100000022000081A400000000000000000000000166622DE800004106000000000000000000000000000000000000001F00000000pg_top-4.1.1/machine/m_aix43.c/*
 * pg_top - a top PostgreSQL users display for Unix
 *
 * SYNOPSIS:  PowerPC running AIX 4.2 or higher
 *
 * DESCRIPTION:
 * This is the machine-dependent module for AIX 4.2 and higher
 * It is currenlty only tested on PowerPC architectures.
 *
 * TERMCAP: -lcurses
 *
 * CFLAGS: -DORDER -DHAVE_GETOPT
 *
 * LIBS: -bD:0x18000000
 *
 * AUTHOR:	Joep Vesseur <joep@fwi.uva.nl>
 *
 * PATCHES: Antoine Tabary <tabary@bruyeres.cea.fr>
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <nlist.h>
#include <sys/sysinfo.h>
#include <procinfo.h>
#include <sys/proc.h>
#include <sys/times.h>
#include <sys/param.h>
#include <pwd.h>
#include "pg_top.h"
#include "machine.h"
#include "utils.h"


#define PROCRESS(p) (((p)->pi_trss + (p)->pi_drss)*4)
#define PROCSIZE(p) (((p)->pi_tsize/1024+(p)->pi_dvm)*4)
#define PROCTIME(pi) (pi->pi_ru.ru_utime.tv_sec + pi->pi_ru.ru_stime.tv_sec)


/*
 * structure definition taken from 'monitor' by Jussi Maki (jmaki@hut.fi)
 */
struct vmker
{
	uint		n0,
				n1,
				n2,
				n3,
				n4,
				n5,
				n6,
				n7,
				n8;
	uint		totalmem;
	uint		badmem;			/* this is used in RS/6000 model 220 */
	uint		freemem;
	uint		n12;
	uint		numperm;		/* this seems to keep other than text and data
								 * segment usage; name taken from
								 * /usr/lpp/bos/samples/vmtune.c */
	uint		totalvmem,
				freevmem;
	uint		n15,
				n16,
				n17,
				n18,
				n19;
};


#define KMEM "/dev/kmem"

/* Indices in the nlist array */
#define X_AVENRUN		0
#define X_SYSINFO		1
#define X_VMKER			2
#define X_PROC			3
#define X_V				4

static struct nlist nlst[] = {
	{"avenrun", 0, 0, 0, 0, 0}, /* 0 */
	{"sysinfo", 0, 0, 0, 0, 0}, /* 1 */
	{"vmker", 0, 0, 0, 0, 0},	/* 2 */
	{"proc", 0, 0, 0, 0, 0},	/* 3 */
	{"v", 0, 0, 0, 0, 0},		/* 4 */
	{NULL, 0, 0, 0, 0, 0}
};


/* get_process_info returns handle. definition is here */
struct handle
{
	struct procsinfo **next_proc;
	int			remaining;
};

/*
 *	These definitions control the format of the per-process area
 */
static char header[] =
"   PID X        PRI NICE   SIZE   RES STATE   TIME   WCPU    CPU COMMAND";

/* 0123456	 -- field to fill in starts at header+6 */
#define UNAME_START 7

#define Proc_format \
	"%6d %-8.8s %3d %4d %5d%c %4d%c %-5s %6s %5.2f%% %5.2f%% %.14s%s"


/* these are for detailing the process states */
int			process_states[9];
char	   *procstatenames[] = {
	" none, ", " sleeping, ", " state2, ", " runnable, ",
	" idle, ", " zombie, ", " stopped, ", " running, ", " swapped, ",
	NULL
};


/* these are for detailing the cpu states */
int			cpu_states[4];
char	   *cpustatenames[] = {
	"idle", "user", "kernel", "wait",
	NULL
};

/* these are for detailing the memory statistics */
long		memory_stats[4];
char	   *memorynames[] = {
	"K Total, ", "K Free, ", "K Buffers", NULL
};

#define M_REAL	   0
#define M_REALFREE 1
#define M_BUFFERS  2

long		swap_stats[3];
char	   *swapnames[] = {
	"K Total, ", "K Free", NULL
};

#define M_VIRTUAL  0
#define M_VIRTFREE 1

char	   *state_abbrev[] = {
	"", "sleep", "", "", "sleep", "zomb", "stop", "run", "swap"
};

/* sorting orders. first is default */
char	   *ordernames[] = {"cpu", "size", "res", "time", "pri", NULL};

/* compare routines */
int			compare_cpu(), compare_size(), compare_res(), compare_time(),
			compare_prio();

int			(*proc_compares[]) () =
{
	compare_cpu,
		compare_size,
		compare_res,
		compare_time,
		compare_prio,
		NULL
};

/* useful externals */
extern int	errno;
extern char *sys_errlist[];
long		lseek();
long		time();
long		percentages();


/* useful globals */
int			kmem;				/* file descriptor */

/* offsets in kernel */
static unsigned long avenrun_offset;
static unsigned long sysinfo_offset;
static unsigned long vmker_offset;
static unsigned long proc_offset;
static unsigned long v_offset;

/* used for calculating cpu state percentages */
static long cp_time[CPU_NTIMES];
static long cp_old[CPU_NTIMES];
static long cp_diff[CPU_NTIMES];

/* the runqueue length is a cumulative value. keep old value */
long		old_runque;

/* process info */
struct var	v_info;				/* to determine nprocs */
int			nprocs;				/* maximum nr of procs in proctab */
int			ncpus;				/* nr of cpus installed */

int			ptsize;				/* size of process table in bytes */
struct proc *p_proc;			/* a copy of the process table */
struct procsinfo *p_info;		/* needed for vm and ru info */
struct procsinfo **pref;		/* processes selected for display */
int			pref_len;			/* number of processes selected */

/* needed to calculate WCPU */
unsigned long curtime;


/*
 * Initialize globals, get kernel offsets and stuff...
 */
machine_init(struct statics *statics)

{
	time_t		uptime,
				now;
	struct tms	tbuf;

	if ((kmem = open(KMEM, O_RDONLY)) == -1)
	{
		perror(KMEM);
		return -1;
	}

	/* get kernel symbol offsets */
	if (knlist(nlst, 5, sizeof(struct nlist)) != 0)
	{
		perror("knlist");
		return -1;
	}
	avenrun_offset = nlst[X_AVENRUN].n_value;
	sysinfo_offset = nlst[X_SYSINFO].n_value;
	vmker_offset = nlst[X_VMKER].n_value;
	proc_offset = nlst[X_PROC].n_value;
	v_offset = nlst[X_V].n_value;

	getkval(v_offset, (caddr_t) & v_info, sizeof v_info, "v");

	ncpus = v_info.v_ncpus;		/* number of cpus */
	nprocs = PROCMASK(PIDMAX);
	if (nprocs > 1024)
		nprocs = 1024;

	ptsize = nprocs * sizeof(struct proc);
	p_proc = (struct proc *) malloc(ptsize);
	p_info = (struct procsinfo *) malloc(nprocs * sizeof(struct procsinfo));
	pref = (struct procsinfo **) malloc(nprocs * sizeof(struct procsinfo *));

	if (!p_proc || !p_info || !pref)
	{
		fprintf(stderr, "pg_top: not enough memory\n");
		return -1;
	}

	/* set boot time */
	now = time(NULL);
	uptime = times(&tbuf) / HZ;
	statics->boottime = now - uptime;

	statics->procstate_names = procstatenames;
	statics->cpustate_names = cpustatenames;
	statics->memory_names = memorynames;
	statics->order_names = ordernames;
	statics->swap_names = swapnames;

	return (0);
}



char *
format_header(char *uname_field)

{
	register char *ptr;

	ptr = header + UNAME_START;
	while (*uname_field != '\0')
	{
		*ptr++ = *uname_field++;
	}

	return (header);
}



void
get_system_info(struct system_info *si)

{
	int			load_avg[3];
	struct sysinfo s_info;
	struct vmker m_info;
	int			i;
	double		total = 0;

	/* get the load avarage array */
	getkval(avenrun_offset, (caddr_t) load_avg, sizeof load_avg, "avenrun");

	/* get the sysinfo structure */
	getkval(sysinfo_offset, (caddr_t) & s_info, sizeof s_info, "sysinfo");

	/* get vmker structure */
	getkval(vmker_offset, (caddr_t) & m_info, sizeof m_info, "vmker");

	/* convert load avarages to doubles */
	for (i = 0; i < 3; i++)
		si->load_avg[i] = (double) load_avg[i] / 65536.0;

	/* calculate cpu state in percentages */
	for (i = 0; i < CPU_NTIMES; i++)
	{
		cp_old[i] = cp_time[i];
		cp_time[i] = s_info.cpu[i];
		cp_diff[i] = cp_time[i] - cp_old[i];
		total += cp_diff[i];
	}

	total = total / 1000.0;		/* pg_top itself will correct this */
	for (i = 0; i < CPU_NTIMES; i++)
	{
		cpu_states[i] = cp_diff[i] / total;
	}

	/* calculate memory statistics, scale 4K pages to megabytes */
#define PAGE_TO_MB(a) ((a)*4/1024)
	memory_stats[M_REAL] = PAGE_TO_MB(m_info.totalmem);
	memory_stats[M_REALFREE] = PAGE_TO_MB(m_info.freemem);
	memory_stats[M_BUFFERS] = PAGE_TO_MB(m_info.numperm);
	swap_stats[M_VIRTUAL] = PAGE_TO_MB(m_info.totalvmem);
	swap_stats[M_VIRTFREE] = PAGE_TO_MB(m_info.freevmem);

	/* runnable processes */
	process_states[0] = s_info.runque - old_runque;
	old_runque = s_info.runque;

	si->cpustates = cpu_states;
	si->memory = memory_stats;
	si->swap = swap_stats;
}

static struct handle handle;

caddr_t
get_process_info(struct system_info *si, struct process_select *sel, int compare_index)

{
	int			i,
				nproc;
	int			ptsize_util;
	int			active_procs = 0,
				total_procs = 0;
	struct procsinfo *pp,
			  **p_pref = pref;
	unsigned long pctcpu;
	pid_t		procsindex = 0;
	struct proc *p;

	si->procstates = process_states;

	curtime = time(0);

	/* get the procsinfo structures of all running processes */
	nproc = getprocs(p_info, sizeof(struct procsinfo), NULL, 0,
					 &procsindex, nprocs);
	if (nproc < 0)
	{
		perror("getprocs");
		quit(1);
	}

	/* the swapper has no cmd-line attached */
	strcpy(p_info[0].pi_comm, "swapper");

	/* get proc table */
	ptsize_util = (PROCMASK(p_info[nproc - 1].pi_pid) + 1) * sizeof(struct proc);
	getkval(proc_offset, (caddr_t) p_proc, ptsize_util, "proc");

	memset(process_states, 0, sizeof process_states);

	/*
	 * build a list of pointers to processes to show. walk through the list of
	 * procsinfo structures instead of the proc table since the mapping of
	 * procsinfo -> proctable is easy, the other way around is cumbersome
	 */
	for (pp = p_info, i = 0; i < nproc; pp++, i++)
	{

		p = &p_proc[PROCMASK(pp->pi_pid)];

		/*
		 * AIX marks all runnable processes as ACTIVE. We want to know which
		 * processes are sleeping, so check used cpu ticks and adjust status
		 * field accordingly
		 */
		if (p->p_stat == SACTIVE && p->p_cpticks == 0)
			p->p_stat = SIDL;

		if (pp->pi_state && (sel->system || ((pp->pi_flags & SKPROC) == 0)))
		{
			total_procs++;
			process_states[p->p_stat]++;
			if ((pp->pi_state != SZOMB) &&
				(sel->idle || p->p_cpticks != 0 || (p->p_stat == SACTIVE))
				&& (sel->uid == -1 || pp->pi_uid == (uid_t) sel->uid))
			{
				*p_pref++ = pp;
				active_procs++;
			}
		}
	}

	/*
	 * the pref array now holds pointers to the procsinfo structures in the
	 * p_info array that were selected for display
	 */

	/* sort if requested */
	if (si->p_active)
		qsort((char *) pref, active_procs, sizeof(struct procsinfo *),
			  proc_compares[compare_index]);

	si->last_pid = -1;			/* no way to figure out last used pid */
	si->p_total = total_procs;
	si->p_active = pref_len = active_procs;

	handle.next_proc = pref;
	handle.remaining = active_procs;

	return ((caddr_t) & handle);
}

char		fmt[MAX_COLS];		/* static area where result is built */

/* define what weighted cpu is. use definition of %CPU from 'man ps(1)' */
#define weighted_cpu(pp) (PROCTIME(pp) == 0 ? 0.0 : \
						(((PROCTIME(pp)*100.0)/(curtime-pi->pi_start)/ncpus)))
#define double_pctcpu(p) ((double)p->p_pctcpu/(double)FLT_MODULO)

char *
format_next_process(caddr_t handle, char *(*get_userid) ())

{
	register struct handle *hp;
	register struct procsinfo *pi;
	register struct proc *p;
	char	   *uname;
	long		cpu_time;
	int			proc_size,
				proc_ress;
	char		size_unit = 'K';
	char		ress_unit = 'K';

	hp = (struct handle *) handle;
	if (hp->remaining == 0)
	{							/* safe guard */
		fmt[0] = '\0';
		return fmt;
	}
	pi = *(hp->next_proc++);
	hp->remaining--;
	p = &p_proc[PROCMASK(pi->pi_pid)];

	cpu_time = PROCTIME(pi);

	/* we disply sizes up to 10M in KiloBytes, beyond 10M in MegaBytes */
	if ((proc_size = (pi->pi_tsize / 1024 + pi->pi_dvm) * 4) > 10240)
	{
		proc_size /= 1024;
		size_unit = 'M';
	}
	if ((proc_ress = (pi->pi_trss + pi->pi_drss) * 4) > 10240)
	{
		proc_ress /= 1024;
		ress_unit = 'M';
	}

	sprintf(fmt, Proc_format,
			pi->pi_pid,			/* PID */
			(*get_userid) (pi->pi_uid), /* login name */
			getpriority(PRIO_PROCESS, pi->pi_pid),
			EXTRACT_NICE(p),	/* fixed or vari */
			proc_size,			/* size */
			size_unit,			/* K or M */
			proc_ress,			/* resident */
			ress_unit,			/* K or M */
			state_abbrev[p->p_stat],	/* process state */
			format_time(cpu_time),	/* time used */
			weighted_cpu(pi),	/* WCPU */
			100.0 * double_pctcpu(p),	/* CPU */
			printable(pi->pi_comm), /* COMM */
			(pi->pi_flags & SKPROC) == 0 ? "" : " (sys)"	/* kernel process? */
		);
	return (fmt);
}


/*
 *	getkval(offset, ptr, size, refstr) - get a value out of the kernel.
 *	"offset" is the byte offset into the kernel for the desired value,
 *		"ptr" points to a buffer into which the value is retrieved,
 *		"size" is the size of the buffer (and the object to retrieve),
 *		"refstr" is a reference string used when printing error meessages,
 *		if "refstr" starts with a '!', then a failure on read will not
 *			be fatal (this may seem like a silly way to do things, but I
 *			really didn't want the overhead of another argument).
 *
 */

int
getkval(unsigned long offset, caddr_t ptr, int size, char *refstr)

{
	int			upper_2gb = 0;

	/*
	 * reads above 2Gb are done by seeking to offset%2Gb, and supplying 1
	 * (opposed to 0) as fourth parameter to readx (see 'man kmem')
	 */
	if (offset > 1 << 31)
	{
		upper_2gb = 1;
		offset &= 0x7fffffff;
	}

	if (lseek(kmem, offset, SEEK_SET) != offset)
	{
		fprintf(stderr, "pg_top: lseek failed\n");
		quit(2);
	}

	if (readx(kmem, ptr, size, upper_2gb) != size)
	{
		if (*refstr == '!')
			return 0;
		else
		{
			fprintf(stderr, "pg_top: kvm_read for %s: %s\n", refstr,
					sys_errlist[errno]);
			quit(2);
		}
	}

	return 1;
}

/* comparison routine for qsort */
/*
 * The following code is taken from the solaris module and adjusted
 * for AIX -- JV .
 */

#define ORDERKEY_PCTCPU \
		   if (lresult = p2->p_pctcpu - p1->p_pctcpu, \
			   (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0)

#define ORDERKEY_CPTICKS \
		   if ((result = PROCTIME(pi2) - PROCTIME(pi1)) == 0)


#define ORDERKEY_STATE \
		   if ((result = sorted_state[p2->p_stat]  \
						 - sorted_state[p1->p_stat])  == 0)

/* Nice values directly reflect the process' priority, and are always >0 ;-) */
#define ORDERKEY_PRIO \
	   if ((result = EXTRACT_NICE(p1) - EXTRACT_NICE(p2)) == 0)

#define ORDERKEY_RSSIZE \
		   if ((result = PROCRESS(pi2) - PROCRESS(pi1)) == 0)
#define ORDERKEY_MEM \
		   if ((result = PROCSIZE(pi2) - PROCSIZE(pi1)) == 0)

static unsigned char sorted_state[] =
{
	0,							/* not used */
	0,
	0,
	0,
	3,							/* sleep */
	1,							/* zombie */
	4,							/* stop */
	6,							/* run */
	2,							/* swap */
};

/* compare_cpu - the comparison function for sorting by cpu percentage */

int
compare_cpu(struct procsinfo **ppi1, struct procsinfo **ppi2)

{
	register struct procsinfo *pi1 = *ppi1,
			   *pi2 = *ppi2;
	register struct proc *p1;
	register struct proc *p2;
	register int result;
	register long lresult;

	p1 = &p_proc[PROCMASK(pi1->pi_pid)];
	p2 = &p_proc[PROCMASK(pi2->pi_pid)];

	ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_RSSIZE
		ORDERKEY_MEM
		;

	return result;
}


/* compare_size - the comparison function for sorting by total memory usage */

int
compare_size(struct procsinfo **ppi1, struct procsinfo **ppi2)

{
	register struct procsinfo *pi1 = *ppi1,
			   *pi2 = *ppi2;
	register struct proc *p1;
	register struct proc *p2;
	register int result;
	register long lresult;

	p1 = &p_proc[PROCMASK(pi1->pi_pid)];
	p2 = &p_proc[PROCMASK(pi2->pi_pid)];

	ORDERKEY_MEM
		ORDERKEY_RSSIZE
		ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_STATE
		ORDERKEY_PRIO
		;

	return result;
}


/* compare_res - the comparison function for sorting by resident set size */

int
compare_res(struct procsinfo **ppi1, struct procsinfo **ppi2)

{
	register struct procsinfo *pi1 = *ppi1,
			   *pi2 = *ppi2;
	register struct proc *p1;
	register struct proc *p2;
	register int result;
	register long lresult;

	p1 = &p_proc[PROCMASK(pi1->pi_pid)];
	p2 = &p_proc[PROCMASK(pi2->pi_pid)];

	ORDERKEY_RSSIZE
		ORDERKEY_MEM
		ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_STATE
		ORDERKEY_PRIO
		;

	return result;
}


/* compare_time - the comparison function for sorting by total cpu time */

int
compare_time(struct procsinfo **ppi1, struct procsinfo **ppi2)

{
	register struct procsinfo *pi1 = *ppi1,
			   *pi2 = *ppi2;
	register struct proc *p1;
	register struct proc *p2;
	register int result;
	register long lresult;

	p1 = &p_proc[PROCMASK(pi1->pi_pid)];
	p2 = &p_proc[PROCMASK(pi2->pi_pid)];

	ORDERKEY_CPTICKS
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return result;
}


/* compare_prio - the comparison function for sorting by cpu percentage */

int
compare_prio(struct procsinfo **ppi1, struct procsinfo **ppi2)

{
	register struct procsinfo *pi1 = *ppi1,
			   *pi2 = *ppi2;
	register struct proc *p1;
	register struct proc *p2;
	register int result;
	register long lresult;

	p1 = &p_proc[PROCMASK(pi1->pi_pid)];
	p2 = &p_proc[PROCMASK(pi2->pi_pid)];

	ORDERKEY_PRIO
		ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_STATE
		ORDERKEY_RSSIZE
		ORDERKEY_MEM
		;

	return result;
}

int
proc_owner(int pid)

{
	int			uid;
	register struct procsinfo **prefp = pref;
	register int cnt = pref_len;

	while (--cnt >= 0)
	{
		if ((*prefp)->pi_pid == pid)
			return (*prefp)->pi_uid;
		prefp++;
	}

	return (-1);
}

void
get_io_info(struct io_info *io_info)
{
	/* Not supported yet */
	memset(io_info, 0, sizeof(*io_info));
}
07070100000023000081A400000000000000000000000166622DE800004B36000000000000000000000000000000000000001E00000000pg_top-4.1.1/machine/m_aix5.c/*
 * pg_top - a top PostgreSQL users display for Unix
 *
 * SYNOPSIS:  PowerPC running AIX 5.1 or higher
 *
 * DESCRIPTION:
 * This is the machine-dependent module for AIX 5.1 and higher (may work on
 * older releases too).  It is currently only tested on PowerPC
 * architectures.
 *
 * TERMCAP: -lcurses
 *
 * CFLAGS: -DORDER -DHAVE_GETOPT -DHAVE_STRERROR -DMAXPROCS=10240
 *
 * LIBS: -lperfstat
 *
 * AUTHOR:	Joep Vesseur <joep@fwi.uva.nl>
 *
 * PATCHES: Antoine Tabary <tabary@bruyeres.cea.fr>, Dan Nelson <dnelson@allantgroup.com>
 */

#define MAXPROCS 10240

#include "config.h"

#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <nlist.h>
#include <procinfo.h>
#include <sys/types.h>
#include <sys/proc.h>
#include <sys/sysinfo.h>
#include <sys/sysconfig.h>
#include <pwd.h>
#include <errno.h>
#include <libperfstat.h>
#include "pg_top.h"
#include "machine.h"
#include "utils.h"


#define PROCRESS(p) (((p)->pi_trss + (p)->pi_drss)*4)
#define PROCSIZE(p) (((p)->pi_tsize/1024+(p)->pi_dvm)*4)
#define PROCTIME(pi) (pi->pi_ru.ru_utime.tv_sec + pi->pi_ru.ru_stime.tv_sec)

#ifdef OLD
/*
 * structure definition taken from 'monitor' by Jussi Maki (jmaki@hut.fi)
 */
struct vmker
{
	uint		n0,
				n1,
				n2,
				n3,
				n4,
				n5,
				n6,
				n7,
				n8;
	uint		totalmem;
	uint		badmem;			/* this is used in RS/6000 model 220 */
	uint		freemem;
	uint		n12;
	uint		numperm;		/* this seems to keep other than text and data
								 * segment usage; name taken from
								 * /usr/lpp/bos/samples/vmtune.c */
	uint		totalvmem,
				freevmem;
	uint		n15,
				n16,
				n17,
				n18,
				n19;
};

#define KMEM "/dev/kmem"

/* Indices in the nlist array */
#define X_AVENRUN		0
#define X_SYSINFO		1
#define X_VMKER			2
#define X_V				3

static struct nlist nlst[] = {
	{"avenrun", 0, 0, 0, 0, 0}, /* 0 */
	{"sysinfo", 0, 0, 0, 0, 0}, /* 1 */
	{"vmker", 0, 0, 0, 0, 0},	/* 2 */
	{"v", 0, 0, 0, 0, 0},		/* 3 */
	{NULL, 0, 0, 0, 0, 0}
};
#endif

/* get_process_info returns handle. definition is here */
struct handle
{
	struct procentry64 **next_proc;
	int			remaining;
};

/*
 *	These definitions control the format of the per-process area
 */
static char header[] =
"   PID X        PRI NICE   SIZE   RES STATE   TIME   WCPU    CPU COMMAND";

/* 0123456	 -- field to fill in starts at header+6 */
#define UNAME_START 7

#define Proc_format \
	"%6d %-8.8s %3d %4d %5d%c %4d%c %-5s %6s %5.2f%% %5.2f%% %.14s%s"


/* these are for detailing the process states */
int			process_states[9];
char	   *procstatenames[] = {
	" none, ", " sleeping, ", " state2, ", " runnable, ",
	" idle, ", " zombie, ", " stopped, ", " running, ", " swapped, ",
	NULL
};

/* these are for detailing the cpu states */
int			cpu_states[CPU_NTIMES];
char	   *cpustatenames[] = {
	"idle", "user", "kernel", "wait",
	NULL
};

/* these are for detailing the memory statistics */
long		memory_stats[7];
char	   *memorynames[] = {
	"K total, ", "K buf, ", "K sys, ", "K free", NULL
};

#define M_REAL		0
#define M_BUFFERS	1
#define M_SYSTEM	2
#define M_REALFREE	3

long		swap_stats[3];
char	   *swapnames[] = {
	"K total, ", "K free", NULL
};

#define M_VIRTUAL 0
#define M_VIRTFREE 1

char	   *state_abbrev[] = {
	NULL, NULL, NULL, NULL, "idle", "zomb", "stop", "run", "swap"
};

/* sorting orders. first is default */
char	   *ordernames[] = {"cpu", "size", "res", "time", "pri", NULL};

/* compare routines */
int			compare_cpu(), compare_size(), compare_res(), compare_time(),
			compare_prio();

int			(*proc_compares[]) () =
{
	compare_cpu,
		compare_size,
		compare_res,
		compare_time,
		compare_prio,
		NULL
};

/* useful externals */
long		percentages(int cnt, int *out, long *new, long *old, long *diffs);
char	   *format_time(long seconds);

#ifdef OLD
/* useful globals */
int			kmem;				/* file descriptor */

/* offsets in kernel */
static unsigned long avenrun_offset;
static unsigned long sysinfo_offset;
static unsigned long vmker_offset;
static unsigned long v_offset;
#endif

/* used for calculating cpu state percentages */
static long cp_time[CPU_NTIMES];
static long cp_old[CPU_NTIMES];
static long cp_diff[CPU_NTIMES];

/* the runqueue length is a cumulative value. keep old value */
long		old_runque;

/* process info */
struct kernvars v_info;			/* to determine nprocs */
int			nprocs;				/* maximum nr of procs in proctab */
int			ncpus;				/* nr of cpus installed */

struct procentry64 *p_info;		/* needed for vm and ru info */
struct procentry64 **pref;		/* processes selected for display */
struct timeval64 *cpu_proc,
		   *old_cpu_proc;		/* total cpu used by each process */
int			pref_len;			/* number of processes selected */

/* needed to calculate WCPU */
unsigned long curtime;

/* needed to calculate CPU */
struct timeval curtimeval;
struct timeval lasttimeval;

#ifdef OLD
int			getkval(unsigned long offset, caddr_t ptr, int size, char *refstr);
#endif

void *
xmalloc(long size)
{
	void	   *p = malloc(size);

	if (!p)
	{
		fprintf(stderr, "Could not allocate %ld bytes: %s\n", size, strerror(errno));
		exit(1);
	}
	return p;
}

/*
 * Initialize globals, get kernel offsets and stuff...
 */
int
			machine_init(statics)
struct statics *statics;

{
#ifdef OLD
	if ((kmem = open(KMEM, O_RDONLY)) == -1)
	{
		perror(KMEM);
		return -1;
	}

	/* get kernel symbol offsets */
	if (knlist(nlst, 4, sizeof(struct nlist)) != 0)
	{
		perror("knlist");
		return -1;
	}
	avenrun_offset = nlst[X_AVENRUN].n_value;
	sysinfo_offset = nlst[X_SYSINFO].n_value;
	vmker_offset = nlst[X_VMKER].n_value;
	v_offset = nlst[X_V].n_value;

	getkval(v_offset, (caddr_t) & v_info, sizeof v_info, "v");
#else
	sysconfig(SYS_GETPARMS, &v_info, sizeof v_info);
#endif
	ncpus = v_info.v_ncpus;		/* number of cpus */

/* procentry64 is 4912 bytes, and PROCMASK(PIDMAX) is 262144.  That'd
   require 1.2gb for the p_info array, which is way overkill.  Raise
   MAXPROCS if you have more than 10240 active processes in the system.
*/

#if 0
	nprocs = PROCMASK(PIDMAX);
#else
	nprocs = MAXPROCS;
#endif

	cpu_proc = (struct timeval64 *) xmalloc(PROCMASK(PIDMAX) * sizeof(struct timeval64));
	old_cpu_proc = (struct timeval64 *) xmalloc(PROCMASK(PIDMAX) * sizeof(struct timeval64));
	p_info = (struct procentry64 *) xmalloc(nprocs * sizeof(struct procentry64));
	pref = (struct procentry64 **) xmalloc(nprocs * sizeof(struct procentry64 *));

	statics->procstate_names = procstatenames;
	statics->cpustate_names = cpustatenames;
	statics->memory_names = memorynames;
	statics->swap_names = swapnames;
	statics->order_names = ordernames;

	return (0);
}

char	   *format_header(uname_field)
register char *uname_field;

{
	register char *ptr;

	ptr = header + UNAME_START;
	while (*uname_field != '\0')
	{
		*ptr++ = *uname_field++;
	}

	return (header);
}




void
			get_system_info(si)
struct system_info *si;

{
#ifdef OLD
	long long	load_avg[3];
	struct sysinfo64 s_info;
	struct vmker m_info;
#else
	perfstat_memory_total_t m_info1;
	perfstat_cpu_total_t s_info1;
#endif
	int			i;
	int			total = 0;

#ifdef OLD
	/* get the load avarage array */
	getkval(avenrun_offset, (caddr_t) load_avg, sizeof load_avg, "avenrun");

	/* get the sysinfo structure */
	getkval(sysinfo_offset, (caddr_t) & s_info, sizeof s_info, "sysinfo64");

	/* get vmker structure */
	getkval(vmker_offset, (caddr_t) & m_info, sizeof m_info, "vmker");
#else
	/* cpu stats */
	perfstat_cpu_total(NULL, &s_info1, sizeof s_info1, 1);

	/* memory stats */
	perfstat_memory_total(NULL, &m_info1, sizeof m_info1, 1);
#endif


#ifdef OLD
	/* convert load avarages to doubles */
	for (i = 0; i < 3; i++)
		si->load_avg[i] = (double) load_avg[i] / 65536.0;

	/* calculate cpu state in percentages */
	for (i = 0; i < CPU_NTIMES; i++)
	{
		cp_old[i] = cp_time[i];
		cp_time[i] = s_info.cpu[i];
		cp_diff[i] = cp_time[i] - cp_old[i];
		total += cp_diff[i];
	}
#else
	/* convert load avarages to doubles */
	for (i = 0; i < 3; i++)
		si->load_avg[i] = (double) s_info1.loadavg[i] / (1 << SBITS);

	/* calculate cpu state in percentages */
	for (i = 0; i < CPU_NTIMES; i++)
	{
		cp_old[i] = cp_time[i];
		cp_time[i] = (i == CPU_IDLE ? s_info1.idle :
					  i == CPU_USER ? s_info1.user :
					  i == CPU_KERNEL ? s_info1.sys :
					  i == CPU_WAIT ? s_info1.wait : 0);
		cp_diff[i] = cp_time[i] - cp_old[i];
		total += cp_diff[i];
	}
#endif
	for (i = 0; i < CPU_NTIMES; i++)
	{
		cpu_states[i] = 1000 * cp_diff[i] / total;
	}

	/* calculate memory statistics, scale 4K pages */
#ifdef OLD
#define PAGE_TO_MB(a) ((a)*4/1024)
	memory_stats[M_TOTAL] = PAGE_TO_MB(m_info.totalmem + m_info.totalvmem);
	memory_stats[M_REAL] = PAGE_TO_MB(m_info.totalmem);
	memory_stats[M_REALFREE] = PAGE_TO_MB(m_info.freemem);
	memory_stats[M_BUFFERS] = PAGE_TO_MB(m_info.numperm);
	swap_stats[M_VIRTUAL] = PAGE_TO_MB(m_info.totalvmem);
	swap_stats[M_VIRTFREE] = PAGE_TO_MB(m_info.freevmem);
#else
#define PAGE_TO_KB(a) ((a)*4)
	memory_stats[M_REAL] = PAGE_TO_KB(m_info1.real_total);
	memory_stats[M_BUFFERS] = PAGE_TO_KB(m_info1.numperm);
#ifdef _AIXVERSION_520
	memory_stats[M_SYSTEM] = PAGE_TO_KB(m_info1.real_system);
#endif
	memory_stats[M_REALFREE] = PAGE_TO_KB(m_info1.real_free);
	swap_stats[M_VIRTUAL] = PAGE_TO_KB(m_info1.pgsp_total);
	swap_stats[M_VIRTFREE] = PAGE_TO_KB(m_info1.pgsp_free);
#endif

	/* runnable processes */
#ifdef OLD
	process_states[0] = s_info.runque - old_runque;
	old_runque = s_info.runque;
#else
	process_states[0] = s_info1.runque - old_runque;
	old_runque = s_info1.runque;
#endif

	si->cpustates = cpu_states;
	si->memory = memory_stats;
	si->swap = swap_stats;
}

static struct handle handle;

caddr_t
get_process_info(si, sel, compare_index)
struct system_info *si;
struct process_select *sel;
int			compare_index;

{
	int			i,
				nproc;
	int			active_procs = 0,
				total_procs = 0;
	struct procentry64 *pp,
			  **p_pref = pref;
	struct timeval64 *cpu_proc_temp;
	double		timediff;
	pid_t		procsindex = 0;

	si->procstates = process_states;

	curtime = time(0);
	lasttimeval = curtimeval;
	gettimeofday(&curtimeval, NULL);

	/* get the procentry64 structures of all running processes */
	nproc = getprocs64(p_info, sizeof(struct procentry64), NULL, 0,
					   &procsindex, nprocs);
	if (nproc < 0)
	{
		perror("getprocs64");
		quit(1);
	}

	/* the swapper has no cmd-line attached */
	strcpy(p_info[0].pi_comm, "swapper");

	if (lasttimeval.tv_sec)
	{
		timediff = (curtimeval.tv_sec - lasttimeval.tv_sec) +
			1.0 * (curtimeval.tv_usec - lasttimeval.tv_usec) / uS_PER_SECOND;
	}

	/*
	 * The pi_cpu value is wildly inaccurate.  The maximum value is 120, but
	 * when the scheduling timer fires, the field is zeroed for all processes
	 * and ramps up over a short period of time.  Instead of using this weird
	 * number, manually calculate an accurate value from the rusage data.
	 * Store this run's rusage in cpu_proc[pid], and subtract from
	 * old_cpu_proc.
	 */
	for (pp = p_info, i = 0; i < nproc; pp++, i++)
	{
		pid_t		pid = PROCMASK(pp->pi_pid);

		/* total system and user time into cpu_proc */
		cpu_proc[pid] = pp->pi_ru.ru_utime;
		cpu_proc[pid].tv_sec += pp->pi_ru.ru_stime.tv_sec;
		cpu_proc[pid].tv_usec += pp->pi_ru.ru_stime.tv_usec;
		if (cpu_proc[pid].tv_usec > NS_PER_SEC)
		{
			cpu_proc[pid].tv_sec++;
			cpu_proc[pid].tv_usec -= NS_PER_SEC;
		}

		/*
		 * If this process was around during the previous update, calculate a
		 * true %CPU.  If not, convert the kernel's cpu value from its 120-max
		 * value to a 10000-max one.
		 */
		if (old_cpu_proc[pid].tv_sec == 0 && old_cpu_proc[pid].tv_usec == 0)
			pp->pi_cpu = pp->pi_cpu * 10000 / 120;
		else
			pp->pi_cpu = ((cpu_proc[pid].tv_sec - old_cpu_proc[pid].tv_sec) +
						  1.0 * (cpu_proc[pid].tv_usec - old_cpu_proc[pid].tv_usec) / NS_PER_SEC) / timediff * 10000;
	}

	/*
	 * remember our current values as old_cpu_proc, and zero out cpu_proc for
	 * the next update cycle
	 */
	memset(old_cpu_proc, 0, sizeof(struct timeval64) * nprocs);
	cpu_proc_temp = cpu_proc;
	cpu_proc = old_cpu_proc;
	old_cpu_proc = cpu_proc_temp;

	memset(process_states, 0, sizeof process_states);

	/* build a list of pointers to processes to show. */
	for (pp = p_info, i = 0; i < nproc; pp++, i++)
	{
		/*
		 * AIX marks all runnable processes as ACTIVE. We want to know which
		 * processes are sleeping, so check used cpu and adjust status field
		 * accordingly
		 */
		if (pp->pi_state == SACTIVE && pp->pi_cpu == 0)
			pp->pi_state = SIDL;

		if (pp->pi_state && (sel->system || ((pp->pi_flags & SKPROC) == 0)))
		{
			total_procs++;
			process_states[pp->pi_state]++;
			if ((pp->pi_state != SZOMB) &&
				(sel->idle || pp->pi_cpu != 0 || (pp->pi_state == SACTIVE))
				&& (sel->uid == -1 || pp->pi_uid == (uid_t) sel->uid))
			{
				*p_pref++ = pp;
				active_procs++;
			}
		}
	}

	/*
	 * the pref array now holds pointers to the procentry64 structures in the
	 * p_info array that were selected for display
	 */

	/* sort if requested */
	if (proc_compares[compare_index] != NULL)
		qsort((char *) pref, active_procs, sizeof(struct procentry64 *),
			  proc_compares[compare_index]);

	si->last_pid = -1;			/* no way to figure out last used pid */
	si->p_total = total_procs;
	si->p_active = pref_len = active_procs;

	handle.next_proc = pref;
	handle.remaining = active_procs;

	return ((caddr_t) & handle);
}

char		fmt[128];			/* static area where result is built */

/* define what weighted cpu is. use definition of %CPU from 'man ps(1)' */
#define weighted_cpu(pp) (PROCTIME(pp) == 0 ? 0.0 : \
						(((PROCTIME(pp)*100.0)/(curtime-pi->pi_start))))

char	   *format_next_process(handle, get_userid)
			caddr_t handle;
char	   *(*get_userid) ();

{
	register struct handle *hp;
	register struct procentry64 *pi;
	long		cpu_time;
	int			proc_size,
				proc_ress;
	char		size_unit = 'K';
	char		ress_unit = 'K';

	hp = (struct handle *) handle;
	if (hp->remaining == 0)
	{							/* safe guard */
		fmt[0] = '\0';
		return fmt;
	}
	pi = *(hp->next_proc++);
	hp->remaining--;

	cpu_time = PROCTIME(pi);

	/* we disply sizes up to 10M in KiloBytes, beyond 10M in MegaBytes */
	if ((proc_size = (pi->pi_tsize / 1024 + pi->pi_dvm) * 4) > 10240)
	{
		proc_size /= 1024;
		size_unit = 'M';
	}
	if ((proc_ress = (pi->pi_trss + pi->pi_drss) * 4) > 10240)
	{
		proc_ress /= 1024;
		ress_unit = 'M';
	}

	sprintf(fmt, Proc_format,
			pi->pi_pid,			/* PID */
			(*get_userid) (pi->pi_uid), /* login name */
			pi->pi_nice,		/* fixed or vari */
			getpriority(PRIO_PROCESS, pi->pi_pid),
			proc_size,			/* size */
			size_unit,			/* K or M */
			proc_ress,			/* resident */
			ress_unit,			/* K or M */
			state_abbrev[pi->pi_state], /* process state */
			format_time(cpu_time),	/* time used */
			weighted_cpu(pi),	/* WCPU */
			pi->pi_cpu / 100.0, /* CPU */
			printable(pi->pi_comm), /* COMM */
			(pi->pi_flags & SKPROC) == 0 ? "" : " (sys)"	/* kernel process? */
		);
	return (fmt);
}

#ifdef OLD
/*
 *	getkval(offset, ptr, size, refstr) - get a value out of the kernel.
 *	"offset" is the byte offset into the kernel for the desired value,
 *		"ptr" points to a buffer into which the value is retrieved,
 *		"size" is the size of the buffer (and the object to retrieve),
 *		"refstr" is a reference string used when printing error meessages,
 *		if "refstr" starts with a '!', then a failure on read will not
 *			be fatal (this may seem like a silly way to do things, but I
 *			really didn't want the overhead of another argument).
 *
 */
int
			getkval(offset, ptr, size, refstr)
unsigned long offset;
caddr_t		ptr;
int			size;
char	   *refstr;

{
	int			upper_2gb = 0;

	/*
	 * reads above 2Gb are done by seeking to offset%2Gb, and supplying 1
	 * (opposed to 0) as fourth parameter to readx (see 'man kmem')
	 */
	if (offset > 1 << 31)
	{
		upper_2gb = 1;
		offset &= 0x7fffffff;
	}

	if (lseek(kmem, offset, SEEK_SET) != offset)
	{
		fprintf(stderr, "pg_top: lseek failed\n");
		quit(2);
	}

	if (readx(kmem, ptr, size, upper_2gb) != size)
	{
		if (*refstr == '!')
			return 0;
		else
		{
			fprintf(stderr, "pg_top: kvm_read for %s: %s\n", refstr,
					sys_errlist[errno]);
			quit(2);
		}
	}

	return 1;
}
#endif

/* comparison routine for qsort */
/*
 * The following code is taken from the solaris module and adjusted
 * for AIX -- JV .
 */

#define ORDERKEY_PCTCPU \
		   if ((result = pi2->pi_cpu - pi1->pi_cpu) == 0)

#define ORDERKEY_CPTICKS \
		   if ((result = PROCTIME(pi2) - PROCTIME(pi1)) == 0)

#define ORDERKEY_STATE \
		   if ((result = sorted_state[pi2->pi_state]  \
						 - sorted_state[pi1->pi_state])  == 0)

/* Nice values directly reflect the process' priority, and are always >0 ;-) */
#define ORDERKEY_PRIO \
	   if ((result = pi1->pi_nice - pi2->pi_nice) == 0)
#define ORDERKEY_RSSIZE \
		   if ((result = PROCRESS(pi2) - PROCRESS(pi1)) == 0)
#define ORDERKEY_MEM \
		   if ((result = PROCSIZE(pi2) - PROCSIZE(pi1)) == 0)

static unsigned char sorted_state[] =
{
	0,							/* not used */
	0,
	0,
	0,
	3,							/* sleep */
	1,							/* zombie */
	4,							/* stop */
	6,							/* run */
	2,							/* swap */
};

/* compare_cpu - the comparison function for sorting by cpu percentage */

int
			compare_cpu(ppi1, ppi2)
struct procentry64 **ppi1;
struct procentry64 **ppi2;

{
	register struct procentry64 *pi1 = *ppi1,
			   *pi2 = *ppi2;
	register int result;

	ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_RSSIZE
		ORDERKEY_MEM
		;

	return result;
}


/* compare_size - the comparison function for sorting by total memory usage */

int
			compare_size(ppi1, ppi2)
struct procentry64 **ppi1;
struct procentry64 **ppi2;

{
	register struct procentry64 *pi1 = *ppi1,
			   *pi2 = *ppi2;
	register int result;

	ORDERKEY_MEM
		ORDERKEY_RSSIZE
		ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_STATE
		ORDERKEY_PRIO
		;

	return result;
}


/* compare_res - the comparison function for sorting by resident set size */

int
			compare_res(ppi1, ppi2)
struct procentry64 **ppi1;
struct procentry64 **ppi2;

{
	register struct procentry64 *pi1 = *ppi1,
			   *pi2 = *ppi2;
	register int result;

	ORDERKEY_RSSIZE
		ORDERKEY_MEM
		ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_STATE
		ORDERKEY_PRIO
		;

	return result;
}


/* compare_time - the comparison function for sorting by total cpu time */

int
			compare_time(ppi1, ppi2)
struct procentry64 **ppi1;
struct procentry64 **ppi2;

{
	register struct procentry64 *pi1 = *ppi1,
			   *pi2 = *ppi2;
	register int result;

	ORDERKEY_CPTICKS
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return result;
}


/* compare_prio - the comparison function for sorting by cpu percentage */

int
			compare_prio(ppi1, ppi2)
struct procentry64 **ppi1;
struct procentry64 **ppi2;

{
	register struct procentry64 *pi1 = *ppi1,
			   *pi2 = *ppi2;
	register int result;

	ORDERKEY_PRIO
		ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_STATE
		ORDERKEY_RSSIZE
		ORDERKEY_MEM
		;

	return result;
}


int
			proc_owner(pid)
int			pid;

{
	register struct procentry64 **prefp = pref;
	register int cnt = pref_len;

	while (--cnt >= 0)
	{
		if ((*prefp)->pi_pid == pid)
			return (*prefp)->pi_uid;
		prefp++;
	}

	return (-1);
}

void
get_io_info(struct io_info *io_info)
{
	/* Not supported yet */
	memset(io_info, 0, sizeof(*io_info));
}
07070100000024000081A400000000000000000000000166622DE8000011B9000000000000000000000000000000000000002000000000pg_top-4.1.1/machine/m_common.c/*
 * machine/m_common.c
 *
 * Functionalities common to all the platforms.
 *
 * Copyright (c) 2013 VMware, Inc. All Rights Reserved.
 */
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <libpq-fe.h>

#include "machine.h"

/* Query to fetch information about database activity */
#define QUERY_STAT_DB \
		"SELECT datid, datname, numbackends, xact_commit, xact_rollback, \n" \
		"       blks_read, blks_hit, tup_returned, tup_fetched, \n" \
		"       tup_inserted, tup_updated, tup_deleted, conflicts \n" \
		"FROM pg_stat_database;"

char	   *backendstatenames[] =
{
	"", "idle", "active", "idltxn", "fast", "abort", "disabl", NULL
};

char	   *procstatenames[] =
{
	" other background task(s), ", " idle, ", " active, ", " idle txn, ",
	" fastpath, ", " aborted, ", " disabled, ", NULL
};

char		fmt_header_replication[] =
"    PID USERNAME APPLICATION          CLIENT STATE     PRIMARY    SENT       WRITE      FLUSH      REPLAY      SLAG  WLAG  FLAG  RLAG";
/*
 * Get database info via the above QUERY_STAT_DB info.
 * Returns rate info on the various statistics by comparing current
 * values with previous values.
 */
void
get_database_info(struct db_info *db_info, struct pg_conninfo_ctx *conninfo)
{
	struct timeval thistime;
	double		timediff;
	int			i;
	int			rows;
	PGresult   *pgresult = NULL;
	struct db_info cur_info;
	static struct timeval lasttime;
	static struct db_info last_db_info;

	/* calculate the time difference since our last check */
	gettimeofday(&thistime, 0);
	if (lasttime.tv_sec)
		timediff = ((thistime.tv_sec - lasttime.tv_sec) +
					(thistime.tv_usec - lasttime.tv_usec) * 1e-6);
	else
		timediff = 0;

	lasttime = thistime;

	rows = 0;
	connect_to_db(conninfo);
	if (conninfo->connection != NULL)
	{
		pgresult = PQexec(conninfo->connection, QUERY_STAT_DB);
		if (PQresultStatus(pgresult) == PGRES_TUPLES_OK)
			rows = PQntuples(pgresult);

	}
	if (rows == 0)
	{
		/* Database probably stopped, clear current and last */
		memset(&last_db_info, 0, sizeof(last_db_info));
	}
	memset(&cur_info, 0, sizeof(cur_info));
	for (i = 0; i < rows; i++)
	{
		PQgetvalue(pgresult, i, 2);
		/* Count all databases, even with no active backends */
		cur_info.numDb++;
		cur_info.numXact += atoi(PQgetvalue(pgresult, i, 3));
		cur_info.numRollback += atoi(PQgetvalue(pgresult, i, 4));
		cur_info.numBlockRead += atoi(PQgetvalue(pgresult, i, 5));
		cur_info.numBlockHit += atoi(PQgetvalue(pgresult, i, 6));
		cur_info.numTupleFetched += atoi(PQgetvalue(pgresult, i, 8));
		cur_info.numTupleAltered += atoi(PQgetvalue(pgresult, i, 9)) +
			atoi(PQgetvalue(pgresult, i, 10)) +
			atoi(PQgetvalue(pgresult, i, 11));
		cur_info.numConflict += atoi(PQgetvalue(pgresult, i, 12));
	}
	if (pgresult != NULL)
		PQclear(pgresult);
	disconnect_from_db(conninfo);
	if (timediff <= 0)
	{
		last_db_info = cur_info;
		memset(db_info, 0, sizeof(*db_info));
		return;
	}

	/* Compute the rate information */
	db_info->numDb = cur_info.numDb;
	db_info->numXact = (double)(cur_info.numXact - last_db_info.numXact) / timediff;
	db_info->numRollback = (double)(cur_info.numRollback - last_db_info.numRollback) / timediff;
	db_info->numBlockRead = (double)(cur_info.numBlockRead - last_db_info.numBlockRead) / timediff;
	db_info->numBlockHit = (double)(cur_info.numBlockHit - last_db_info.numBlockHit) / timediff;
	db_info->numTupleFetched = (double)(cur_info.numTupleFetched - last_db_info.numTupleFetched) / timediff;
	db_info->numTupleAltered = (double)(cur_info.numTupleAltered - last_db_info.numTupleAltered) / timediff;
	db_info->numConflict = (double)(cur_info.numConflict - last_db_info.numConflict) / timediff;
	last_db_info = cur_info;
}

void
update_state(int *pgstate, char *state)
{
	/*
	 * pgstate is always cleared to 0 when the node is created, so it will be
	 * to STATE_UNDEFINED if there is no match when comparing the state
	 */
	if (strcmp(state, "idle") == 0)
		*pgstate = STATE_IDLE;
	else if (strcmp(state, "active") == 0)
		*pgstate = STATE_RUNNING;
	else if (strcmp(state, "idle in transaction") == 0)
		*pgstate = STATE_IDLEINTRANSACTION;
	else if (strcmp(state, "fastpath function call") == 0)
		*pgstate = STATE_FASTPATH;
	else if (strcmp(state, "idle in transaction (aborted)") == 0)
		*pgstate = STATE_IDLEINTRANSACTION_ABORTED;
	else if (strcmp(state, "disabled") == 0)
		*pgstate = STATE_DISABLED;
}

void
update_str(char **old, char *new)
{
	if (*old == NULL)
		*old = strdup(new);
	else if (strcmp(*old, new) != 0)
	{
		free(*old);
		*old = strdup(new);
	}
}
07070100000025000081A400000000000000000000000166622DE800007118000000000000000000000000000000000000002100000000pg_top-4.1.1/machine/m_freebsd.c/*
 * pg_top - a top PostgreSQL users display for Unix
 *
 * SYNOPSIS:  For FreeBSD-2.x, 3.x, 4.x, and 5.x
 *
 * DESCRIPTION:
 * Originally written for BSD4.4 system by Christos Zoulas.
 * Ported to FreeBSD 2.x by Steven Wallace && Wolfram Schneider
 * Order support hacked in from top-3.5beta6/machine/m_aix41.c
 *	 by Monte Mitzelfelt
 * Ported to FreeBSD 5.x by William LeFebvre
 *
 * AUTHOR:	Christos Zoulas <christos@ee.cornell.edu>
 *			Steven Wallace	<swallace@freebsd.org>
 *			Wolfram Schneider <wosch@FreeBSD.org>
 */


#include <sys/time.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <sys/param.h>
#include <err.h>

#include "config.h"
#include <stdio.h>
#include <string.h>
#include <nlist.h>
#include <math.h>
#include <kvm.h>
#include <pwd.h>
#include <sys/errno.h>
#include <sys/sysctl.h>
#include <sys/dkstat.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/vmmeter.h>
#include <sys/resource.h>
#include <sys/rtprio.h>
#include <sys/tree.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

/* Swap */
#include <stdlib.h>
#include <sys/conf.h>

#include <osreldate.h>			/* for changes in kernel structures */

#include "pg_top.h"
#include "machine.h"
#include "utils.h"

/* declarations for load_avg */
#include "loadavg.h"

#define GETSYSCTL(name, var) getsysctl(name, &(var), sizeof(var))

static int	getkval __P((unsigned long, int *, int, char *));
extern char *printable __P((char *));
static void getsysctl(const char *name, void *ptr, size_t len);
int			swapmode __P((int *retavail, int *retfree));

static int	maxcpu;
static int	maxid;
static int	ncpus;
static u_long cpumask;
static long *times;
static long *pcpu_cp_time;
static long *pcpu_cp_old;
static long *pcpu_cp_diff;
static int64_t * pcpu_cpu_states;

static int	smpmode;
static int	namelength;
static int	cmdlength;


/* get_process_info passes back a handle.  This is what it looks like: */

struct handle
{
	struct kinfo_proc **next_proc;	/* points to next valid proc pointer */
	int			remaining;		/* number of pointers remaining */
};

struct pg_proc
{
	RB_ENTRY(pg_proc) entry;
	pid_t		pid;

	/* This will be the previous copy of ki_rusage. */
    struct rusage ki_rusage;

	char *name;
	char *usename;
	int pgstate;
	unsigned long xtime;
	unsigned long qtime;
	unsigned int locks;

	/* Replication data */
	char	   *application_name;
	char	   *client_addr;
	char	   *repstate;
	char	   *primary;
	char	   *sent;
	char	   *write;
	char	   *flush;
	char	   *replay;
	long long	sent_lag;
	long long	write_lag;
	long long	flush_lag;
	long long	replay_lag;
};

int			topproccmp(struct pg_proc *, struct pg_proc *);

RB_HEAD(pgproc, pg_proc) head_proc = RB_INITIALIZER(&head_proc);
RB_PROTOTYPE(pgproc, pg_proc, entry, topproccmp)
RB_GENERATE(pgproc, pg_proc, entry, topproccmp)

/* macros to access process information */
#if OSMAJOR <= 4
#define PP(pp, field) ((pp)->kp_proc . p_##field)
#define EP(pp, field) ((pp)->kp_eproc . e_##field)
#define VP(pp, field) ((pp)->kp_eproc.e_vm . vm_##field)
#define PRUID(pp) ((pp)->kp_eproc.e_pcred.p_ruid)
#else
#define PP(pp, field) ((pp)->ki_##field)
#define VP(pp, field) ((pp)->ki_##field)
#define PRUID(pp) ((pp)->ki_ruid)
#endif
#define RU(pp)	(&(pp)->ki_rusage)

/* what we consider to be process size: */
#if OSMAJOR <= 4
#define PROCSIZE(pp) (VP((pp), map.size) / 1024)
#else
#define PROCSIZE(pp) (((pp)->ki_size) / 1024)
#endif

/* for 5.x and higher we show thread count */
#if OSMAJOR >= 5
#define SHOW_THREADS
#endif

/* definitions for indices in the nlist array */

static struct nlist nlst[] = {
#define X_CCPU		0
	{"_ccpu"},
#define X_CP_TIME	1
	{"_cp_time"},
#define X_AVENRUN	2
	{"_averunnable"},

#define X_BUFSPACE	3
	{"_bufspace"},				/* K in buffer cache */
#define X_CNT			4
	{"_cnt"},					/* struct vmmeter cnt */

/* Last pid */
#define X_LASTPID	5
	{"_nextpid"},

#define X_BOOTTIME	6
	{"_boottime"},

	{0}
};

/*
 *	These definitions control the format of the per-process area
 */

static char header[] =
"  PID %-*.*s    SIZE    RES STATE   XTIME  QTIME    CPU LOCKS COMMAND";

/* process state names for the "STATE" column of the display */
/* the extra nulls in the string "run" are for adding a slash and
   the processor number when needed */

char	   *state_abbrev[] =
{
	"", "START", "RUN\0\0\0", "SLEEP", "STOP", "ZOMB",
};


char fmt_header_io[] =
		"PID   USERNAME     VCSW  IVCSW   READ  WRITE  FAULT  TOTAL COMMAND";

static kvm_t * kd;

/* values that we stash away in _init and use in later routines */

static double logcpu;

/* these are retrieved from the kernel in _init */

static load_avg ccpu;

/* these are offsets obtained via nlist and used in the get_ functions */

static unsigned long cp_time_offset;
static unsigned long avenrun_offset;
static unsigned long lastpid_offset;
static int	lastpid;
static unsigned long cnt_offset;
static unsigned long bufspace_offset;

/* these are for calculating cpu state percentages */

static int64_t cp_time[CPUSTATES];
static int64_t cp_old[CPUSTATES];
static int64_t cp_diff[CPUSTATES];

/* these are for detailing the process states */

int			process_states[6];

/* these are for detailing the cpu states */

int64_t		cpu_states[CPUSTATES];
char	   *cpustatenames[] = {
	"user", "nice", "system", "interrupt", "idle", NULL
};

/* these are for detailing the memory statistics */

long		memory_stats[7];
char	   *memorynames[] = {
	"K Active, ", "K Inact, ", "K Wired, ", "K Cache, ", "K Buf, ", "K Free",
	NULL
};

long		swap_stats[7];
char	   *swapnames[] = {
/*	 0			 1			  2			  3			   4	   5 */
	"K Total, ", "K Used, ", "K Free, ", "% Inuse, ", "K In, ", "K Out",
	NULL
};


/* these are for keeping track of the proc array */

static int	nproc;
static int	onproc = -1;
static int	pref_len;
static struct kinfo_proc *pbase;
static struct kinfo_proc **pref;

/* these are for getting the memory statistics */

static int	pageshift;			/* log base 2 of the pagesize */

/* define pagetok in terms of pageshift */

#define pagetok(size) ((size) << pageshift)

/* useful externals */
long		percentages();

/* sorting orders. first is default */
char	   *ordernames[] = {"cpu", "size", "res", "time", "pri", NULL};

/* compare routines */
int			proc_compare(), compare_size(), compare_res(), compare_time(), compare_prio();

int			(*proc_compares[]) () =
{
	proc_compare,
		compare_size,
		compare_res,
		compare_time,
		compare_prio,
		NULL
};

int
machine_init(struct statics *statics)

{
	register int pagesize;
	size_t		size;
	struct passwd *pw;
	int			i,
				j,
				empty;

	size = sizeof(smpmode);
	if ((sysctlbyname("machdep.smp_active", &smpmode, &size, NULL, 0) != 0 &&
		 sysctlbyname("smp.smp_active", &smpmode, &size, NULL, 0) != 0) ||
		size != sizeof(smpmode))
		smpmode = 0;

	while ((pw = getpwent()) != NULL)
	{
		if (strlen(pw->pw_name) > namelength)
			namelength = strlen(pw->pw_name);
	}
	if (namelength < 8)
		namelength = 8;
	if (smpmode && namelength > 13)
		namelength = 13;
	else if (namelength > 15)
		namelength = 15;

	/*
	 * Silence kvm_open in the event that the pid from the database is gone
	 * before we ask the operating system about it.
	 */
	if ((kd = kvm_open(NULL, "/dev/null", NULL, O_RDONLY, NULL)) == NULL)
		return -1;

	/* get number of cpus */
	GETSYSCTL("kern.ccpu", ccpu);

	/* stash away certain offsets for later use */
	cp_time_offset = nlst[X_CP_TIME].n_value;
	avenrun_offset = nlst[X_AVENRUN].n_value;
	lastpid_offset = nlst[X_LASTPID].n_value;
	cnt_offset = nlst[X_CNT].n_value;
	bufspace_offset = nlst[X_BUFSPACE].n_value;

	/* this is used in calculating WCPU -- calculate it ahead of time */
	logcpu = log(loaddouble(ccpu));

	pbase = NULL;
	pref = NULL;
	nproc = 0;
	onproc = -1;

	/* get the page size with "getpagesize" and calculate pageshift from it */
	pagesize = getpagesize();
	pageshift = 0;
	while (pagesize > 1)
	{
		pageshift++;
		pagesize >>= 1;
	}

	/* we only need the amount of log(2)1024 for our conversion */
	pageshift -= LOG1024;

	/* fill in the statics information */
	statics->procstate_names = procstatenames;
	statics->cpustate_names = cpustatenames;
	statics->memory_names = memorynames;
	statics->swap_names = swapnames;
	statics->order_names = ordernames;
	statics->flags.fullcmds = 1;

	/* Allocate state for per-CPU stats. */
	cpumask = 0;
	ncpus = 0;
	GETSYSCTL("kern.smp.maxcpus", maxcpu);
	size = sizeof(long) * maxcpu * CPUSTATES;
	times = malloc(size);
	if (times == NULL)
		err(1, "malloc %zd bytes", size);
	if (sysctlbyname("kern.cp_times", times, &size, NULL, 0) == -1)
		err(1, "sysctlbyname kern.cp_times");
	pcpu_cp_time = calloc(1, size);
	maxid = (size / CPUSTATES / sizeof(long)) - 1;
	for (i = 0; i <= maxid; i++)
	{
		empty = 1;
		for (j = 0; empty && j < CPUSTATES; j++)
		{
			if (times[i * CPUSTATES + j] != 0)
				empty = 0;
		}
		if (!empty)
		{
			cpumask |= (1ul << i);
			ncpus++;
		}
	}
	size = sizeof(long) * ncpus * CPUSTATES;
	pcpu_cp_old = calloc(1, size);
	pcpu_cp_diff = calloc(1, size);
	pcpu_cpu_states = calloc(1, size);
	statics->ncpus = ncpus;

	/* all done! */
	return (0);
}

char *
format_header(char *uname_field)

{
	static char Header[128];

	snprintf(Header, sizeof(Header), header, namelength, namelength,
			uname_field);

	cmdlength = 80 - strlen(Header) + 6;

	return Header;
}

static int	swappgsin = -1;
static int	swappgsout = -1;
extern struct timeval timeout;

void
get_system_info(struct system_info *si)

{
	struct loadavg sysload;
	size_t		size;
	int			i,
				j;

	/* get the CPU stats */
	size = (maxid + 1) * CPUSTATES * sizeof(long);
	if (sysctlbyname("kern.cp_times", pcpu_cp_time, &size, NULL, 0) == -1)
		err(1, "sysctlbyname kern.cp_times");
	GETSYSCTL("kern.cp_time", cp_time);
	GETSYSCTL("vm.loadavg", sysload);
	GETSYSCTL("kern.lastpid", lastpid);

	/* convert load averages to doubles */
	for (i = 0; i < 3; i++)
		si->load_avg[i] = (double) sysload.ldavg[i] / sysload.fscale;

	/* convert cp_time counts to percentages */
	for (i = j = 0; i <= maxid; i++)
	{
		if ((cpumask & (1ul << i)) == 0)
			continue;
		percentages(CPUSTATES, &pcpu_cpu_states[j * CPUSTATES],
					&pcpu_cp_time[j * CPUSTATES],
					&pcpu_cp_old[j * CPUSTATES],
					&pcpu_cp_diff[j * CPUSTATES]);
		j++;
	}
	percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);

	/* sum memory & swap statistics */
	{
		static unsigned int swap_delay = 0;
		static int	swapavail = 0;
		static int	swapfree = 0;
		static int	bufspace = 0;
		static int	nspgsin,
					nspgsout;

		/*
		 * Use this temporary int array because we use longs for the other
		 * patforms.
		 */
		int			tmp_memory_stats[7];

		GETSYSCTL("vfs.bufspace", bufspace);
		GETSYSCTL("vm.stats.vm.v_active_count", tmp_memory_stats[0]);
		GETSYSCTL("vm.stats.vm.v_inactive_count", tmp_memory_stats[1]);
		GETSYSCTL("vm.stats.vm.v_wire_count", tmp_memory_stats[2]);
		GETSYSCTL("vm.stats.vm.v_cache_count", tmp_memory_stats[3]);
		GETSYSCTL("vm.stats.vm.v_free_count", tmp_memory_stats[5]);
		GETSYSCTL("vm.stats.vm.v_swappgsin", nspgsin);
		GETSYSCTL("vm.stats.vm.v_swappgsout", nspgsout);

		/* convert memory stats to Kbytes */
		memory_stats[0] = pagetok(tmp_memory_stats[0]);
		memory_stats[1] = pagetok(tmp_memory_stats[1]);
		memory_stats[2] = pagetok(tmp_memory_stats[2]);
		memory_stats[3] = pagetok(tmp_memory_stats[3]);
		memory_stats[4] = bufspace / 1024;
		memory_stats[5] = pagetok(tmp_memory_stats[5]);
		memory_stats[6] = -1;

		/* first interval */
		if (swappgsin < 0)
		{
			swap_stats[4] = 0;
			swap_stats[5] = 0;
		}

		/* compute differences between old and new swap statistic */
		else
		{
			swap_stats[4] = pagetok(((nspgsin - swappgsin)));
			swap_stats[5] = pagetok(((nspgsout - swappgsout)));
		}

		swappgsin = nspgsin;
		swappgsout = nspgsout;

		/* call CPU heavy swapmode() only for changes */
		if (swap_stats[4] > 0 || swap_stats[5] > 0 || swap_delay == 0)
		{
			swap_stats[3] = swapmode(&swapavail, &swapfree);
			swap_stats[0] = swapavail;
			swap_stats[1] = swapavail - swapfree;
			swap_stats[2] = swapfree;
		}
		swap_delay = 1;
		swap_stats[6] = -1;
	}

	/* set arrays and strings */
	si->cpustates = cpu_states;
	si->memory = memory_stats;
	si->swap = swap_stats;

	if (lastpid > 0)
	{
		si->last_pid = lastpid;
	}
	else
	{
		si->last_pid = -1;
	}

}

static struct handle handle;
static int	show_fullcmd;

caddr_t
get_process_info(struct system_info *si,
				 struct process_select *sel,
				 int compare_index,
				 struct pg_conninfo_ctx *conninfo,
				 int mode)

{
	register int i;
	register int total_procs;
	register int active_procs;
	register struct kinfo_proc **prefp;
	register struct kinfo_proc *pp;

	/* these are copied out of sel for speed */
	int			show_idle;
	int			show_self;
	int			show_system = 0;

	PGresult   *pgresult = NULL;
	struct pg_proc *n, *p;

	nproc = 0;
	connect_to_db(conninfo);
	if (conninfo->connection != NULL)
	{
		if (mode == MODE_REPLICATION)
		{
			pgresult = pg_replication(conninfo->connection);
		}
		else
		{
			pgresult = pg_processes(conninfo->connection);
		}
		nproc = PQntuples(pgresult);
		if (nproc > onproc)
			pbase = (struct kinfo_proc *)
				realloc(pbase, sizeof(struct kinfo_proc) * nproc);

		pgresult = pg_processes(conninfo->connection);
	}

	if (nproc > onproc)
		pref = (struct kinfo_proc **) realloc(pref, sizeof(struct kinfo_proc *)
											  * (onproc = nproc));
	if (pref == NULL)
	{
		(void) fprintf(stderr, "pg_top: Out of memory.\n");
		quit(23);
	}
	/* get a pointer to the states summary array */
	si->procstates = process_states;

	/* set up flags which define what we are going to select */
	show_idle = sel->idle;
	show_self = 0;
	show_fullcmd = sel->fullcmd;

	/* count up process states and get pointers to interesting procs */
	total_procs = 0;
	active_procs = 0;
	memset((char *) process_states, 0, sizeof(process_states));
	prefp = pref;
	for (pp = pbase, i = 0; i < nproc; pp++, i++)
	{
		struct kinfo_proc *junk2;
		int			junk;

		junk2 = kvm_getprocs(kd, KERN_PROC_PID,
							 atoi(PQgetvalue(pgresult, i, 0)), &junk);
		if (junk2 == NULL)
		{
			continue;
		}

		/*
		 * FIXME: This memcpy is so not elegant and the reason why I'm doing
		 * it...
		 */
		memcpy(&pbase[i], &junk2[0], sizeof(struct kinfo_proc));

		/*
		 * Place pointers to each valid proc structure in pref[]. Process
		 * slots that are actually in use have a non-zero status field.
		 * Processes with P_SYSTEM set are system processes---these get
		 * ignored unless show_sysprocs is set.
		 */
		if (PP(pp, stat) != 0 &&
			(show_self != PP(pp, pid)) &&
			(show_system || ((PP(pp, flag) & P_SYSTEM) == 0)))
		{
			total_procs++;
			process_states[(unsigned char) PP(pp, stat)]++;
			if ((PP(pp, stat) != SZOMB) &&
				(show_idle || (PP(pp, pctcpu) != 0) ||
				 (PP(pp, stat) == SRUN)))
			{
				*prefp++ = pp;
				active_procs++;
			}
		}

		n = malloc(sizeof(struct pg_proc));
		if (n == NULL)
		{
			fprintf(stderr, "malloc error\n");
			if (pgresult != NULL)
				PQclear(pgresult);
			disconnect_from_db(conninfo);
			exit(1);
		}
		memset(n, 0, sizeof(struct pg_proc));
		n->pid = atoi(PQgetvalue(pgresult, i, 0));
		p = RB_INSERT(pgproc, &head_proc, n);
		if (p != NULL)
		{
			free(n);
			n = p;
			memcpy(RU(n), RU(&junk2[0]), sizeof(struct rusage));
		}

		if (mode == MODE_REPLICATION)
		{
			update_str(&n->usename, PQgetvalue(pgresult, i, REP_USENAME));
			update_str(&n->application_name,
					PQgetvalue(pgresult, i, REP_APPLICATION_NAME));
			update_str(&n->client_addr,
					PQgetvalue(pgresult, i, REP_CLIENT_ADDR));
			update_str(&n->repstate, PQgetvalue(pgresult, i, REP_STATE));
			update_str(&n->primary,
					PQgetvalue(pgresult, i, REP_WAL_INSERT));
			update_str(&n->sent, PQgetvalue(pgresult, i, REP_SENT));
			update_str(&n->write, PQgetvalue(pgresult, i, REP_WRITE));
			update_str(&n->flush, PQgetvalue(pgresult, i, REP_FLUSH));
			update_str(&n->replay, PQgetvalue(pgresult, i, REP_REPLAY));
			n->sent_lag = atol(PQgetvalue(pgresult, i, REP_SENT_LAG));
			n->write_lag = atol(PQgetvalue(pgresult, i, REP_WRITE_LAG));
			n->flush_lag = atol(PQgetvalue(pgresult, i, REP_FLUSH_LAG));
			n->replay_lag = atol(PQgetvalue(pgresult, i, REP_REPLAY_LAG));
		}
		else
		{
			update_str(&n->name, PQgetvalue(pgresult, i, PROC_QUERY));
			printable(n->name);
			update_state(&n->pgstate, PQgetvalue(pgresult, i, PROC_STATE));
			update_str(&n->usename, PQgetvalue(pgresult, i, PROC_USENAME));
			n->xtime = atol(PQgetvalue(pgresult, i, PROC_XSTART));
			n->qtime = atol(PQgetvalue(pgresult, i, PROC_QSTART));
			n->locks = atoi(PQgetvalue(pgresult, i, PROC_LOCKS));
		}
	}

	if (pgresult != NULL)
		PQclear(pgresult);
	disconnect_from_db(conninfo);

	/* if requested, sort the "interesting" processes */
	if (compare_index >= 0 && active_procs)
		qsort((char *) pref, active_procs, sizeof(struct kinfo_proc *),
		  		proc_compares[compare_index]);

	/* remember active and total counts */
	si->p_total = total_procs;
	si->p_active = pref_len = active_procs;

	/* pass back a handle */
	handle.next_proc = pref;
	handle.remaining = active_procs;
	return ((caddr_t) & handle);
}

char		fmt[MAX_COLS];		/* static area where result is built */
char		cmd[MAX_COLS];

char *
format_next_io(caddr_t handle)
{
	register struct kinfo_proc *pp;
	struct handle *hp;
	struct pg_proc n, *p = NULL;

	/* find and remember the next proc structure */
	hp = (struct handle *) handle;
	pp = *(hp->next_proc++);
	hp->remaining--;

	memset(&n, 0, sizeof(struct pg_proc));
	n.pid = PP(pp, pid);
	p = RB_FIND(pgproc, &head_proc, &n);

	snprintf(fmt, sizeof(fmt),
			"%5d %-*.*s %6ld %6ld %6ld %6ld %6ld %6ld %s",
			PP(pp, pid),
			namelength, namelength,
			p->usename,
			RU(pp)->ru_nvcsw - RU(p)->ru_nvcsw,
			RU(pp)->ru_nivcsw - RU(p)->ru_nivcsw,
			RU(pp)->ru_inblock - RU(p)->ru_inblock,
			RU(pp)->ru_oublock - RU(p)->ru_oublock,
			RU(pp)->ru_majflt - RU(p)->ru_majflt,
			(RU(pp)->ru_inblock - RU(p)->ru_inblock) +
					(RU(pp)->ru_oublock - RU(p)->ru_oublock) +
					(RU(pp)->ru_majflt - RU(p)->ru_majflt),
			p->name);

	return fmt;
}

char *
format_next_process(caddr_t handle)

{
	register struct kinfo_proc *pp;
	register long cputime;
	register double pct;
	struct handle *hp;
	char		status[16];
	int			state;

	struct pg_proc n, *pr = NULL;

	/* find and remember the next proc structure */
	hp = (struct handle *) handle;
	pp = *(hp->next_proc++);
	hp->remaining--;

	/* get the process's command name in to "cmd" */
	if (show_fullcmd)
	{
		struct pargs pargs;
		int			len;

		/* get the pargs structure */
		getkval((unsigned long) PP(pp, args), (int *) &pargs,
				sizeof(pargs), "!pargs");

		/* determine workable length */
		if ((len = pargs.ar_length) >= MAX_COLS)
		{
			len = MAX_COLS - 1;
		}

		/* get the string from that */
		getkval((unsigned long) PP(pp, args) +
				sizeof(pargs.ar_ref) +
				sizeof(pargs.ar_length),
				(int *) cmd, len, "!cmdline");
	}
#if OSMAJOR <= 4
	else if ((PP(pp, flag) & P_INMEM) == 0)
#else
	else if ((PP(pp, sflag) & PS_INMEM) == 0)
#endif
	{
		/* Print swapped processes as <pname> */
		char	   *p;

		cmd[0] = '<';
		p = strecpy(cmd + 1, PP(pp, comm));
		*p++ = '>';
		*p = '\0';
	}
	else
	{
		/* take it straight out of p_comm */
		strncpy(cmd, PP(pp, comm), MAX_COLS - 1);
	}

	/*
	 * Convert the process's runtime from microseconds to seconds.  This time
	 * includes the interrupt time although that is not wanted here. ps(1) is
	 * similarly sloppy.
	 */
	cputime = (PP(pp, runtime) + 500000) / 1000000;

	/* calculate the base for cpu percentages */
	pct = pctdouble(PP(pp, pctcpu));

	/* generate "STATE" field */
	switch (state = PP(pp, stat))
	{
		case SRUN:
			if (smpmode && PP(pp, oncpu) != 0xff)
				snprintf(status, sizeof(status), "CPU%d", PP(pp, oncpu));
			else
				strcpy(status, "RUN");
			break;
		case SSLEEP:
			if (PP(pp, wmesg) != NULL)
			{
#if OSMAJOR <= 4
				snprintf(status, sizeof(status), "%.6s", EP(pp, wmesg));
#else
				snprintf(status, sizeof(status), "%.6s", PP(pp, wmesg));
#endif
				break;
			}
			/* fall through */
		default:

			if (state >= 0 &&
				state < sizeof(state_abbrev) / sizeof(*state_abbrev))
				snprintf(status, sizeof(status), "%.6s", state_abbrev[(unsigned char) state]);
			else
				snprintf(status, sizeof(status), "?%5d", state);
			break;
	}

	memset(&n, 0, sizeof(struct pg_proc));
	n.pid = PP(pp, pid);
	pr = RB_FIND(pgproc, &head_proc, &n);

	/* format this entry */
	snprintf(fmt, sizeof(fmt),
			"%5d %-*.*s %7s %6s %-6.6s %5s %5s %5.2f%% %5d %s",
			PP(pp, pid),
			namelength, namelength,
			pr->usename,
			format_k(PROCSIZE(pp)),
			format_k(pagetok(VP(pp, rssize))),
			backendstatenames[pr->pgstate],
			format_time(pr->xtime),
			format_time(pr->qtime),
			100.0 * pct,
			pr->locks,
			pr->name);

	/* return the result */
	return (fmt);
}

char *
format_next_replication(caddr_t handle)
{
	static char fmt[MAX_COLS];	/* static area where result is built */
	register struct kinfo_proc *pp;
	struct handle *hp;
	struct pg_proc n, *p = NULL;

	/* find and remember the next proc structure */
	hp = (struct handle *) handle;
	pp = *(hp->next_proc++);
	hp->remaining--;

	memset(&n, 0, sizeof(struct pg_proc));
	n.pid = PP(pp, pid);
	p = RB_FIND(pgproc, &head_proc, &n);

	snprintf(fmt, sizeof(fmt),
			 "%7d %-8.8s %-11.11s %15s %-9.9s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %5s %5s %5s %5s",
			 p->pid,
			 p->usename,
			 p->application_name,
			 p->client_addr,
			 p->repstate,
			 p->primary,
			 p->sent,
			 p->write,
			 p->flush,
			 p->replay,
			 format_b(p->sent_lag),
			 format_b(p->write_lag),
			 format_b(p->flush_lag),
			 format_b(p->replay_lag));

	/* return the result */
	return (fmt);
}

/*
 *	getkval(offset, ptr, size, refstr) - get a value out of the kernel.
 *	"offset" is the byte offset into the kernel for the desired value,
 *		"ptr" points to a buffer into which the value is retrieved,
 *		"size" is the size of the buffer (and the object to retrieve),
 *		"refstr" is a reference string used when printing error meessages,
 *		if "refstr" starts with a '!', then a failure on read will not
 *			be fatal (this may seem like a silly way to do things, but I
 *			really didn't want the overhead of another argument).
 *
 */

static int
getkval(unsigned long offset, int *ptr, int size, char *refstr)

{
	if (kvm_read(kd, offset, (char *) ptr, size) != size)
	{
		if (*refstr == '!')
		{
			return (0);
		}
		else
		{
			fprintf(stderr, "pg_top: kvm_read for %s: %s\n",
					refstr, strerror(errno));
			quit(23);
		}
	}
	return (1);
}

/* comparison routines for qsort */

/*
 *	proc_compare - comparison function for "qsort"
 *	Compares the resource consumption of two processes using five
 *		distinct keys.	The keys (in descending order of importance) are:
 *		percent cpu, cpu ticks, state, resident set size, total virtual
 *		memory usage.  The process states are ordered as follows (from least
 *		to most important):  WAIT, zombie, sleep, stop, start, run.  The
 *		array declaration below maps a process state index into a number
 *		that reflects this ordering.
 */

static unsigned char sorted_state[] =
{
	0,							/* not used		*/
	3,							/* sleep		*/
	1,							/* ABANDONED (WAIT) */
	6,							/* run			*/
	5,							/* start		*/
	2,							/* zombie		*/
	4							/* stop			*/
};


#define ORDERKEY_PCTCPU \
  if (lresult = (long) PP(p2, pctcpu) - (long) PP(p1, pctcpu), \
	 (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0)

#define ORDERKEY_CPTICKS \
  if ((result = PP(p2, runtime) > PP(p1, runtime) ? 1 : \
				PP(p2, runtime) < PP(p1, runtime) ? -1 : 0) == 0)

#define ORDERKEY_STATE \
  if ((result = sorted_state[(unsigned char) PP(p2, stat)] - \
				sorted_state[(unsigned char) PP(p1, stat)]) == 0)

#if OSMAJOR <= 4
#define ORDERKEY_PRIO \
  if ((result = PP(p2, priority) - PP(p1, priority)) == 0)
#else
#define ORDERKEY_PRIO \
  if ((result = PP(p2, pri.pri_user) - PP(p1, pri.pri_user)) == 0)
#endif

#define ORDERKEY_RSSIZE \
  if ((result = VP(p2, rssize) - VP(p1, rssize)) == 0)

#define ORDERKEY_MEM \
  if ( (result = PROCSIZE(p2) - PROCSIZE(p1)) == 0 )

/* compare_cpu - the comparison function for sorting by cpu percentage */

int
proc_compare(struct proc **pp1, struct proc **pp2)

{
	register struct kinfo_proc *p1;
	register struct kinfo_proc *p2;
	register int result;
	register pctcpu lresult;

	/* remove one level of indirection */
	p1 = *(struct kinfo_proc **) pp1;
	p2 = *(struct kinfo_proc **) pp2;

	ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_RSSIZE
		ORDERKEY_MEM
		;

	return (result);
}

/* compare_size - the comparison function for sorting by total memory usage */

int
compare_size(struct proc **pp1, struct proc **pp2)

{
	register struct kinfo_proc *p1;
	register struct kinfo_proc *p2;
	register int result;
	register pctcpu lresult;

	/* remove one level of indirection */
	p1 = *(struct kinfo_proc **) pp1;
	p2 = *(struct kinfo_proc **) pp2;

	ORDERKEY_MEM
		ORDERKEY_RSSIZE
		ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_STATE
		ORDERKEY_PRIO
		;

	return (result);
}

/* compare_res - the comparison function for sorting by resident set size */

int
compare_res(struct proc **pp1, struct proc **pp2)

{
	register struct kinfo_proc *p1;
	register struct kinfo_proc *p2;
	register int result;
	register pctcpu lresult;

	/* remove one level of indirection */
	p1 = *(struct kinfo_proc **) pp1;
	p2 = *(struct kinfo_proc **) pp2;

	ORDERKEY_RSSIZE
		ORDERKEY_MEM
		ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_STATE
		ORDERKEY_PRIO
		;

	return (result);
}

/* compare_time - the comparison function for sorting by total cpu time */

int
compare_time(struct proc **pp1, struct proc **pp2)

{
	register struct kinfo_proc *p1;
	register struct kinfo_proc *p2;
	register int result;
	register pctcpu lresult;

	/* remove one level of indirection */
	p1 = *(struct kinfo_proc **) pp1;
	p2 = *(struct kinfo_proc **) pp2;

	ORDERKEY_CPTICKS
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_RSSIZE
		ORDERKEY_MEM
		;

	return (result);
}

/* compare_prio - the comparison function for sorting by cpu percentage */

int
compare_prio(struct proc **pp1, struct proc **pp2)

{
	register struct kinfo_proc *p1;
	register struct kinfo_proc *p2;
	register int result;
	register pctcpu lresult;

	/* remove one level of indirection */
	p1 = *(struct kinfo_proc **) pp1;
	p2 = *(struct kinfo_proc **) pp2;

	ORDERKEY_PRIO
		ORDERKEY_CPTICKS
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_RSSIZE
		ORDERKEY_MEM
		;

	return (result);
}

/*
 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
 *		the process does not exist.
 *		It is EXTREMLY IMPORTANT that this function work correctly.
 *		If pg_top runs setuid root (as in SVR4), then this function
 *		is the only thing that stands in the way of a serious
 *		security problem.  It validates requests for the "kill"
 *		and "renice" commands.
 */

uid_t
proc_owner(pid_t pid)

{
	register int cnt;
	register struct kinfo_proc **prefp;
	register struct kinfo_proc *pp;

	prefp = pref;
	cnt = pref_len;
	while (--cnt >= 0)
	{
		pp = *prefp++;
		if (PP(pp, pid) == (pid_t) pid)
		{
			return ((int) PRUID(pp));
		}
	}
	return (-1);
}


static void
getsysctl(const char *name, void *ptr, size_t len)
{
	size_t		nlen = len;

	if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1)
	{
		fprintf(stderr, "pg_top: sysctl(%s...) failed: %s\n", name,
				strerror(errno));
		quit(23);
	}
	if (nlen != len)
	{
		fprintf(stderr, "pg_top: sysctl(%s...) expected %lu, got %lu\n",
				name, (unsigned long) len, (unsigned long) nlen);
		quit(23);
	}
}

/*
 * swapmode is based on a program called swapinfo written
 * by Kevin Lahey <kml@rokkaku.atl.ga.us>.
 */

#define SVAR(var) __STRING(var) /* to force expansion */
#define KGET(idx, var)							\
	KGET1(idx, &var, sizeof(var), SVAR(var))
#define KGET1(idx, p, s, msg)						\
	KGET2(nlst[idx].n_value, p, s, msg)
#define KGET2(addr, p, s, msg)						\
	if (kvm_read(kd, (u_long)(addr), p, s) != s) {				\
		warnx("cannot read %s: %s", msg, kvm_geterr(kd));		\
		return (0);												\
	   }
#define KGETRET(addr, p, s, msg)					\
	if (kvm_read(kd, (u_long)(addr), p, s) != s) {			\
		warnx("cannot read %s: %s", msg, kvm_geterr(kd));	\
		return (0);						\
	}


int
swapmode(int *retavail, int *retfree)

{
	int			n;
	int			pagesize = getpagesize();
	struct kvm_swap swapary[1];

	*retavail = 0;
	*retfree = 0;

#define CONVERT(v)	((quad_t)(v) * pagesize / 1024)

	n = kvm_getswapinfo(kd, swapary, 1, 0);
	if (n < 0 || swapary[0].ksw_total == 0)
		return (0);

	*retavail = CONVERT(swapary[0].ksw_total);
	*retfree = CONVERT(swapary[0].ksw_total - swapary[0].ksw_used);

	n = (int) ((double) swapary[0].ksw_used * 100.0 /
			   (double) swapary[0].ksw_total);
	return (n);
}

int
topproccmp(struct pg_proc *e1, struct pg_proc *e2)
{
	return (e1->pid < e2->pid ? -1 : e1->pid > e2->pid);
}
void

get_io_info(struct io_info *io_info)
{
	/* Not supported yet */
	memset(io_info, 0, sizeof(*io_info));
}
07070100000026000081A400000000000000000000000166622DE8000002CF000000000000000000000000000000000000002700000000pg_top-4.1.1/machine/m_freebsd.man.rstFreeBSD 4.0 NOTES
=================

Last pid is compiler depended. 

$ strings /kernel | grep _nextpid

DESCRIPTION OF MEMORY
=====================

Memory: 10M Act 1208K Inact 3220K Wired 132K Free 25% Swap, 2924Kin 2604Kout

:K:: Kilobyte
:M:: Megabyte
:%:: 1/100
:Act:: number of pages active
:Incat:: number of pages inactive
:Wired:: number of pages wired down
:Free:: number of pages free
:Swap:: swap usage
:Kin:: kilobytes swap pager pages paged in (last interval)
:Kout:: kilobytes swap pager pages paged out  (last interval)

See /usr/include/sys/vmmeter.h and  /sys/vm/vm_meter.c.

Christos Zoulas, Steven Wallace, Wolfram Schneider, Monte Mitzelfelt.

This module was retrofitted from FreeBSD 9.1 sources.
07070100000027000081A400000000000000000000000166622DE80000823A000000000000000000000000000000000000001F00000000pg_top-4.1.1/machine/m_linux.c/*
 * pg_top - a top PostgreSQL users display for Unix
 *
 * SYNOPSIS:  Linux 1.2.x, 1.3.x, 2.x, using the /proc filesystem
 *
 * DESCRIPTION:
 * This is the machine-dependent module for Linux 1.2.x, 1.3.x or 2.x.
 *
 * LIBS:
 *
 * CFLAGS: -DHAVE_GETOPT -DHAVE_STRERROR -DORDER
 *
 * TERMCAP: -lcurses
 *
 * AUTHOR: Richard Henderson <rth@tamu.edu>
 * Order support added by Alexey Klimkin <kad@klon.tme.mcst.ru>
 * Ported to 2.4 by William LeFebvre
 *
 * Portions Copyright (c) 2013 VMware, Inc. All Rights Reserved.
 */

#define _GNU_SOURCE

#include "config.h"

#include <sys/types.h>
#include <time.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <bsd/stdlib.h>
#include <bsd/sys/tree.h>
#include <errno.h>
#include <dirent.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/vfs.h>

#include <sys/param.h>			/* for HZ */

#if 0
#include <linux/proc_fs.h>		/* for PROC_SUPER_MAGIC */
#else
#define PROC_SUPER_MAGIC 0x9fa0
#endif

#define BUFFERLEN 255
#define GET_VALUE(v) \
		p = strchr(p, ':'); \
		++p; \
		++p; \
		q = strchr(p, '\n'); \
		len = q - p; \
		if (len >= BUFFERLEN) \
		{ \
			printf("ERROR - value is larger than the buffer: %d\n", __LINE__); \
			exit(1); \
		} \
		strncpy(value, p, len); \
		value[len] = '\0'; \
		v = atoll(value);

#include "machine.h"
#include "utils.h"

#define PROCFS "/proc"
extern char *myname;

/*=PROCESS INFORMATION==================================================*/

struct top_proc
{
	RB_ENTRY(top_proc) entry;
	pid_t		pid;

	/* index for which element is current in data arrays */
	int index;

	/* Data from /proc/<pid>/stat. */
	char	   *name;
	char	   *usename;
	unsigned long size,
				rss;			/* in k */
	int			state;
	int			pgstate;
	unsigned long time;
	unsigned long start_time;
	unsigned long xtime;
	unsigned long qtime;
	unsigned int locks;
	double		pcpu;

	/* Data from /proc/<pid>/io. */
	long long	iops[2]; /* syscr + syscw */
	long long	syscr[2];
	long long	syscw[2];
	long long	read_bytes[2];
	long long	write_bytes[2];

	/* Replication data */
	char	   *application_name;
	char	   *client_addr;
	char	   *repstate;
	char	   *primary;
	char	   *sent;
	char	   *write;
	char	   *flush;
	char	   *replay;
	long long	sent_lag;
	long long	write_lag;
	long long	flush_lag;
	long long	replay_lag;
};

int			topproccmp(struct top_proc *, struct top_proc *);

RB_HEAD(pgproc, top_proc) head_proc = RB_INITIALIZER(&head_proc);
RB_PROTOTYPE(pgproc, top_proc, entry, topproccmp)
RB_GENERATE(pgproc, top_proc, entry, topproccmp)

double		timediff;

/*=STATE IDENT STRINGS==================================================*/

#define NCPUSTATES 5
static char *cpustatenames[NCPUSTATES + 1] =
{
	"user", "nice", "system", "idle", "iowait",
	NULL
};
static int	show_iowait = 0;

#define MEMUSED    0
#define MEMFREE    1
#define MEMSHARED  2
#define MEMBUFFERS 3
#define MEMCACHED  4
#define NMEMSTATS  5
static char *memorynames[NMEMSTATS + 1] =
{
	"K used, ", "K free, ", "K shared, ", "K buffers, ", "K cached",
	NULL
};

enum swap
{
	SWAPUSED,
	SWAPFREE,
	SWAPCACHED,
	SWAPIN,
	SWAPOUT,
	NSWAPSTATS
};
static char *swapnames[NSWAPSTATS + 1] =
{
	"K used, ", "K free, ", "K cached, ", "K in, ", "K out", NULL
};

struct swap_t
{
	int index;
	long long in[2];
	long long out[2];
} swap_activity;

static char fmt_header[] =
"    PID X           SIZE   RES STATE   XTIME  QTIME  %CPU LOCKS COMMAND";

char		fmt_header_io[] =
"    PID    IOPS   IORPS   IOWPS READS WRITES COMMAND";

/* these are names given to allowed sorting orders -- first is default */
static char *ordernames[] =
{
	"cpu", "size", "res", "xtime", "qtime", "iops", "iorps", "iowps", "reads",
	"writes", "locks", "command", "flag", "rlag", "slag", "wlag", NULL
};

/* forward definitions for comparison functions */
static int	compare_cmd(const void *, const void *);
static int	compare_cpu(const void *, const void *);
static int	compare_iops(const void *, const void *);
static int	compare_lag_flush(const void *, const void *);
static int	compare_lag_replay(const void *, const void *);
static int	compare_lag_sent(const void *, const void *);
static int	compare_lag_write(const void *, const void *);
static int	compare_locks(const void *, const void *);
static int	compare_qtime(const void *, const void *);
static int	compare_reads(const void *, const void *);
static int	compare_res(const void *, const void *);
static int	compare_size(const void *, const void *);
static int	compare_syscr(const void *, const void *);
static int	compare_syscw(const void *, const void *);
static int	compare_writes(const void *, const void *);
static int	compare_xtime(const void *, const void *);

int			(*proc_compares[]) () =
{
	compare_cpu,
		compare_size,
		compare_res,
		compare_xtime,
		compare_qtime,
		compare_iops,
		compare_syscr,
		compare_syscw,
		compare_reads,
		compare_writes,
		compare_locks,
		compare_cmd,
		compare_lag_flush,
		compare_lag_replay,
		compare_lag_sent,
		compare_lag_write,
		NULL
};

/*=SYSTEM STATE INFO====================================================*/

/* these are for calculating cpu state percentages */

static int64_t cp_time[NCPUSTATES];
static int64_t cp_old[NCPUSTATES];
static int64_t cp_diff[NCPUSTATES];

/* for calculating the exponential average */

static struct timeval lasttime;

/* these are for keeping track of processes */

#define INITIAL_ACTIVE_SIZE  (256)
#define PROCBLOCK_SIZE		 (32)
static struct top_proc *pgtable;
static int	proc_index;
static time_t boottime = -1;

/* these are for passing data back to the machine independant portion */

static int64_t cpu_states[NCPUSTATES];
static int	process_states[NPROCSTATES];
static long memory_stats[NMEMSTATS];
static long swap_stats[NSWAPSTATS];

/* usefull macros */
#define bytetok(x)	(((x) + 512) >> 10)
#define pagetok(x)	((x) * sysconf(_SC_PAGESIZE) >> 10)

/*======================================================================*/

static inline long long
diff_stat(long long value[2], int index)
{
	return value[index] - value[(index + 1) % 2];
}

static inline char *
skip_ws(const char *p)
{
	while (isspace(*p))
		p++;
	return (char *) p;
}

static inline char *
skip_token(const char *p)
{
	while (isspace(*p))
		p++;
	while (*p && !isspace(*p))
		p++;
	return (char *) p;
}

int
topproccmp(struct top_proc *e1, struct top_proc *e2)
{
	return (e1->pid < e2->pid ? -1 : e1->pid > e2->pid);
}

static void
xfrm_cmdline(char *p, int len)
{
	while (--len > 0)
	{
		if (*p == '\0')
		{
			*p = ' ';
		}
		p++;
	}
}

int
machine_init(struct statics *statics)
{
	/* make sure the proc filesystem is mounted */
	{
		struct statfs sb;

		if (statfs(PROCFS, &sb) < 0 || sb.f_type != PROC_SUPER_MAGIC)
		{
			fprintf(stderr, "%s: proc filesystem not mounted on " PROCFS "\n",
					myname);
			return -1;
		}
	}

	/* chdir to the proc filesystem to make things easier */
	chdir(PROCFS);

	/* a few preliminary checks */
	{
		int			fd;
		char		buff[128];
		char	   *p;
		int			cnt = 0;
		unsigned long uptime;
		struct timeval tv;

		/* get a boottime */
		if ((fd = open("uptime", 0)) != -1)
		{
			if (read(fd, buff, sizeof(buff)) > 0)
			{
				uptime = strtoul(buff, &p, 10);
				gettimeofday(&tv, 0);
				boottime = tv.tv_sec - uptime;
			}
			close(fd);
		}

		/* see how many states we get from stat */
		if ((fd = open("stat", 0)) != -1)
		{
			if (read(fd, buff, sizeof(buff)) > 0)
			{
				if ((p = strchr(buff, '\n')) != NULL)
				{
					*p = '\0';
					p = buff;
					cnt = 0;
					while (*p != '\0')
					{
						if (*p++ == ' ')
						{
							cnt++;
						}
					}
				}
			}

			close(fd);
		}
		if (cnt > 5)
		{
			/* we have iowait */
			show_iowait = 1;
		}
	}

	/* if we aren't showing iowait, then we have to tweak cpustatenames */
	if (!show_iowait)
	{
		cpustatenames[4] = NULL;
	}

	/* fill in the statics information */
	statics->procstate_names = procstatenames;
	statics->cpustate_names = cpustatenames;
	statics->memory_names = memorynames;
	statics->swap_names = swapnames;
	statics->order_names = ordernames;
	statics->boottime = boottime;
	statics->flags.fullcmds = 1;
	statics->flags.warmup = 1;

	/* all done! */
	return 0;
}

void
get_system_info(struct system_info *info)
{
	char		buffer[4096 + 1];
	int			fd,
				len;
	char	   *p;

	/* get load averages */

	if ((fd = open("loadavg", O_RDONLY)) != -1)
	{
		if ((len = read(fd, buffer, sizeof(buffer) - 1)) > 0)
		{
			buffer[len] = '\0';
			info->load_avg[0] = strtod(buffer, &p);
			info->load_avg[1] = strtod(p, &p);
			info->load_avg[2] = strtod(p, &p);
			p = skip_token(p);	/* skip running/tasks */
			p = skip_ws(p);
			if (*p)
			{
				info->last_pid = atoi(p);
			}
			else
			{
				info->last_pid = -1;
			}
		}
		close(fd);
	}

	/* get the cpu time info */
	if ((fd = open("stat", O_RDONLY)) != -1)
	{
		if ((len = read(fd, buffer, sizeof(buffer) - 1)) > 0)
		{
			buffer[len] = '\0';
			p = skip_token(buffer); /* "cpu" */
			cp_time[0] = strtoul(p, &p, 0);
			cp_time[1] = strtoul(p, &p, 0);
			cp_time[2] = strtoul(p, &p, 0);
			cp_time[3] = strtoul(p, &p, 0);
			if (show_iowait)
			{
				cp_time[4] = strtoul(p, &p, 0);
			}

			/* convert cp_time counts to percentages */
			percentages(NCPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
		}
		close(fd);
	}

	/* get system wide memory usage */
	if ((fd = open("meminfo", O_RDONLY)) != -1)
	{
		char	   *p;
		int			mem = 0;
		int			swap = 0;
		unsigned long memtotal = 0;
		unsigned long memfree = 0;
		unsigned long swaptotal = 0;

		if ((len = read(fd, buffer, sizeof(buffer) - 1)) > 0)
		{
			buffer[len] = '\0';
			p = buffer - 1;

			/* iterate thru the lines */
			while (p != NULL)
			{
				p++;
				if (p[0] == ' ' || p[0] == '\t')
				{
					/* skip */
				}
				else if (strncmp(p, "Mem:", 4) == 0)
				{
					p = skip_token(p);	/* "Mem:" */
					p = skip_token(p);	/* total memory */
					memory_stats[MEMUSED] = strtoul(p, &p, 10);
					memory_stats[MEMFREE] = strtoul(p, &p, 10);
					memory_stats[MEMSHARED] = strtoul(p, &p, 10);
					memory_stats[MEMBUFFERS] = strtoul(p, &p, 10);
					memory_stats[MEMCACHED] = strtoul(p, &p, 10);
					memory_stats[MEMUSED] = bytetok(memory_stats[MEMUSED]);
					memory_stats[MEMFREE] = bytetok(memory_stats[MEMFREE]);
					memory_stats[MEMSHARED] = bytetok(memory_stats[MEMSHARED]);
					memory_stats[MEMBUFFERS] =
						bytetok(memory_stats[MEMBUFFERS]);
					memory_stats[MEMCACHED] = bytetok(memory_stats[MEMCACHED]);
					mem = 1;
				}
				else if (strncmp(p, "Swap:", 5) == 0)
				{
					p = skip_token(p);	/* "Swap:" */
					p = skip_token(p);	/* total swap */
					swap_stats[SWAPUSED] = strtoul(p, &p, 10);
					swap_stats[SWAPFREE] = strtoul(p, &p, 10);
					swap_stats[SWAPUSED] = bytetok(swap_stats[SWAPUSED]);
					swap_stats[SWAPFREE] = bytetok(swap_stats[SWAPFREE]);
					swap = 1;
				}
				else if (!mem && strncmp(p, "MemTotal:", 9) == 0)
				{
					p = skip_token(p);
					memtotal = strtoul(p, &p, 10);
				}
				else if (!mem && memtotal > 0 && strncmp(p, "MemFree:", 8) == 0)
				{
					p = skip_token(p);
					memfree = strtoul(p, &p, 10);
					memory_stats[MEMUSED] = memtotal - memfree;
					memory_stats[MEMFREE] = memfree;
				}
				else if (!mem && strncmp(p, "MemShared:", 10) == 0)
				{
					p = skip_token(p);
					memory_stats[MEMSHARED] = strtoul(p, &p, 10);
				}
				else if (!mem && strncmp(p, "Buffers:", 8) == 0)
				{
					p = skip_token(p);
					memory_stats[MEMBUFFERS] = strtoul(p, &p, 10);
				}
				else if (!mem && strncmp(p, "Cached:", 7) == 0)
				{
					p = skip_token(p);
					memory_stats[MEMCACHED] = strtoul(p, &p, 10);
				}
				else if (!swap && strncmp(p, "SwapTotal:", 10) == 0)
				{
					p = skip_token(p);
					swaptotal = strtoul(p, &p, 10);
				}
				else if (!swap && swaptotal > 0 && strncmp(p, "SwapFree:", 9) == 0)
				{
					p = skip_token(p);
					memfree = strtoul(p, &p, 10);
					swap_stats[SWAPUSED] = swaptotal - memfree;
					swap_stats[SWAPFREE] = memfree;
				}
				else if (!mem && strncmp(p, "SwapCached:", 11) == 0)
				{
					p = skip_token(p);
					swap_stats[SWAPCACHED] = strtoul(p, &p, 10);
				}

				/* move to the next line */
				p = strchr(p, '\n');
			}
		}
		close(fd);
	}

	/* get swap activity */
	if ((fd = open("vmstat", O_RDONLY)) != -1)
	{
		unsigned long swpin = -1;
		unsigned long swpout = -1;

		if ((len = read(fd, buffer, sizeof(buffer) - 1)) > 0)
		{
			buffer[len] = '\0';
			p = buffer - 1;
			while (p != NULL)
			{
				p++;

				if (swpin == -1 && strncmp(p, "pswpin", 6) == 0)
				{
					p = skip_token(p);
					swpin = strtoul(p, &p, 10);
				}
				else if (swpout == -1 && strncmp(p, "pswpout", 7) == 0)
				{
					p = skip_token(p);
					swpout = strtoul(p, &p, 10);
				}

				if (swpin != -1 && swpout != -1)
				{
					swap_activity.in[swap_activity.index] = swpin;
					swap_activity.out[swap_activity.index] = swpout;

					swap_stats[SWAPIN] = diff_stat(swap_activity.in,
							swap_activity.index);
					swap_stats[SWAPOUT] = diff_stat(swap_activity.out,
							swap_activity.index);


					swap_activity.index = (swap_activity.index + 1) % 2;
					break;
				}

				/* move to the next line */
				p = strchr(p, '\n');
			}
		}
		close(fd);
	}
	else
	{
		swap_activity.in[swap_activity.index] = -1;
		swap_activity.out[swap_activity.index] = -1;
	}

	/* set arrays and strings */
	info->cpustates = cpu_states;
	info->memory = memory_stats;
	info->swap = swap_stats;
}

static void
read_one_proc_stat(struct top_proc *proc, struct process_select *sel)
{
	char		buffer[4096],
			   *p,
			   *q;
	int			fd,
				len;
	int			fullcmd;
	char		value[BUFFERLEN + 1];

	long long	tmp;

	/* if anything goes wrong, we return with proc->state == 0 */
	proc->state = 0;

	/* full cmd handling */
	fullcmd = sel->fullcmd;
	if (fullcmd == 1)
	{
		sprintf(buffer, "%d/cmdline", proc->pid);
		if ((fd = open(buffer, O_RDONLY)) != -1)
		{
			/* read command line data */
			/* (theres no sense in reading more than we can fit) */
			if ((len = read(fd, buffer, MAX_COLS)) > 1)
			{
				buffer[len] = '\0';
				xfrm_cmdline(buffer, len);
				update_str(&proc->name, buffer);
				printable(proc->name);
			}
			else
			{
				fullcmd = 0;
			}
			close(fd);
		}
		else
		{
			fullcmd = 0;
		}
	}

	/* grab the proc stat info in one go */
	sprintf(buffer, "%d/stat", proc->pid);

	fd = open(buffer, O_RDONLY);
	len = read(fd, buffer, sizeof(buffer) - 1);
	close(fd);

	buffer[len] = '\0';

	/* parse out the status, described in 'man proc' */

	/* skip pid and locate command, which is in parentheses */
	if ((p = strchr(buffer, '(')) == NULL)
	{
		return;
	}
	if ((q = strrchr(++p, ')')) == NULL)
	{
		return;
	}

	/* set the procname */
	*q = '\0';
	if (!fullcmd)
	{
		update_str(&proc->name, p);
		printable(proc->name);
	}

	/* scan the rest of the line */
	p = q + 1;
	p = skip_ws(p);
	switch (*p++)				/* state */
	{
		case 'R':
			proc->state = 1;
			break;
		case 'S':
			proc->state = 2;
			break;
		case 'D':
			proc->state = 3;
			break;
		case 'Z':
			proc->state = 4;
			break;
		case 'T':
			proc->state = 5;
			break;
		case 'W':
			proc->state = 6;
			break;
		case '\0':
			return;
	}

	p = skip_token(p);			/* skip ppid */
	p = skip_token(p);			/* skip pgrp */
	p = skip_token(p);			/* skip session */
	p = skip_token(p);			/* skip tty nr */
	p = skip_token(p);			/* skip tty pgrp */
	p = skip_token(p);			/* skip flags */
	p = skip_token(p);			/* skip min flt */
	p = skip_token(p);			/* skip cmin flt */
	p = skip_token(p);			/* skip maj flt */
	p = skip_token(p);			/* skip cmaj flt */

	proc->time = strtoul(p, &p, 10);	/* utime */
	proc->time += strtoul(p, &p, 10);	/* stime */

	p = skip_token(p);			/* skip cutime */
	p = skip_token(p);			/* skip cstime */
	p = skip_token(p);			/* skip priority */
	p = skip_token(p);			/* skip nice */
	p = skip_token(p);			/* skip num_threads */
	p = skip_token(p);			/* skip itrealvalue, 0 */
	proc->start_time = strtoul(p, &p, 10);	/* start_time */
	proc->size = bytetok(strtoul(p, &p, 10));	/* vsize */
	proc->rss = pagetok(strtoul(p, &p, 10));	/* rss */

#if 0
	/* for the record, here are the rest of the fields */
	p = skip_token(p);			/* skip rlim */
	p = skip_token(p);			/* skip start_code */
	p = skip_token(p);			/* skip end_code */
	p = skip_token(p);			/* skip start_stack */
	p = skip_token(p);			/* skip esp */
	p = skip_token(p);			/* skip eip */
	p = skip_token(p);			/* skip signal */
	p = skip_token(p);			/* skip sigblocked */
	p = skip_token(p);			/* skip sigignore */
	p = skip_token(p);			/* skip sigcatch */
	p = skip_token(p);			/* skip wchan */
	p = skip_token(p);			/* skip nswap, not maintained */
	p = skip_token(p);			/* exit signal */
	p = skip_token(p);			/* processor */
	p = skip_token(p);			/* rt_priority */
	p = skip_token(p);			/* policy */
	p = skip_token(p);			/* delayacct_blkio_ticks */
#endif

	/* Get the io stats. */
	sprintf(buffer, "%d/io", proc->pid);
	fd = open(buffer, O_RDONLY);
	if (fd == -1)
	{
		/*
		 * CONFIG_TASK_IO_ACCOUNTING is not enabled in the Linux kernel or
		 * this version of Linux may not support collecting i/o statistics per
		 * pid.
		 */
		return;
	}
	len = read(fd, buffer, sizeof(buffer) - 1);
	close(fd);

	buffer[len] = '\0';
	p = buffer;

	p = skip_token(p);			/* rchar */
	p = skip_token(p);			/* wchar */

	GET_VALUE(tmp);				/* syscr */
	proc->syscr[proc->index] = tmp;
	proc->iops[proc->index] = tmp;

	GET_VALUE(tmp);				/* syscw */
	proc->syscw[proc->index] = tmp;
	proc->iops[proc->index] += tmp;

	GET_VALUE(tmp);				/* read_bytes */
	proc->read_bytes[proc->index] = tmp;

	GET_VALUE(tmp);				/* write_bytes */
	proc->write_bytes[proc->index] = tmp;

	GET_VALUE(tmp);				/* cancelled_write_bytes */
	proc->write_bytes[proc->index] -= tmp;
}

caddr_t
get_process_info(struct system_info *si,
				 struct process_select *sel,
				 int compare_index, struct pg_conninfo_ctx *conninfo, int mode)
{
	struct timeval thistime;
	double		tickdiff;

	/* calculate the time difference since our last check */
	gettimeofday(&thistime, 0);
	if (lasttime.tv_sec)
	{
		timediff = ((thistime.tv_sec - lasttime.tv_sec) +
					(thistime.tv_usec - lasttime.tv_usec) * 1e-6);
	}
	else
	{
		timediff = 0;
	}
	lasttime = thistime;

	tickdiff = timediff * HZ;				/* convert to ticks */

	/* read the process information */
	{
		int			total_procs = 0;
		int			active_procs = 0;

		int			show_idle = sel->idle;

		int			i;
		int			rows;
		PGresult   *pgresult = NULL;

		struct top_proc *n,
				   *p;

		memset(process_states, 0, sizeof(process_states));

		connect_to_db(conninfo);
		if (conninfo->connection != NULL)
		{
			if (mode == MODE_REPLICATION)
			{
				pgresult = pg_replication(conninfo->connection);
			}
			else
			{
				pgresult = pg_processes(conninfo->connection);
			}
			rows = PQntuples(pgresult);
		}
		else
		{
			rows = 0;
		}

		if (rows > 0)
		{
			p = realloc(pgtable, sizeof(struct top_proc) * rows);
			if (p == NULL)
			{
				fprintf(stderr, "realloc error\n");
				if (pgresult != NULL)
					PQclear(pgresult);
				disconnect_from_db(conninfo);
				exit(1);
			}
			pgtable = p;
		}

		for (i = 0; i < rows; i++)
		{
			unsigned long otime;

			n = malloc(sizeof(struct top_proc));
			if (n == NULL)
			{
				fprintf(stderr, "malloc error\n");
				if (pgresult != NULL)
					PQclear(pgresult);
				disconnect_from_db(conninfo);
				exit(1);
			}
			memset(n, 0, sizeof(struct top_proc));
			n->pid = atoi(PQgetvalue(pgresult, i, 0));
			p = RB_INSERT(pgproc, &head_proc, n);
			if (p != NULL)
			{
				free(n);
				n = p;
			}
			else
			{
				n->time = 0;
			}

			otime = n->time;

			if (mode == MODE_REPLICATION)
			{
				update_str(&n->usename, PQgetvalue(pgresult, i, REP_USENAME));
				update_str(&n->application_name,
						PQgetvalue(pgresult, i, REP_APPLICATION_NAME));
				update_str(&n->client_addr,
						PQgetvalue(pgresult, i, REP_CLIENT_ADDR));
				update_str(&n->repstate, PQgetvalue(pgresult, i, REP_STATE));
				update_str(&n->primary,
						PQgetvalue(pgresult, i, REP_WAL_INSERT));
				update_str(&n->sent, PQgetvalue(pgresult, i, REP_SENT));
				update_str(&n->write, PQgetvalue(pgresult, i, REP_WRITE));
				update_str(&n->flush, PQgetvalue(pgresult, i, REP_FLUSH));
				update_str(&n->replay, PQgetvalue(pgresult, i, REP_REPLAY));
				n->sent_lag = atol(PQgetvalue(pgresult, i, REP_SENT_LAG));
				n->write_lag = atol(PQgetvalue(pgresult, i, REP_WRITE_LAG));
				n->flush_lag = atol(PQgetvalue(pgresult, i, REP_FLUSH_LAG));
				n->replay_lag = atol(PQgetvalue(pgresult, i, REP_REPLAY_LAG));

				memcpy(&pgtable[active_procs++], n, sizeof(struct top_proc));
			}
			else
			{
				read_one_proc_stat(n, sel);
				if (sel->fullcmd == 2)
				{
					update_str(&n->name, PQgetvalue(pgresult, i, PROC_QUERY));
					printable(n->name);
				}
				update_state(&n->pgstate, PQgetvalue(pgresult, i, PROC_STATE));
				update_str(&n->usename, PQgetvalue(pgresult, i, PROC_USENAME));
				n->xtime = atol(PQgetvalue(pgresult, i, PROC_XSTART));
				n->qtime = atol(PQgetvalue(pgresult, i, PROC_QSTART));
				n->locks = atoi(PQgetvalue(pgresult, i, PROC_LOCKS));

				process_states[n->pgstate]++;

				if (tickdiff > 0.0)
				{
					if ((n->pcpu = (n->time - otime) / tickdiff) < 0.0001)
					{
						n->pcpu = 0;
					}
				}

				if ((show_idle || n->pgstate != STATE_IDLE) &&
					(sel->usename[0] == '\0' ||
					 strcmp(n->usename, sel->usename) == 0))
					memcpy(&pgtable[active_procs++], n,
						   sizeof(struct top_proc));
			}
			n->index = (n->index + 1) % 2;
			total_procs++;
		}
		if (pgresult != NULL)
			PQclear(pgresult);
		disconnect_from_db(conninfo);

		si->p_active = active_procs;
		si->p_total = total_procs;
		si->procstates = process_states;
	}

	/* if requested, sort the "active" procs */
	if (compare_index >= 0 && si->p_active)
	{
		qsort(pgtable, si->p_active, sizeof(struct top_proc),
			  proc_compares[compare_index]);
	}

	/* don't even pretend that the return value thing here isn't bogus */
	proc_index = 0;
	return (caddr_t) 0;
}

char *
format_header(char *uname_field)
{
	int			uname_len = strlen(uname_field);

	if (uname_len > 8)
		uname_len = 8;

	memcpy(strchr(fmt_header, 'X'), uname_field, uname_len);

	return fmt_header;
}

char *
format_next_io(caddr_t handle)
{
	static char fmt[MAX_COLS];	/* static area where result is built */
	struct top_proc *p = &pgtable[proc_index++];

	snprintf(fmt, sizeof(fmt),
			"%7d %7.0f %7.0f %7.0f %5s %6s %s",
			p->pid,
			diff_stat(p->iops, p->index) / timediff,
			diff_stat(p->syscr, p->index) / timediff,
			diff_stat(p->syscw, p->index) / timediff,
			format_b(diff_stat(p->read_bytes, p->index) / timediff),
			format_b(diff_stat(p->write_bytes, p->index) / timediff),
			p->name);

	return (fmt);
}

char *
format_next_process(caddr_t handle)
{
	static char fmt[MAX_COLS];	/* static area where result is built */
	struct top_proc *p = &pgtable[proc_index++];

	snprintf(fmt, sizeof(fmt),
			 "%7d %-10.8s %5s %5s %-6s %5s %5s %5.1f %5d %s",
			 p->pid,
			 p->usename,
			 format_k(p->size),
			 format_k(p->rss),
			 backendstatenames[p->pgstate],
			 format_time(p->xtime),
			 format_time(p->qtime),
			 p->pcpu * 100.0,
			 p->locks,
			 p->name);

	/* return the result */
	return (fmt);
}

char *
format_next_replication(caddr_t handle)
{
	static char fmt[MAX_COLS];	/* static area where result is built */
	struct top_proc *p = &pgtable[proc_index++];

	snprintf(fmt, sizeof(fmt),
			 "%7d %-8.8s %-11.11s %15s %-9.9s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %5s %5s %5s %5s",
			 p->pid,
			 p->usename,
			 p->application_name,
			 p->client_addr,
			 p->repstate,
			 p->primary,
			 p->sent,
			 p->write,
			 p->flush,
			 p->replay,
			 format_b(p->sent_lag),
			 format_b(p->write_lag),
			 format_b(p->flush_lag),
			 format_b(p->replay_lag));

	/* return the result */
	return (fmt);
}

/* comparison routines for qsort */

/*
 * There are currently four possible comparison routines.  main selects
 * one of these by indexing in to the array proc_compares.
 *
 * Possible keys are defined as macros below.  Currently these keys are
 * defined:  percent cpu, cpu ticks, process state, resident set size,
 * total virtual memory usage.	The process states are ordered as follows
 * (from least to most important):	WAIT, zombie, sleep, stop, start, run.
 * The array declaration below maps a process state index into a number
 * that reflects this ordering.
 */

/* First, the possible comparison keys.  These are defined in such a way
   that they can be merely listed in the source code to define the actual
   desired ordering.
 */

#define ORDERKEY_IOPS   if ((result = diff_stat(p2->iops, p2->index) - \
			                          diff_stat(p1->iops, p1->index)) == 0)
#define ORDERKEY_LAG_FLUSH  if ((result = p2->flush_lag - p1->flush_lag) == 0)
#define ORDERKEY_LAG_REPLAY if ((result = p2->replay_lag - \
                                          p1->replay_lag) == 0)
#define ORDERKEY_LAG_SENT   if ((result = p2->sent_lag - p1->sent_lag) == 0)
#define ORDERKEY_LAG_WRITE  if ((result = p2->write_lag - p1->write_lag) == 0)
#define ORDERKEY_LOCKS   if ((result = p2->locks - p1->locks) == 0)
#define ORDERKEY_MEM     if ((result = p2->size - p1->size) == 0)
#define ORDERKEY_NAME    if ((result = strcmp(p1->name, p2->name)) == 0)
#define ORDERKEY_PCTCPU  if ((result = (int)(p2->pcpu - p1->pcpu)) == 0)
#define ORDERKEY_QTIME   if ((result = p2->qtime - p1->qtime) == 0)
#define ORDERKEY_READS   if ((result = diff_stat(p2->read_bytes, p2->index) - \
			                           diff_stat(p1->read_bytes, p1->index)) == 0)
#define ORDERKEY_RSSIZE  if ((result = p2->rss - p1->rss) == 0)
#define ORDERKEY_STATE   if ((result = p1->pgstate < p2->pgstate))
#define ORDERKEY_SYSCR   if ((result = diff_stat(p2->syscr, p2->index) - \
                                       diff_stat(p1->syscr, p1->index)) == 0)
#define ORDERKEY_SYSCW   if ((result = diff_stat(p2->syscw, p2->index) - \
                                       diff_stat(p1->syscw, p1->index)) == 0)
#define ORDERKEY_WRITES  if ((result = diff_stat(p2->write_bytes, p2->index) - \
			                           diff_stat(p1->write_bytes, p1->index)) == 0)
#define ORDERKEY_XTIME   if ((result = p2->xtime - p1->xtime) == 0)

/* compare_cmd - the comparison function for sorting by command name */

static int
compare_cmd(const void *v1, const void *v2)
{
	struct top_proc *p1 = (struct top_proc *) v1;
	struct top_proc *p2 = (struct top_proc *) v2;
	int			result;

	ORDERKEY_NAME
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_RSSIZE
		ORDERKEY_MEM
		;

	return (result);
}

/* compare_cpu - the comparison function for sorting by cpu percentage */

static int
compare_cpu(const void *v1, const void *v2)
{
	struct top_proc *p1 = (struct top_proc *) v1;
	struct top_proc *p2 = (struct top_proc *) v2;
	int			result;

	ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_RSSIZE
		ORDERKEY_MEM
		;

	return (result);
}

/* compare_iops - the comparison function for sorting by iops */

static int
compare_iops(const void *v1, const void *v2)
{
	struct top_proc *p1 = (struct top_proc *) v1;
	struct top_proc *p2 = (struct top_proc *) v2;
	int			result;

	ORDERKEY_IOPS
		ORDERKEY_SYSCW
		ORDERKEY_SYSCR
		ORDERKEY_READS
		ORDERKEY_WRITES
		ORDERKEY_NAME
		;

	return (result);
}

static int
compare_lag_flush(const void *v1, const void *v2)
{
	struct top_proc *p1 = (struct top_proc *) v1;
	struct top_proc *p2 = (struct top_proc *) v2;
	int			result;

	ORDERKEY_LAG_FLUSH
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return (result);
}

static int
compare_lag_replay(const void *v1, const void *v2)
{
	struct top_proc *p1 = (struct top_proc *) v1;
	struct top_proc *p2 = (struct top_proc *) v2;
	int			result;

	ORDERKEY_LAG_REPLAY
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return (result);
}

static int
compare_lag_sent(const void *v1, const void *v2)
{
	struct top_proc *p1 = (struct top_proc *) v1;
	struct top_proc *p2 = (struct top_proc *) v2;
	int			result;

	ORDERKEY_LAG_SENT
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return (result);
}

static int
compare_lag_write(const void *v1, const void *v2)
{
	struct top_proc *p1 = (struct top_proc *) v1;
	struct top_proc *p2 = (struct top_proc *) v2;
	int			result;

	ORDERKEY_LAG_WRITE
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return (result);
}

/*
 * compare_locks - the comparison function for sorting by total locks ancquired
 */

static int
compare_locks(const void *v1, const void *v2)
{
	struct top_proc *p1 = (struct top_proc *) v1;
	struct top_proc *p2 = (struct top_proc *) v2;
	int			result;

	ORDERKEY_LOCKS
		ORDERKEY_QTIME
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return (result);
}

/* compare_qtime - the comparison function for sorting by total cpu qtime */

static int
compare_qtime(const void *v1, const void *v2)
{
	struct top_proc *p1 = (struct top_proc *) v1;
	struct top_proc *p2 = (struct top_proc *) v2;
	int			result;

	ORDERKEY_QTIME
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return (result);
}

static int
compare_reads(const void *v1, const void *v2)
{
	struct top_proc *p1 = (struct top_proc *) v1;
	struct top_proc *p2 = (struct top_proc *) v2;
	int			result;

	ORDERKEY_READS
		ORDERKEY_SYSCR
		ORDERKEY_IOPS
		ORDERKEY_SYSCW
		ORDERKEY_WRITES
		ORDERKEY_NAME
		;

	return (result);
}

/* compare_res - the comparison function for sorting by resident set size */

static int
compare_res(const void *v1, const void *v2)
{
	struct top_proc *p1 = (struct top_proc *) v1;
	struct top_proc *p2 = (struct top_proc *) v2;
	int			result;

	ORDERKEY_RSSIZE
		ORDERKEY_MEM
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		;

	return (result);
}

/* compare_size - the comparison function for sorting by total memory usage */

static int
compare_size(const void *v1, const void *v2)
{
	struct top_proc *p1 = (struct top_proc *) v1;
	struct top_proc *p2 = (struct top_proc *) v2;
	int			result;

	ORDERKEY_MEM
		ORDERKEY_RSSIZE
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		;

	return (result);
}

static int
compare_syscr(const void *v1, const void *v2)
{
	struct top_proc *p1 = (struct top_proc *) v1;
	struct top_proc *p2 = (struct top_proc *) v2;
	int			result;

	ORDERKEY_SYSCR
		ORDERKEY_IOPS
		ORDERKEY_SYSCW
		ORDERKEY_READS
		ORDERKEY_WRITES
		ORDERKEY_NAME
		;

	return (result);
}

static int
compare_syscw(const void *v1, const void *v2)
{
	struct top_proc *p1 = (struct top_proc *) v1;
	struct top_proc *p2 = (struct top_proc *) v2;
	int			result;

	ORDERKEY_SYSCW
		ORDERKEY_IOPS
		ORDERKEY_SYSCR
		ORDERKEY_READS
		ORDERKEY_WRITES
		ORDERKEY_NAME
		;

	return (result);
}

/* compare_xtime - the comparison function for sorting by total cpu xtime */

static int
compare_xtime(const void *v1, const void *v2)
{
	struct top_proc *p1 = (struct top_proc *) v1;
	struct top_proc *p2 = (struct top_proc *) v2;
	int			result;

	ORDERKEY_XTIME
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return (result);
}

static int
compare_writes(const void *v1, const void *v2)
{
	struct top_proc *p1 = (struct top_proc *) v1;
	struct top_proc *p2 = (struct top_proc *) v2;
	int			result;

	ORDERKEY_WRITES
		ORDERKEY_IOPS
		ORDERKEY_SYSCR
		ORDERKEY_SYSCW
		ORDERKEY_READS
		ORDERKEY_NAME
		;

	return (result);
}

/*
 * Get IO information for the SCSI devices in the system.  Returns
 * read/write IOs per second and bandwidth by comparing current values
 * with previous values.
 */
void
get_io_info(struct io_info *io_info)
{
	struct timeval thistime;
	double timediff;
	static struct timeval lasttime;
	struct io_info cur_info;
	static struct io_info last_io_info;
	FILE *fp;
	char line[256];
	int major, minor;
	char dev_name[32];
	int64_t reads, readsectors, skip, writes, writesectors;
	int i;

	/* calculate the time difference since our last check */
	gettimeofday(&thistime, 0);
	if (lasttime.tv_sec)
		timediff = ((thistime.tv_sec - lasttime.tv_sec) +
					(thistime.tv_usec - lasttime.tv_usec) * 1e-6);
	else
		timediff = 0;

	lasttime = thistime;

	fp = fopen("/proc/diskstats", "r");
	if (fp == NULL)
	{
		memset(io_info, 0, sizeof(*io_info));
		return;
	}

	memset(&cur_info, 0, sizeof(cur_info));
	while (fgets(line, 256, fp) != NULL)
	{
		i = sscanf(line, "%d %d %31s %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld",
				   &major, &minor, dev_name,
				   &reads, &skip, &readsectors, &skip,
				   &writes, &skip, &writesectors, &skip,
				   &skip, &skip, &skip);
		if (i != 14)
			continue;

		/* Total up full scsi devices (not partitions) */
		if (major == 8 && (minor % 16) == 0)
		{
			cur_info.reads += reads;
			cur_info.readsectors += readsectors;
			cur_info.writes += writes;
			cur_info.writesectors += writesectors;
		}
    }
	fclose(fp);
	if (timediff == 0)
	{
		last_io_info = cur_info;
		memset(io_info, 0, sizeof(*io_info));
		return;
	}

	/* Compute the rate information */
	io_info->reads = (double)(cur_info.reads - last_io_info.reads) / timediff;
	io_info->readsectors = (double)(cur_info.readsectors - last_io_info.readsectors) / timediff;
	io_info->writes = (double)(cur_info.writes - last_io_info.writes) / timediff;
	io_info->writesectors = (double)(cur_info.writesectors - last_io_info.writesectors) / timediff;
	last_io_info = cur_info;
}
07070100000028000081A400000000000000000000000166622DE80000016A000000000000000000000000000000000000002500000000pg_top-4.1.1/machine/m_linux.man.rstLINUX NOTES
===========

The Linux port was written by Richard Henderson <rth@tamu.edu>.
The CPU% calculation was brazenly stolen from the Solaris 2
port and should be attributed to one of the many names listed
in its man page.

The order support was stolen from SUNOS 5 port by
Alexey Klimkin <kad@klon.tme.mcst.ru>

Made to work under 2.4 by William LeFebvre.
07070100000029000081A400000000000000000000000166622DE800005061000000000000000000000000000000000000002000000000pg_top-4.1.1/machine/m_macosx.c/*
 * m_macosx.c
 *
 * AUTHOR:	Andrew S. Townley
 *		based on m_bsd44.c and m_next32.c
 *		by Christos Zoulas and Tim Pugh
 * CREATED: Tue Aug 11 01:51:35 CDT 1998
 * SYNOPSIS:  MacOS X Server (Rhapsody Developer Release 2)
 * DESCRIPTION:
 *	MacOS X Server (Rhapsody Developer Release 2)
 *
 * CFLAGS: -DHAVE_STRERROR
 * TERMCAP: none
 * MATH: none
 */

/*
 * normal stuff
 */

#include "config.h"
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include "os.h"
#include "pg_top.h"
#include "machine.h"
#include "utils.h"

/*
 * MacOS kernel stuff
 */

#include <fcntl.h>
#include <sys/dkstat.h>
#include <sys/sysctl.h>
#include <mach/message.h>
#include <mach/vm_statistics.h>
#include <mach/mach.h>
#include <mach/host_info.h>

/* for new sysctl calls */
#include <sys/types.h>
#include <sys/stat.h>

#define VMUNIX		"/mach_kernel"
/* #define MEM		"/dev/mem" */
#define SWAP		NULL

#define NUM_AVERAGES	3
#define LOG1024		10

#define PP(pp, field)	((pp)->kp_proc . field)
#define EP(pp, field)	((pp)->kp_eproc . field)
#define VP(pp, field)	((pp)->kp_eproc.e_vm . field)
#define MPP(mp, field)	(PP((mp)->kproc, field))
#define MEP(mp, field)	(EP((mp)->kproc, field))
#define MVP(mp, field)	(VP((mp)->kproc, field))
#define TP(mp, field)	((mp)->task_info . field)
#define RP(mp, field)	((mp)->thread_summary . field)

/* define what weighted cpu is */
#define weighted_cpu(pct, s) (s == 0 ? 0.0 : \
						 ((pct) / (1.0 - exp(s * logcpu))))

/* what we consider to be process size: */
#ifdef notdef
#define PROCSIZE(pp) (VP((pp), vm_tsize) + VP((pp), vm_dsize) + VP((pp), vm_ssize))
#endif
#define PROCSIZE(pp) (EP(pp, e_xsize))
#define TASKSIZE(t) (TP(t, virtual_size) + TP(t, resident_size))

/* what we consider to be resident set size: */
#ifdef notdef
#define RSSIZE(pp) (MVP((pp), vm_rssize))
#endif
#define RSSIZE(pp) (MEP((pp), e_xrssize))

#define pctdouble(p) ((double)(p) / FSCALE)

/*
 * globals
 */

/* static kvm_t *kd = NULL; */
static int	nproc;
static int	onproc = -1;
static int	pref_len;
static int	maxmem;
static char fmt[MAX_COLS];

/* static double logcpu = 1.0; */

/* process array stuff */

static struct kinfo_proc *kproc_list = NULL;
static struct macos_proc *proc_list = NULL;
static struct macos_proc **proc_ref = NULL;
static int	process_states[7];
static struct handle handle;
static struct kinfo_proc *pbase;

/*
 * The mach information hopefully will not be necessary
 * when the kvm_* interfaces are supported completely.
 *
 * Since we're only concerned with task and thread info
 * for 'interesting' processes, we're going to only allocate
 * as many task and thread structures as needed.
 */

/* static struct task_basic_info *task_list = NULL; */

/* memory statistics */

static int	pageshift = 0;
static int	pagesize = 0;

#define pagetok(size)	((size) << pageshift)

static int	swappgsin = -1;
static int	swappgsout = -1;
static vm_statistics_data_t vm_stats;
static long memory_stats[7];

/* CPU state percentages */

host_cpu_load_info_data_t cpuload;

static int64_t cp_time[CPU_STATE_MAX];
static int64_t cp_old[CPU_STATE_MAX];
static int64_t cp_diff[CPU_STATE_MAX];
static int64_t cpu_states[CPU_STATE_MAX];

/*
 * types
 */

typedef long pctcpu;

/*
 * We need to declare a hybrid structure which will store all
 * of the stuff we care about for each process.
 */

#define FULLCMDLEN 1024

struct macos_proc
{
	struct kinfo_proc *kproc;
	task_t		the_task;
	struct task_basic_info task_info;
	unsigned int thread_count;
	struct thread_basic_info thread_summary;
	char		fullcmd[FULLCMDLEN + 1];
};

static int	show_fullcmd;
struct handle
{
	struct macos_proc **next_proc;
	int			remaining;
};


static char header[] =
"  PID X        PRI THRD  SIZE   RES STATE   TIME    MEM    CPU COMMAND";

/* 0123456	 -- field to fill in starts at header+6 */
#define UNAME_START 6

#define Proc_format \
		"%5d %-8.8s %3d %4d %5s %5s %-5s %6s %5.2f%% %5.2f%% %.180s"


int			proc_compare(const void *, const void *);
int			get_fullcmd(int, char *);

/*
 * puke()
 *
 * This function is used to report errors to stderr.
 */

static void
puke(const char *fmt,...)
{
	va_list		args;

	va_start(args, fmt);
	vfprintf(stderr, fmt, args);
	va_end(args);

	fputc('\n', stderr);
	fflush(stderr);
}

/*
 * load_thread_info()
 *
 * This function will attempt to load the thread summary info
 * for a Mach task.  The task is located as part of the macos_proc
 * structure.
 *
 * returns the kern_return_t value of any failed call or KERN_SUCCESS
 * if everything works.
 */

int
load_thread_info(struct macos_proc *mp)
{
	register kern_return_t rc = 0;
	register int i = 0;
	register int t_utime = 0;
	register int t_stime = 0;
	register int t_cpu = 0;
	register task_t the_task = mp->the_task;

	thread_array_t thread_list = NULL;

	/*
	 * We need to load all of the threads for the given task so we can get the
	 * performance data from them.
	 */

	mp->thread_count = 0;
	rc = task_threads(the_task, &thread_list, &(mp->thread_count));

	if (rc != KERN_SUCCESS)
	{
		return (rc);
	}

	/*
	 * now, for each of the threads, we need to sum the stats so we can
	 * present the whole thing to the caller.
	 */

	for (i = 0; i < mp->thread_count; i++)
	{
		struct thread_basic_info t_info;
		unsigned int icount = THREAD_BASIC_INFO_COUNT;
		kern_return_t rc = 0;

		rc = thread_info(thread_list[i], THREAD_BASIC_INFO,
						 (thread_info_t) & t_info, &icount);

		if (rc != KERN_SUCCESS)
		{
			puke("error: unable to load thread info for task (%s); rc = %d",
				 strerror(errno), rc);
			return (rc);
		}

		t_utime += t_info.user_time.seconds;
		t_stime += t_info.system_time.seconds;
		t_cpu += t_info.cpu_usage;
	}

	vm_deallocate(mach_task_self(), (vm_address_t) thread_list, sizeof(thread_array_t) * (mp->thread_count));

	/*
	 * Now, we load the values in the structure above.
	 */

	RP(mp, user_time).seconds = t_utime;
	RP(mp, system_time).seconds = t_stime;
	RP(mp, cpu_usage) = t_cpu;

	return (KERN_SUCCESS);
}




/*
 * prototypes for functions which pg_top needs
 */

char	   *printable();

/*
 * definitions for offsets
 */

#define X_NPROC		0
#define X_HZ		1
#define X_MAXMEM	2

#define NLIST_LAST	3

static char *procstates[] =
{
	"",
	" starting, ",
	" running, ",
	" sleeping, ",
	" stopped, ",
	" zombie, ",
	" swapped ",
	NULL
};

static char *cpustates[] =
{
	"user",
	"system",
	"idle",
	"nice",
	NULL
};

static char *state_abbrev[] =
{
	"",
	"start",
	"run\0\0\0",
	"sleep",
	"stop",
	"zomb"
};

static char *memnames[] =
{
	"K Tot, ",
	"K Free, ",
	"K Act, ",
	"K Inact, ",
	"K Wired, ",
	"K in, ",
	"K out ",
	NULL
};

/*
 * format_header()
 *
 * This function is used to add the username into the
 * header information.
 */

char *
format_header(register char *uname_field)
{
	register char *ptr;

	ptr = header + UNAME_START;
	while (*uname_field != '\0')
		*ptr++ = *uname_field++;

	return (header);
}

char *
format_next_io(caddr_t handle, char *(*getuserid) ())
{
	return (fmt);
}

/*
 * format_next_process()
 *
 * This function actually is responsible for the formatting of
 * each row which is displayed.
 */

char		cmd[MAX_COLS];

char *
format_next_process(caddr_t handle, char *(*getuserid) ())
{
	register struct macos_proc *pp;
	register long cputime;
	register double pct;
	struct handle *hp;
	char	   *command;		/* text outputted to describe the command */
	int			show_cmd_local = show_fullcmd;

	/*
	 * we need to keep track of the next proc structure.
	 */

	hp = (struct handle *) handle;
	pp = *(hp->next_proc++);
	hp->remaining--;

	/*
	 * get the process structure and take care of the cputime
	 */

	if ((MPP(pp, p_flag) & P_INMEM) == 0)
	{
		/* we want to print swapped processes as <pname> */
		char	   *comm = MPP(pp, p_comm);

#define COMSIZ	sizeof(MPP(pp, p_comm))
		char		buf[COMSIZ];

		strncpy(buf, comm, COMSIZ);
		comm[0] = '<';
		strncpy(&comm[1], buf, COMSIZ - 2);
		comm[COMSIZ - 2] = '\0';
		strncat(comm, ">", COMSIZ - 1);
		comm[COMSIZ - 1] = '\0';
		command = comm;
	}

	/*
	 * count the cpu time, but ignore the interrupts
	 *
	 * At the present time (DR2 8/1998), MacOS X doesn't correctly report this
	 * information through the kinfo_proc structure.  We need to get it from
	 * the task threads.
	 *
	 * cputime = PP(pp, p_rtime).tv_sec;
	 */

	cputime = RP(pp, user_time).seconds + RP(pp, system_time).seconds;

	/*
	 * calculate the base cpu percentages
	 *
	 * Again, at the present time, MacOS X doesn't report this information
	 * through the kinfo_proc.	We need to talk to the threads.
	 */

	pct = (double) (RP(pp, cpu_usage)) / TH_USAGE_SCALE;

	/* get the process's command name in to "cmd" */
	if (show_fullcmd)
		if (get_fullcmd(MPP(pp, p_pid), pp->fullcmd) < 0)
			show_cmd_local = 0; /* Don't show if full command not found. */

	/*
	 * format the entry
	 */

	/*
	 * In the final version, I would expect this to work correctly, but it
	 * seems that not all of the fields in the proc structure are being used.
	 *
	 * For now, we'll attempt to get some of the things we need from the mach
	 * task info.
	 */

	sprintf(fmt,
			Proc_format,
			MPP(pp, p_pid),
			(*getuserid) (MEP(pp, e_pcred.p_ruid)),
			0,
			pp->thread_count,
			format_k(TASKSIZE(pp) / 1024),
			format_k(pagetok(RSSIZE(pp))),
			state_abbrev[(u_char) MPP(pp, p_stat)],
			format_time(cputime),
			100.0 * TP(pp, resident_size) / maxmem,
			100.0 * pct,
			(show_cmd_local == 0 ? command : pp->fullcmd));

	return (fmt);
}

int
get_fullcmd(int pid, char *fullcmd)
{
	char	   *args,
			   *namePtr,
			   *stringPtr,
			   *cp;

	size_t		size = 0;
	int			mib[4],
				maxarg,
				numArgs,
				c = 0;

	mib[0] = CTL_KERN;
	mib[1] = KERN_ARGMAX;

	size = sizeof(maxarg);
	if (sysctl(mib, 2, &maxarg, &size, NULL, 0) == -1)
		return -1;

	args = (char *) malloc(maxarg);
	if (args == NULL)
		return -2;

	mib[0] = CTL_KERN;
	mib[1] = KERN_PROCARGS2;
	mib[2] = pid;

	if (mib[1] < 1)
		return -3;

	size = (size_t) maxarg;
	if (sysctl(mib, 3, args, &size, NULL, 0) == -1)
		return -4;

	memcpy(&numArgs, args, sizeof(numArgs));

	/* Skip exec_patch. */
	for (cp = args + sizeof(numArgs); cp < &args[size]; cp++)
		if (*cp == '\0')
			break;
	if (cp == &args[size])
		return -5;

	/* Skip trailing '\0' characters. */
	for (; cp < &args[size]; cp++)
		if (*cp == '\0')
			break;
	if (cp == &args[size])
		return -6;

	stringPtr = cp;

	/* Convert all '\0' to ' ' in the process arguments portion. */
	for (namePtr = NULL; c < numArgs && cp < &args[size]; cp++)
	{
		if (*cp == '\0')
		{
			c++;
			if (namePtr != NULL)
				*namePtr = ' ';
			namePtr = cp;
		}
	}

	/* Convert all '\0' to ' ' in the process environment settings portion. */
	for (; cp < &args[size]; cp++)
	{
		if (*cp == '\0')
		{
			if (namePtr != NULL)
			{
				if (&namePtr[1] == cp)
					break;
				*namePtr = ' ';
			}
			namePtr = cp;
		}
	}

	if (namePtr == NULL || namePtr == stringPtr)
		return -7;

	/* Get rid of leading whitespace. */
	while (stringPtr[0] == ' ' && stringPtr[0] != '\0')
		++stringPtr;

	strncpy(fullcmd, stringPtr, (size_t) FULLCMDLEN);

	return 1;
}

/*
 * get_process_info()
 *
 * This function returns information about the processes
 * on the system.
 */

caddr_t
get_process_info(struct system_info *si, struct process_select *sel, int x,
				 const char *values[])
{
	register int i;
	register int total_procs;
	register int active_procs;
	register struct macos_proc **prefp;
	register struct macos_proc *pp;
	register struct kinfo_proc *pp2;

	/*
	 * these are copied out of sel for speed
	 */

	int			show_idle;
	int			show_system;
	int			show_uid;
	int			show_command;

	/* begin mucking */
	/* kproc_list = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc); */
	PGconn	   *pgconn;
	PGresult   *pgresult = NULL;

	nproc = 0;
	pgconn = connect_to_db(values);
	if (pgconn != NULL)
	{
		pgresult = pg_processes(pgconn);
		nproc = PQntuples(pgresult);
		pbase = (struct kinfo_proc *) malloc(sizeof(struct kinfo_proc *));
	}
	PQfinish(pgconn);

	int			mib[4];

	mib[0] = CTL_KERN;
	mib[1] = KERN_PROC;
	mib[2] = KERN_PROC_PID;

	size_t		len = nproc;

	struct kinfo_proc *buffer;

	buffer = (struct kinfo_proc *) malloc(len * sizeof(struct kinfo_proc));

	for (i = 0; i < nproc; i++)
	{
		size_t		size = sizeof(struct kinfo_proc);

		mib[3] = atoi(PQgetvalue(pgresult, i, 0));

		if (sysctl(mib, sizeof(mib) / sizeof(int), &buffer[i], &size, NULL,
				   0) == -1)
		{
			perror("sysctl atoi loop");
			return "1";
		}

	}

	kproc_list = buffer;
	len = nproc;
	/* end selena's messing about */

	if (nproc > onproc)
	{
		proc_list = (struct macos_proc *) realloc(proc_list,
												  sizeof(struct macos_proc) * nproc);
		proc_ref = (struct macos_proc **) realloc(proc_ref,
												  sizeof(struct macos_proc *) * (onproc = nproc));
	}

	if (proc_ref == NULL || proc_list == NULL || kproc_list == NULL)
	{
		puke("error: out of memory (%s)", strerror(errno));
		return (NULL);
	}

	/*
	 * now, our task is to build the array of information we need to function
	 * correctly.  This involves setting a pointer to each real kinfo_proc
	 * structure returned by kvm_getprocs() in addition to getting the mach
	 * information for each of those processes.
	 */

	for (pp2 = kproc_list, i = 0; i < nproc; pp2++, i++)
	{

		/*
		 * first, we set the pointer to the reference in the kproc list.
		 */

		proc_list[i].kproc = pp2;

		/*
		 * then, we load all of the task info for the process
		 */

		if (PP(pp2, p_stat) != SZOMB)
		{
			load_thread_info(&proc_list[i]);
		}
	}

	/* get a pointer to the states summary array */
	si->procstates = process_states;

	/* set up flags which define what we are going to select */
	show_idle = sel->idle;
	show_uid = sel->uid != -1;
	show_command = sel->command != NULL;
	show_fullcmd = sel->fullcmd;

	/* count up process states and get pointers to interesting procs */
	total_procs = 0;
	active_procs = 0;
	memset((char *) process_states, 0, sizeof(process_states));
	prefp = proc_ref;
	for (pp = proc_list, i = 0; i < nproc; pp++, i++)
	{
		/*
		 * Place pointers to each valid proc structure in proc_ref[].  Process
		 * slots that are actually in use have a non-zero status field.
		 * Processes with P_SYSTEM set are system processes---these get
		 * ignored unless show_sysprocs is set.
		 */
		if (MPP(pp, p_stat) != 0 &&
			(show_system || ((MPP(pp, p_flag) & P_SYSTEM) == 0)))
		{
			total_procs++;
			process_states[(unsigned char) MPP(pp, p_stat)]++;
			if ((MPP(pp, p_stat) != SZOMB) &&
				(show_idle || (MPP(pp, p_pctcpu) != 0) ||
				 (MPP(pp, p_stat) == SRUN)) &&
				(!show_uid || MEP(pp, e_pcred.p_ruid) == (uid_t) sel->uid))
			{
				*prefp++ = pp;
				active_procs++;
			}
		}
	}

	/*
	 * if requested, sort the "interesting" processes
	 */

	qsort((char *) proc_ref, active_procs, sizeof(struct macos_proc *),
		  proc_compare);

	/* remember active and total counts */
	si->p_total = total_procs;
	si->p_active = pref_len = active_procs;

	/* pass back a handle */
	handle.next_proc = proc_ref;
	handle.remaining = active_procs;
	return ((caddr_t) & handle);
}

/*
 * get_system_info()
 *
 * This function is responsible for geting the periodic
 * system information snapshot.
 */

void
get_system_info(struct system_info *si)
{
	register long total;
	register int i;
	unsigned int count = HOST_CPU_LOAD_INFO_COUNT;
	double		avg[3];

	if (host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO,
						(host_info_t) & cpuload, &count) == KERN_SUCCESS)
	{
		for (i = 0; i < CPU_STATE_MAX; i++)
		{
			cp_time[i] = cpuload.cpu_ticks[i];
		}
	}

#ifdef MAX_VERBOSE

	/*
	 * print out the entries
	 */

	for (i = 0; i < CPU_STATE_MAX; i++)
		printf("cp_time[%d] = %d\n", i, cp_time[i]);
	fflush(stdout);
#endif							/* MAX_VERBOSE */

	/*
	 * get the load averages
	 */
	getloadavg(avg, sizeof(avg));
	si->load_avg[0] = avg[0];
	si->load_avg[1] = avg[1];
	si->load_avg[2] = avg[2];

#ifdef MAX_VERBOSE
	printf("%-30s%03.2f, %03.2f, %03.2f\n",
		   "load averages:",
		   si->load_avg[0],
		   si->load_avg[1],
		   si->load_avg[2]);
#endif							/* MAX_VERBOSE */

	total = percentages(CPU_STATE_MAX, cpu_states, cp_time, cp_old, cp_diff);

	/*
	 * get the memory statistics
	 */

	{
		kern_return_t status;

		count = HOST_VM_INFO_COUNT;
		status = host_statistics(mach_host_self(), HOST_VM_INFO,
								 (host_info_t) & vm_stats, &count);

		if (status != KERN_SUCCESS)
		{
			puke("error: vm_statistics() failed (%s)", strerror(errno));
			return;
		}

		/*
		 * we already have the total memory, we just need to get it in the
		 * right format.
		 */

		pagesize = 1;			/* temporary fix to div by 0 errors */
		memory_stats[0] = pagetok(maxmem / pagesize);
		memory_stats[1] = pagetok(vm_stats.free_count);
		memory_stats[2] = pagetok(vm_stats.active_count);
		memory_stats[3] = pagetok(vm_stats.inactive_count);
		memory_stats[4] = pagetok(vm_stats.wire_count);

		if (swappgsin < 0)
		{
			memory_stats[5] = 1;
			memory_stats[6] = 1;
		}
		else
		{
			memory_stats[5] = pagetok(((vm_stats.pageins - swappgsin)));
			memory_stats[6] = pagetok(((vm_stats.pageouts - swappgsout)));
		}
		swappgsin = vm_stats.pageins;
		swappgsout = vm_stats.pageouts;
	}

	si->cpustates = cpu_states;
	si->memory = memory_stats;
	si->last_pid = -1;

	return;
}

/*
 * machine_init()
 *
 * This function is responsible for filling in the values of the
 * statics structure.
 */

int
machine_init(struct statics *stat)
{
	size_t		size;

	size = sizeof(maxmem);
	sysctlbyname("hw.physmem", &maxmem, &size, NULL, 0);

	size = sizeof(nproc);
	sysctlbyname("kern.maxproc", &nproc, &size, NULL, 0);

#ifdef MAX_VERBOSE
	printf("%-30s%10d\n", "total system memory:", maxmem);
#endif							/* MAX_VERBOSE */

	/*
	 * calculate the pageshift from the system page size
	 */

	pagesize = getpagesize();
	pageshift = 0;
	while ((pagesize >>= 1) > 0)
		pageshift++;

	pageshift -= LOG1024;

	/*
	 * fill in the statics information
	 */

	stat->procstate_names = procstates;
	stat->cpustate_names = cpustates;
	stat->memory_names = memnames;
	stat->flags.fullcmds = 1;

	return (0);
}

/* comparison routine for qsort */

/*
 *	proc_compare - comparison function for "qsort"
 *	Compares the resource consumption of two processes using five
 *		distinct keys.	The keys (in descending order of importance) are:
 *		percent cpu, cpu ticks, state, resident set size, total virtual
 *		memory usage.  The process states are ordered as follows (from least
 *		to most important):  WAIT, zombie, sleep, stop, start, run.  The
 *		array declaration below maps a process state index into a number
 *		that reflects this ordering.
 */

static unsigned char sorted_state[] =
{
	0,							/* not used		*/
	3,							/* sleep		*/
	1,							/* ABANDONED (WAIT) */
	6,							/* run			*/
	5,							/* start		*/
	2,							/* zombie		*/
	4							/* stop			*/
};

int
proc_compare(const void *pp1, const void *pp2)
{
	register struct macos_proc *p1;
	register struct macos_proc *p2;
	register int result;
	register pctcpu lresult;

	/* remove one level of indirection */
	p1 = *(struct macos_proc **) pp1;
	p2 = *(struct macos_proc **) pp2;

	/* compare percent cpu (pctcpu) */
	if ((lresult = RP(p2, cpu_usage) - RP(p1, cpu_usage)) == 0)
	{
		/* use cpticks to break the tie */
		if ((result = MPP(p2, p_cpticks) - MPP(p1, p_cpticks)) == 0)
		{
			/* use process state to break the tie */
			if ((result = sorted_state[(unsigned char) MPP(p2, p_stat)] -
				 sorted_state[(unsigned char) MPP(p1, p_stat)]) == 0)
			{
				/* use priority to break the tie */
				if ((result = MPP(p2, p_priority) - MPP(p1, p_priority)) == 0)
				{
					/* use resident set size (rssize) to break the tie */
					if ((result = RSSIZE(p2) - RSSIZE(p1)) == 0)
					{
						/* use total memory to break the tie */
						result = PROCSIZE(p2->kproc) - PROCSIZE(p1->kproc);
					}
				}
			}
		}
	}
	else
	{
		result = lresult < 0 ? -1 : 1;
	}

	return (result);
}


/*
 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
 *		the process does not exist.
 *		It is EXTREMLY IMPORTANT that this function work correctly.
 *		If pg_top runs setuid root (as in SVR4), then this function
 *		is the only thing that stands in the way of a serious
 *		security problem.  It validates requests for the "kill"
 *		and "renice" commands.
 */

uid_t
proc_owner(pid_t pid)
{
	register int cnt;
	register struct macos_proc **prefp;
	register struct macos_proc *pp;

	prefp = proc_ref;
	cnt = pref_len;
	while (--cnt >= 0)
	{
		pp = *prefp++;
		if (MPP(pp, p_pid) == (pid_t) pid)
		{
			return ((int) MEP(pp, e_pcred.p_ruid));
		}
	}
	return (-1);
}
0707010000002A000081A400000000000000000000000166622DE800000431000000000000000000000000000000000000002600000000pg_top-4.1.1/machine/m_macosx.man.rstMacOS X NOTES
=============

The display is pretty close to the recommended display and also that
of a normal 4.4 BSD system.  The NICE column has been changed to be
the number of threads for each process.  The SIZE column reflects the
total size of the process (resident + non-resident) while the RES
column shows only the resident size.  The STATE column uses
information taken from the kinfo_proc structure p_pstat member.  It
will accurately display the state of stopped and zombie processes, but
I am not really sure about the other states.  Finally, the MEM column
is included which displays the percent of total memory per the ps
command.

The MacOS X module was written by Andrew S. Townley <atownley@primenet.com>.
Many thanks to William LeFebvre who is the original author
of the top utility and to Mike Rhee who showed the utility
to me in the first place.  Thanks also to Christos Zoulas
who wrote the 4.4 BSD implementation of the machine module.
I also got some pointers from the NEXTSTEP 3.2 and OSF/1
versions by Tim Pugh and Anthony Baxter, respectively.

0707010000002B000081A400000000000000000000000166622DE800005050000000000000000000000000000000000000002000000000pg_top-4.1.1/machine/m_netbsd.c/*	$NetBSD: m_netbsd15.c,v 1.16 2002/03/23 01:28:11 thorpej Exp $	*/

/*
 * pg_top - a top PostgreSQL users display for Unix
 *
 * SYNOPSIS:  For a NetBSD-1.5 (or later) system
 *
 * DESCRIPTION:
 * Originally written for BSD4.4 system by Christos Zoulas.
 * Based on the FreeBSD 2.0 version by Steven Wallace and Wolfram Schneider.
 * NetBSD-1.0 port by Arne Helme. Process ordering by Luke Mewburn.
 * NetBSD-1.3 port by Luke Mewburn, based on code by Matthew Green.
 * NetBSD-1.4/UVM port by matthew green.
 * NetBSD-1.5 port by Simon Burge.
 * NetBSD-1.6/UBC port by Tomas Svensson.
 * NetBSD-3.0/kernel threads port Simon Burge.
 * -
 * This is the machine-dependent module for NetBSD-1.5 and later
 * works for:
 *	NetBSD-1.6
 *	NetBSD-2.0
 *	NetBSD-3.0	(when released)
 *	NetBSD-3.99.10	(current development version)
 * and should work for:
 *	NetBSD-1.5
 * -
 * Doesn't include separate CPU states line per cpu on multiprocessor
 * systems like the NetBSD version of pg_top, but that requires some
 * recent kernel support.  This module forsakes that functionality as
 * a tradeoff for working on older versions of NetBSD.
 * -
 * pg_top does not need to be installed setuid or setgid with this module.
 *
 * LIBS: -lkvm
 *
 * AUTHORS: Christos Zoulas <christos@ee.cornell.edu>
 *		Steven Wallace <swallace@freebsd.org>
 *		Wolfram Schneider <wosch@cs.tu-berlin.de>
 *		Arne Helme <arne@acm.org>
 *		Luke Mewburn <lukem@netbsd.org>
 *		matthew green <mrg@eterna.com.au>
 *		Simon Burge <simonb@netbsd.org>
 *		Tomas Svensson <ts@unix1.net>
 *
 *
 * $Id: m_netbsd15.c,v 1.16 2002/03/23 01:28:11 thorpej Exp $
 */

#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/sched.h>
#include <sys/swap.h>

#include <uvm/uvm_extern.h>

#include <err.h>
#include <errno.h>
#include <kvm.h>
#include <math.h>
#include <nlist.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "os.h"
#include "pg_top.h"
#include "machine.h"
#include "utils.h"
#include "display.h"
#include "loadavg.h"

void		percentages64 __P((int, int *, u_int64_t *, u_int64_t *, u_int64_t *));


/* get_process_info passes back a handle.  This is what it looks like: */

struct handle
{
	struct kinfo_proc2 **next_proc; /* points to next valid proc pointer */
	int			remaining;		/* number of pointers remaining */
};

/* define what weighted cpu is. */
#define weighted_cpu(pct, pp) ((pp)->p_swtime == 0 ? 0.0 : \
			 ((pct) / (1.0 - exp((pp)->p_swtime * logcpu))))

/* what we consider to be process size: */
#define PROCSIZE(pp) \
	((pp)->p_vm_tsize + (pp)->p_vm_dsize + (pp)->p_vm_ssize)


/*
 * These definitions control the format of the per-process area
 */

static char header[] =
"  PID X        PRI NICE   SIZE   RES STATE      TIME   WCPU    CPU COMMAND";

/* 0123456	 -- field to fill in starts at header+6 */
#define UNAME_START 6

#define Proc_format \
	"%5d %-8.8s %3d %4d%7s %5s %-8.8s%7s %5.2f%% %5.2f%% %.12s"


/*
 * Process state names for the "STATE" column of the display.
 */

const char *state_abbrev[] = {
	"", "START", "RUN", "SLEEP", "STOP", "ZOMB", "DEAD", "CPU"
};

static kvm_t * kd;

/* these are retrieved from the kernel in _init */

static double logcpu;
static int	hz;
static int	ccpu;

/* these are for calculating cpu state percentages */

static u_int64_t cp_time[CPUSTATES];
static u_int64_t cp_old[CPUSTATES];
static u_int64_t cp_diff[CPUSTATES];

/* these are for detailing the process states */

int			process_states[8];
char	   *procstatenames[] = {
	"", " starting, ", " runnable, ", " sleeping, ", " stopped, ",
	" zombie, ", " dead, ", " on processor, ",
	NULL
};

/* these are for detailing the cpu states */

int			cpu_states[CPUSTATES];
char	   *cpustatenames[] = {
	"user", "nice", "system", "interrupt", "idle", NULL
};

/* these are for detailing the memory statistics */

long		memory_stats[7];
char	   *memorynames[] = {
	"K Act, ", "K Inact, ", "K Wired, ", "K Exec, ", "K File, ",
	"K Free, ",
	NULL
};

long		swap_stats[4];
char	   *swapnames[] = {
	"K Total, ", "K Used, ", "K Free, ",
	NULL
};


/* these are names given to allowed sorting orders -- first is default */
char	   *ordernames[] = {
	"cpu",
	"pri",
	"res",
	"size",
	"state",
	"time",
	NULL
};

/* forward definitions for comparison functions */
static int	compare_cpu __P((struct proc **, struct proc **));
static int	compare_prio __P((struct proc **, struct proc **));
static int	compare_res __P((struct proc **, struct proc **));
static int	compare_size __P((struct proc **, struct proc **));
static int	compare_state __P((struct proc **, struct proc **));
static int	compare_time __P((struct proc **, struct proc **));

int			(*proc_compares[])
__P((struct proc **, struct proc **)) =
{
	compare_cpu,
		compare_prio,
		compare_res,
		compare_size,
		compare_state,
		compare_time,
		NULL
};


/* these are for keeping track of the proc array */

static int	nproc;
static int	onproc = -1;
static int	pref_len;
static struct kinfo_proc2 *pbase;
static struct kinfo_proc2 **pref;

/* these are for getting the memory statistics */

static int	pageshift;			/* log base 2 of the pagesize */

/* define pagetok in terms of pageshift */

#define pagetok(size) ((size) << pageshift)

/* Pre/post lwp process states */
#ifndef SDEAD
#define SDEAD	LSDEAD
#define SRUN	LSRUN
#define SONPROC LSONPROC
#define SSLEEP	LSSLEEP
#define P_INMEM L_INMEM
#endif

int
			machine_init(statics)
struct statics *statics;

{
	int			pagesize;
	int			mib[2];
	size_t		size;
	struct clockinfo clockinfo;

	if ((kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open")) == NULL)
		return -1;

	mib[0] = CTL_KERN;
	mib[1] = KERN_CCPU;
	size = sizeof(ccpu);
	if (sysctl(mib, 2, &ccpu, &size, NULL, 0) == -1)
	{
		fprintf(stderr, "pg_top: sysctl kern.ccpu failed: %s\n",
				strerror(errno));
		return (-1);
	}

	mib[0] = CTL_KERN;
	mib[1] = KERN_CLOCKRATE;
	size = sizeof(clockinfo);
	if (sysctl(mib, 2, &clockinfo, &size, NULL, 0) == -1)
	{
		fprintf(stderr, "pg_top: sysctl kern.clockrate failed: %s\n",
				strerror(errno));
		return (-1);
	}
	hz = clockinfo.stathz;

	/* this is used in calculating WCPU -- calculate it ahead of time */
	logcpu = log(loaddouble(ccpu));

	pbase = NULL;
	pref = NULL;
	nproc = 0;
	onproc = -1;
	/* get the page size with "getpagesize" and calculate pageshift from it */
	pagesize = getpagesize();
	pageshift = 0;
	while (pagesize > 1)
	{
		pageshift++;
		pagesize >>= 1;
	}

	/* we only need the amount of log(2)1024 for our conversion */
	pageshift -= LOG1024;

	/* fill in the statics information */
	statics->procstate_names = procstatenames;
	statics->cpustate_names = cpustatenames;
	statics->memory_names = memorynames;
	statics->swap_names = swapnames;
	statics->order_names = ordernames;

	/* all done! */
	return (0);
}

char	   *format_header(uname_field)
char	   *uname_field;

{
	char	   *ptr;

	ptr = header + UNAME_START;
	while (*uname_field != '\0')
	{
		*ptr++ = *uname_field++;
	}

	return (header);
}

void
			get_system_info(si)
struct system_info *si;

{
	size_t		ssize;
	int			mib[2];
	struct uvmexp_sysctl uvmexp;
	struct swapent *sep,
			   *seporig;
	u_int64_t	totalsize,
				totalinuse;
	int			size,
				inuse,
				ncounted;
	int			rnswap,
				nswap;

	mib[0] = CTL_KERN;
	mib[1] = KERN_CP_TIME;
	ssize = sizeof(cp_time);
	if (sysctl(mib, 2, cp_time, &ssize, NULL, 0) < 0)
	{
		fprintf(stderr, "pg_top: sysctl kern.cp_time failed: %s\n",
				strerror(errno));
		quit(23);
	}

	if (getloadavg(si->load_avg, NUM_AVERAGES) < 0)
	{
		int			i;

		warn("can't getloadavg");
		for (i = 0; i < NUM_AVERAGES; i++)
			si->load_avg[i] = 0.0;
	}

	/* convert cp_time counts to percentages */
	percentages64(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);

	mib[0] = CTL_VM;
	mib[1] = VM_UVMEXP2;
	ssize = sizeof(uvmexp);
	if (sysctl(mib, 2, &uvmexp, &ssize, NULL, 0) < 0)
	{
		fprintf(stderr, "pg_top: sysctl vm.uvmexp2 failed: %s\n",
				strerror(errno));
		quit(23);
	}

	/* convert memory stats to Kbytes */
	memory_stats[0] = pagetok(uvmexp.active);
	memory_stats[1] = pagetok(uvmexp.inactive);
	memory_stats[2] = pagetok(uvmexp.wired);
	memory_stats[3] = pagetok(uvmexp.execpages);
	memory_stats[4] = pagetok(uvmexp.filepages);
	memory_stats[5] = pagetok(uvmexp.free);

	swap_stats[0] = swap_stats[1] = swap_stats[2] = 0;

	seporig = NULL;
	do
	{
		nswap = swapctl(SWAP_NSWAP, 0, 0);
		if (nswap < 1)
			break;

		/*
		 * Use seporig to keep track of the malloc'd memory base, as sep will
		 * be incremented in the for loop below.
		 */
		seporig = sep = (struct swapent *) malloc(nswap * sizeof(*sep));
		if (sep == NULL)
			break;
		rnswap = swapctl(SWAP_STATS, (void *) sep, nswap);
		if (nswap != rnswap)
			break;

		totalsize = totalinuse = ncounted = 0;
		for (; rnswap-- > 0; sep++)
		{
			ncounted++;
			size = sep->se_nblks;
			inuse = sep->se_inuse;
			totalsize += size;
			totalinuse += inuse;
		}
		swap_stats[0] = dbtob(totalsize) / 1024;
		swap_stats[1] = dbtob(totalinuse) / 1024;
		swap_stats[2] = dbtob(totalsize) / 1024 - swap_stats[1];

		/*
		 * Free here, before we malloc again in the next iteration of this
		 * loop.
		 */
		if (seporig)
		{
			free(seporig);
			seporig = NULL;
		}
	} while (0);

	/*
	 * Catch the case where we malloc'd, but then exited the loop due to nswap
	 * != rnswap.
	 */
	if (seporig)
		free(seporig);

	memory_stats[6] = -1;
	swap_stats[3] = -1;

	/* set arrays and strings */
	si->cpustates = cpu_states;
	si->memory = memory_stats;
	si->swap = swap_stats;
	si->last_pid = -1;
}


caddr_t
get_process_info(si, sel, compare_index)
struct system_info *si;
struct process_select *sel;
int			compare_index;

{
	int			i;
	int			total_procs;
	int			active_procs;
	struct kinfo_proc2 **prefp;
	struct kinfo_proc2 *pp;

	/* these are copied out of sel for speed */
	int			show_idle;
	int			show_system;
	int			show_uid;
	int			show_command;

	static struct handle handle;


	pbase = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2), &nproc);
	if (nproc > onproc)
		pref = (struct kinfo_proc2 **) realloc(pref,
											   sizeof(struct kinfo_proc2 *) * (onproc = nproc));
	if (pref == NULL || pbase == NULL)
	{
		(void) fprintf(stderr, "pg_top: Out of memory.\n");
		quit(23);
	}
	/* get a pointer to the states summary array */
	si->procstates = process_states;

	/* set up flags which define what we are going to select */
	show_idle = sel->idle;
	show_system = sel->system;
	show_uid = sel->uid != -1;
	show_command = sel->command != NULL;

	/* count up process states and get pointers to interesting procs */
	total_procs = 0;
	active_procs = 0;
	memset((char *) process_states, 0, sizeof(process_states));
	prefp = pref;
	for (pp = pbase, i = 0; i < nproc; pp++, i++)
	{
		/*
		 * Place pointers to each valid proc structure in pref[]. Process
		 * slots that are actually in use have a non-zero status field.
		 * Processes with P_SYSTEM set are system processes---these get
		 * ignored unless show_sysprocs is set.
		 */
		if (pp->p_stat != 0 && (show_system || ((pp->p_flag & P_SYSTEM) == 0)))
		{
			total_procs++;
			process_states[(unsigned char) pp->p_stat]++;
			if (pp->p_stat != SZOMB && pp->p_stat != SDEAD &&
				(show_idle || (pp->p_pctcpu != 0) ||
				 (pp->p_stat == SRUN || pp->p_stat == SONPROC)) &&
				(!show_uid || pp->p_ruid == (uid_t) sel->uid))
			{
				*prefp++ = pp;
				active_procs++;
			}
		}
	}

	/* if requested, sort the "interesting" processes */
	qsort((char *) pref, active_procs, sizeof(struct kinfo_proc2 *),
		  proc_compares[compare_index]);

	/* remember active and total counts */
	si->p_total = total_procs;
	si->p_active = pref_len = active_procs;

	/* pass back a handle */
	handle.next_proc = pref;
	handle.remaining = active_procs;
	return ((caddr_t) & handle);
}


char	   *format_next_process(handle, get_userid)
			caddr_t handle;
char	   *(*get_userid) __P((int));

{
	struct kinfo_proc2 *pp;
	long		cputime;
	double		pct;
	struct handle *hp;
	const char *statep;

#ifdef KI_NOCPU
	char		state[10];
#endif
	char		wmesg[KI_WMESGLEN + 1];
	static char fmt[128];		/* static area where result is built */
	char	   *pretty = "";

	/* find and remember the next proc structure */
	hp = (struct handle *) handle;
	pp = *(hp->next_proc++);
	hp->remaining--;

	/* get the process's user struct and set cputime */
	if ((pp->p_flag & P_INMEM) == 0)
		pretty = "<>";
	else if ((pp->p_flag & P_SYSTEM) != 0)
		pretty = "[]";

	if (pretty[0] != '\0')
	{
		/*
		 * Print swapped processes as <pname> and system processes as [pname]
		 */
		char	   *comm = pp->p_comm;

#define COMSIZ sizeof(pp->p_comm)
		char		buf[COMSIZ];

		(void) strncpy(buf, comm, COMSIZ);
		comm[0] = pretty[0];
		(void) strncpy(&comm[1], buf, COMSIZ - 2);
		comm[COMSIZ - 2] = '\0';
		(void) strncat(comm, &pretty[1], COMSIZ - 1);
		comm[COMSIZ - 1] = '\0';
	}

#if 0
	/* This does not produce the correct results */
	cputime = pp->p_uticks + pp->p_sticks + pp->p_iticks;
#else
	cputime = pp->p_rtime_sec;	/* This does not count interrupts */
#endif

	/* calculate the base for cpu percentages */
	pct = pctdouble(pp->p_pctcpu);

	if (pp->p_stat == SSLEEP)
	{
		strlcpy(wmesg, pp->p_wmesg, sizeof(wmesg));
		statep = wmesg;
	}
	else
		statep = state_abbrev[(unsigned) pp->p_stat];

#ifdef KI_NOCPU
	/* Post-1.5 change: add cpu number if appropriate */
	if (pp->p_cpuid != KI_NOCPU)
	{
		switch (pp->p_stat)
		{
			case SONPROC:
			case SRUN:
			case SSLEEP:
				snprintf(state, sizeof(state), "%.6s/%lld",
						 statep, (long long) pp->p_cpuid);
				statep = state;
				break;
		}
	}
#endif
	/* format this entry */
	sprintf(fmt,
			Proc_format,
			pp->p_pid,
			(*get_userid) (pp->p_ruid),
			pp->p_priority - PZERO,
			pp->p_nice - NZERO,
			format_k(pagetok(PROCSIZE(pp))),
			format_k(pagetok(pp->p_vm_rssize)),
			statep,
			format_time(cputime),
			100.0 * weighted_cpu(pct, pp),
			100.0 * pct,
			printable(pp->p_comm));

	/* return the result */
	return (fmt);
}

/* comparison routines for qsort */

/*
 * There are currently four possible comparison routines.  main selects
 * one of these by indexing in to the array proc_compares.
 *
 * Possible keys are defined as macros below.  Currently these keys are
 * defined:  percent cpu, cpu ticks, process state, resident set size,
 * total virtual memory usage.	The process states are ordered as follows
 * (from least to most important):	WAIT, zombie, sleep, stop, start, run.
 * The array declaration below maps a process state index into a number
 * that reflects this ordering.
 */

/*
 * First, the possible comparison keys.  These are defined in such a way
 * that they can be merely listed in the source code to define the actual
 * desired ordering.
 */

#define ORDERKEY_PCTCPU \
	if (lresult = (pctcpu)(p2)->p_pctcpu - (pctcpu)(p1)->p_pctcpu,\
		(result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0)

#define ORDERKEY_CPTICKS \
	if (lresult = (pctcpu)(p2)->p_rtime_sec \
			- (pctcpu)(p1)->p_rtime_sec,\
		(result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0)

#define ORDERKEY_STATE \
	if ((result = sorted_state[(int)(p2)->p_stat] - \
			  sorted_state[(int)(p1)->p_stat] ) == 0)

#define ORDERKEY_PRIO \
	if ((result = (p2)->p_priority - (p1)->p_priority) == 0)

#define ORDERKEY_RSSIZE \
	if ((result = p2->p_vm_rssize - p1->p_vm_rssize) == 0)

#define ORDERKEY_MEM	\
	if ((result = (PROCSIZE(p2) - PROCSIZE(p1))) == 0)

/*
 * Now the array that maps process state to a weight.
 * The order of the elements should match those in state_abbrev[]
 */

static int	sorted_state[] = {
	0,							/* (not used)	 ? */
	6,							/* "start"	SIDL	*/
	4,							/* "run"	SRUN	*/
	3,							/* "sleep"	SSLEEP	*/
	3,							/* "stop"	SSTOP	*/
	2,							/* "dead"	SDEAD	*/
	1,							/* "zomb"	SZOMB	*/
	5,							/* "onproc" SONPROC */
};

/* compare_cpu - the comparison function for sorting by cpu percentage */

static int
			compare_cpu(pp1, pp2)
struct proc **pp1,
		  **pp2;

{
	struct kinfo_proc2 *p1;
	struct kinfo_proc2 *p2;
	int			result;
	pctcpu		lresult;

	/* remove one level of indirection */
	p1 = *(struct kinfo_proc2 **) pp1;
	p2 = *(struct kinfo_proc2 **) pp2;

	ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_RSSIZE
		ORDERKEY_MEM
		;

	return (result);
}

/* compare_prio - the comparison function for sorting by process priority */

static int
			compare_prio(pp1, pp2)
struct proc **pp1,
		  **pp2;

{
	struct kinfo_proc2 *p1;
	struct kinfo_proc2 *p2;
	int			result;
	pctcpu		lresult;

	/* remove one level of indirection */
	p1 = *(struct kinfo_proc2 **) pp1;
	p2 = *(struct kinfo_proc2 **) pp2;

	ORDERKEY_PRIO
		ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_STATE
		ORDERKEY_RSSIZE
		ORDERKEY_MEM
		;

	return (result);
}

/* compare_res - the comparison function for sorting by resident set size */

static int
			compare_res(pp1, pp2)
struct proc **pp1,
		  **pp2;

{
	struct kinfo_proc2 *p1;
	struct kinfo_proc2 *p2;
	int			result;
	pctcpu		lresult;

	/* remove one level of indirection */
	p1 = *(struct kinfo_proc2 **) pp1;
	p2 = *(struct kinfo_proc2 **) pp2;

	ORDERKEY_RSSIZE
		ORDERKEY_MEM
		ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_STATE
		ORDERKEY_PRIO
		;

	return (result);
}

/* compare_size - the comparison function for sorting by total memory usage */

static int
			compare_size(pp1, pp2)
struct proc **pp1,
		  **pp2;

{
	struct kinfo_proc2 *p1;
	struct kinfo_proc2 *p2;
	int			result;
	pctcpu		lresult;

	/* remove one level of indirection */
	p1 = *(struct kinfo_proc2 **) pp1;
	p2 = *(struct kinfo_proc2 **) pp2;

	ORDERKEY_MEM
		ORDERKEY_RSSIZE
		ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_STATE
		ORDERKEY_PRIO
		;

	return (result);
}

/* compare_state - the comparison function for sorting by process state */

static int
			compare_state(pp1, pp2)
struct proc **pp1,
		  **pp2;

{
	struct kinfo_proc2 *p1;
	struct kinfo_proc2 *p2;
	int			result;
	pctcpu		lresult;

	/* remove one level of indirection */
	p1 = *(struct kinfo_proc2 **) pp1;
	p2 = *(struct kinfo_proc2 **) pp2;

	ORDERKEY_STATE
		ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_PRIO
		ORDERKEY_RSSIZE
		ORDERKEY_MEM
		;

	return (result);
}

/* compare_time - the comparison function for sorting by total cpu time */

static int
			compare_time(pp1, pp2)
struct proc **pp1,
		  **pp2;

{
	struct kinfo_proc2 *p1;
	struct kinfo_proc2 *p2;
	int			result;
	pctcpu		lresult;

	/* remove one level of indirection */
	p1 = *(struct kinfo_proc2 **) pp1;
	p2 = *(struct kinfo_proc2 **) pp2;

	ORDERKEY_CPTICKS
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return (result);
}


/*
 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
 *		the process does not exist.
 *		It is EXTREMLY IMPORTANT that this function work correctly.
 *		If pg_top runs setuid root (as in SVR4), then this function
 *		is the only thing that stands in the way of a serious
 *		security problem.  It validates requests for the "kill"
 *		and "renice" commands.
 */

int
			proc_owner(pid)
int			pid;

{
	int			cnt;
	struct kinfo_proc2 **prefp;
	struct kinfo_proc2 *pp;

	prefp = pref;
	cnt = pref_len;
	while (--cnt >= 0)
	{
		pp = *prefp++;
		if (pp->p_pid == (pid_t) pid)
			return (pp->p_ruid);
	}
	return (-1);
}

/*
 *	percentages(cnt, out, new, old, diffs) - calculate percentage change
 *	between array "old" and "new", putting the percentages i "out".
 *	"cnt" is size of each array and "diffs" is used for scratch space.
 *	The array "old" is updated on each call.
 *	The routine assumes modulo arithmetic.	This function is especially
 *	useful on BSD mchines for calculating cpu state percentages.
 */

void
			percentages64(cnt, out, new, old, diffs)
int			cnt;
int		   *out;
u_int64_t  *new;
u_int64_t  *old;
u_int64_t  *diffs;

{
	int			i;
	u_int64_t	change;
	u_int64_t	total_change;
	u_int64_t  *dp;
	u_int64_t	half_total;

	/* initialization */
	total_change = 0;
	dp = diffs;

	/* calculate changes for each state and the overall change */
	for (i = 0; i < cnt; i++)
	{
		/*
		 * Don't worry about wrapping - even at hz=1GHz, a u_int64_t will last
		 * at least 544 years.
		 */
		change = *new - *old;
		total_change += (*dp++ = change);
		*old++ = *new++;
	}

	/* avoid divide by zero potential */
	if (total_change == 0)
		total_change = 1;

	/* calculate percentages based on overall change, rounding up */
	half_total = total_change / 2;
	for (i = 0; i < cnt; i++)
		*out++ = (int) ((*diffs++ * 1000 + half_total) / total_change);
}

void
get_io_info(struct io_info *io_info)
{
    /* Not supported yet */
    memset(io_info, 0, sizeof(*io_info));
}
0707010000002C000081A400000000000000000000000166622DE8000000C9000000000000000000000000000000000000002600000000pg_top-4.1.1/machine/m_netbsd.man.rstNetBSD NOTES
============

This module has been tested on NetBSD 1.6, NetBSD 2.0 and NetBSD 3.0.
It should also work on NetBSD 1.5, and probably any newer releases of
NetBSD with little or no changes.
0707010000002D000081A400000000000000000000000166622DE8000055C4000000000000000000000000000000000000002100000000pg_top-4.1.1/machine/m_openbsd.c/*-
 * Copyright (c) 1994 Thorsten Lockert <tholo@sigmasoft.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * AUTHOR:  Thorsten Lockert <tholo@sigmasoft.com>
 *          Adapted from BSD4.4 by Christos Zoulas <christos@ee.cornell.edu>
 *          Patch for process wait display by Jarl F. Greipsland <jarle@idt.unit.no>
 *	    Patch for -DORDER by Kenneth Stailey <kstailey@disclosure.com>
 *	    Patch for new swapctl(2) by Tobias Weingartner <weingart@openbsd.org>
 *	    Adapted for pg_top by Mark Wong <markwkm@gmail.com>
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/sched.h>
#include <sys/swap.h>
#include <sys/sysctl.h>
#include <sys/tree.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <err.h>
#include <errno.h>

#include "pg_top.h"
#include "display.h"
#include "machine.h"
#include "utils.h"
#include "loadavg.h"

static long swapmode(long *, long *);
static char *format_comm(struct kinfo_proc *);

/* get_process_info passes back a handle.  This is what it looks like: */

struct handle
{
	struct kinfo_proc **next_proc;	/* points to next valid proc pointer */
	int			remaining;		/* number of pointers remaining */
};

struct pg_proc
{
	RB_ENTRY(pg_proc) entry;
	pid_t		pid;

	char *name;
	char *usename;
	int pgstate;
	unsigned long xtime;
	unsigned long qtime;
	unsigned int locks;

	/* Replication data */
	char	   *application_name;
	char	   *client_addr;
	char	   *repstate;
	char	   *primary;
	char	   *sent;
	char	   *write;
	char	   *flush;
	char	   *replay;
	long long	sent_lag;
	long long	write_lag;
	long long	flush_lag;
	long long	replay_lag;
};

int			topproccmp(struct pg_proc *, struct pg_proc *);

RB_HEAD(pgproc, pg_proc) head_proc = RB_INITIALIZER(&head_proc);
RB_PROTOTYPE(pgproc, pg_proc, entry, topproccmp)
RB_GENERATE(pgproc, pg_proc, entry, topproccmp)

/* what we consider to be process size: */
#define PROCSIZE(pp) ((pp)->p_vm_tsize + (pp)->p_vm_dsize + (pp)->p_vm_ssize)

/*
 *  These definitions control the format of the per-process area
 */
static char header[] =
"  PID X         SIZE   RES STATE     XTIME  QTIME  %CPU LOCKS COMMAND";

/* 0123456   -- field to fill in starts at header+6 */
#define UNAME_START 6

#define Proc_format \
	"%5d %-8.8s %5s %5s %-8s %5s %5s %5.2f %5d %.50s"

/* process state names for the "STATE" column of the display */
/*
 * the extra nulls in the string "run" are for adding a slash and the
 * processor number when needed
 */

char	   *state_abbrev[] = {
	"", "start", "run", "sleep", "stop", "zomb", "dead", "onproc"
};

/* these are for calculating cpu state percentages */
static int64_t * *cp_time;
static int64_t * *cp_old;
static int64_t * *cp_diff;

/* these are for detailing the process states */
int			process_states[6];

/* these are for detailing the cpu states */
int64_t    *cpu_states;
char	   *cpustatenames[] = {
	"user", "nice", "system", "interrupt", "idle", NULL
};

/* these are for detailing the memory statistics */
long		memory_stats[8];
char	   *memorynames[] = {
	"Real: ", "K/", "K act/tot  ", "Free: ", "K  ",
	"Swap: ", "K/", "K used/tot",
	NULL
};

/* these are names given to allowed sorting orders -- first is default */
char	   *ordernames[] = {
	"cpu", "size", "res", "time", "pri", NULL
};

/* compare routines */
static int	compare_cpu(), compare_size(), compare_res(), compare_time(), compare_prio();

int			(*proc_compares[]) () =
{
	compare_cpu,
		compare_size,
		compare_res,
		compare_time,
		compare_prio,
		NULL
};


/* these are for keeping track of the proc array */
static int	nproc;
static int	onproc = -1;
static int	pref_len;
static struct kinfo_proc *pbase;
static struct kinfo_proc **pref;

/* these are for getting the memory statistics */
static int	pageshift;			/* log base 2 of the pagesize */

/* define pagetok in terms of pageshift */
#define pagetok(size) ((size) << pageshift)

int			ncpu;

unsigned int maxslp;

int
machine_init(struct statics *statics)
{
	size_t		size = sizeof(ncpu);
	int			mib[2],
				pagesize,
				cpu;

	mib[0] = CTL_HW;
	mib[1] = HW_NCPU;
	if (sysctl(mib, 2, &ncpu, &size, NULL, 0) == -1)
		return (-1);
	cpu_states = calloc(ncpu, CPUSTATES * sizeof(int64_t));
	if (cpu_states == NULL)
		err(1, NULL);
	cp_time = calloc(ncpu, sizeof(int64_t *));
	cp_old = calloc(ncpu, sizeof(int64_t *));
	cp_diff = calloc(ncpu, sizeof(int64_t *));
	if (cp_time == NULL || cp_old == NULL || cp_diff == NULL)
		err(1, NULL);
	for (cpu = 0; cpu < ncpu; cpu++)
	{
		cp_time[cpu] = calloc(CPUSTATES, sizeof(int64_t));
		cp_old[cpu] = calloc(CPUSTATES, sizeof(int64_t));
		cp_diff[cpu] = calloc(CPUSTATES, sizeof(int64_t));
		if (cp_time[cpu] == NULL || cp_old[cpu] == NULL ||
			cp_diff[cpu] == NULL)
			err(1, NULL);
	}

	pbase = NULL;
	pref = NULL;
	onproc = -1;
	nproc = 0;

	/*
	 * get the page size with "getpagesize" and calculate pageshift from it
	 */
	pagesize = getpagesize();
	pageshift = 0;
	while (pagesize > 1)
	{
		pageshift++;
		pagesize >>= 1;
	}

	/* we only need the amount of log(2)1024 for our conversion */
	pageshift -= LOG1024;

	/* fill in the statics information */
	statics->procstate_names = procstatenames;
	statics->cpustate_names = cpustatenames;
	statics->memory_names = memorynames;
	statics->order_names = ordernames;
	return (0);
}

char *
format_header(char *uname_field)
{
	char	   *ptr;

	ptr = header + UNAME_START;
	while (*uname_field != '\0')
		*ptr++ = *uname_field++;
	return (header);
}

void
get_system_info(struct system_info *si)
{
	static int	sysload_mib[] = {CTL_VM, VM_LOADAVG};
	static int uvmexp_mib[] = {CTL_VM, VM_UVMEXP};
	struct loadavg sysload;
	struct uvmexp uvmexp;
	double	   *infoloadp;
	size_t		size;
	int			i;
	int64_t    *tmpstate;

	/*
	 * Can't track down the exact issue, but I think it has something to do
	 * with pg_top being the only process connected to the database, that its
	 * pid is gone before data is extracted from the process table.  So assume
	 * that there's nothing worth getting from the process table unless there
	 * is more than 1 process.
	 */
	if (nproc > 1)
	{
		if (ncpu > 1)
		{
			int			cp_time_mib[] = {CTL_KERN, KERN_CPTIME2, 0};

			size = CPUSTATES * sizeof(int64_t);
			for (i = 0; i < ncpu; i++)
			{
				cp_time_mib[2] = i;
				tmpstate = cpu_states + (CPUSTATES * i);
				if (sysctl(cp_time_mib, 3, cp_time[i], &size, NULL, 0) < 0)
					warn("sysctl kern.cp_time2 failed");
				/* convert cp_time2 counts to percentages */
				else
					(void) percentages(CPUSTATES, tmpstate, cp_time[i],
									   cp_old[i], cp_diff[i]);
			}
		}
		else
		{
			int			cp_time_mib[] = {CTL_KERN, KERN_CPTIME};
			long		cp_time_tmp[CPUSTATES];

			size = sizeof(cp_time_tmp);
			if (sysctl(cp_time_mib, 2, cp_time_tmp, &size, NULL, 0) < 0)
				warn("sysctl kern.cp_time failed");
			else
			{
				for (i = 0; i < CPUSTATES; i++)
					cp_time[0][i] = cp_time_tmp[i];
				/* convert cp_time counts to percentages */
				(void) percentages(CPUSTATES, cpu_states, cp_time[0],
								   cp_old[0], cp_diff[0]);
			}
		}
	}

	size = sizeof(sysload);
	if (sysctl(sysload_mib, 2, &sysload, &size, NULL, 0) < 0)
		warn("sysctl failed");
	infoloadp = si->load_avg;
	for (i = 0; i < 3; i++)
		*infoloadp++ = ((double) sysload.ldavg[i]) / sysload.fscale;


	/* get total -- systemwide main memory usage structure */
	size = sizeof(uvmexp);
	if (sysctl(uvmexp_mib, 2, &uvmexp, &size, NULL, 0) < 0)
	{
		warn("sysctl failed");
		bzero(&uvmexp, sizeof(uvmexp));
	}
	/* convert memory stats to Kbytes */
	memory_stats[0] = -1;
	memory_stats[1] = pagetok(uvmexp.active);
	memory_stats[2] = pagetok(uvmexp.npages - uvmexp.free);
	memory_stats[3] = -1;
	memory_stats[4] = pagetok(uvmexp.free);
	memory_stats[5] = -1;

	if (!swapmode(&memory_stats[6], &memory_stats[7]))
	{
		memory_stats[6] = 0;
		memory_stats[7] = 0;
	}

	/* set arrays and strings */
	si->cpustates = cpu_states;
	si->memory = memory_stats;
	si->last_pid = -1;
}

static struct handle handle;

caddr_t
get_process_info(struct system_info *si, struct process_select *sel,
				 int compare_index, struct pg_conninfo_ctx *conninfo, int mode)
{
	int			show_idle,
				show_cmd;
	int			total_procs,
				active_procs;
	struct kinfo_proc **prefp,
			   *pp;
	int			mib[6];
	size_t		size;

	int			i;
	PGresult   *pgresult = NULL;
	struct pg_proc *n, *p;

	size = (size_t) sizeof(struct kinfo_proc);
	mib[0] = CTL_KERN;
	mib[1] = KERN_PROC;
	mib[2] = KERN_PROC_PID;
	mib[4] = sizeof(struct kinfo_proc);
	mib[5] = 1;

	nproc = 0;
	connect_to_db(conninfo);
	if (conninfo->connection != NULL)
	{
		if (mode == MODE_REPLICATION)
		{
			pgresult = pg_replication(conninfo->connection);
		}
		else
		{
			pgresult = pg_processes(conninfo->connection);
		}
		nproc = PQntuples(pgresult);
		if (nproc > onproc)
			pbase = (struct kinfo_proc *)
					realloc(pbase, sizeof(struct kinfo_proc) * nproc);
	}

	if (nproc > onproc)
		pref = (struct kinfo_proc **) realloc(pref,
											  sizeof(struct kinfo_proc *) * (onproc = nproc));
	if (pref == NULL)
	{
		warnx("Out of memory.");
		quit(23);
	}
	/* get a pointer to the states summary array */
	si->procstates = process_states;

	/* set up flags which define what we are going to select */
	show_idle = sel->idle;
	show_cmd = sel->command != NULL;

	/* count up process states and get pointers to interesting procs */
	total_procs = 0;
	active_procs = 0;
	memset((char *) process_states, 0, sizeof(process_states));
	prefp = pref;
	i = 0;
	for (pp = pbase; pp < &pbase[nproc]; pp++)
	{
		mib[3] = atoi(PQgetvalue(pgresult, i, 0));
		if (sysctl(mib, 6, &pbase[i], &size, NULL, 0) != 0)
		{
			/*
			 * It appears that when pg_top is the only process accessing the
			 * database, the pg_top connection might be gone from the process
			 * table before we get it from the operating system.  If sysctl
			 * throws any error, assume that is the case and adjust pbase
			 * accordingly.
			 */
			--nproc;
			continue;
		}

		/*
		 * Place pointers to each valid proc structure in pref[]. Process
		 * slots that are actually in use have a non-zero status field.
		 * Processes with P_SYSTEM set are system processes---these get
		 * ignored unless show_system is set.
		 */
		if (pp->p_stat != 0 &&
			((pp->p_flag & P_SYSTEM) == 0) &&
			((pp->p_flag & P_THREAD) == 0))
		{
			total_procs++;
			process_states[(unsigned char) pp->p_stat]++;
			if (pp->p_stat != SZOMB &&
				(show_idle || pp->p_pctcpu != 0 ||
				 pp->p_stat == SRUN) &&
				(!show_cmd || strstr(pp->p_comm,
									 sel->command)))
			{
				*prefp++ = pp;
				active_procs++;
			}
		}

		n = malloc(sizeof(struct pg_proc));
		if (n == NULL)
		{
			fprintf(stderr, "malloc error\n");
			if (pgresult != NULL)
				PQclear(pgresult);
			disconnect_from_db(conninfo);
			exit(1);
		}
		memset(n, 0, sizeof(struct pg_proc));
		n->pid = atoi(PQgetvalue(pgresult, i, 0));
		p = RB_INSERT(pgproc, &head_proc, n);
		if (p != NULL)
		{
			free(n);
			n = p;
		}

		if (mode == MODE_REPLICATION)
		{
			update_str(&n->usename, PQgetvalue(pgresult, i, REP_USENAME));
			update_str(&n->application_name,
					PQgetvalue(pgresult, i, REP_APPLICATION_NAME));
			update_str(&n->client_addr,
					PQgetvalue(pgresult, i, REP_CLIENT_ADDR));
			update_str(&n->repstate, PQgetvalue(pgresult, i, REP_STATE));
			update_str(&n->primary,
					PQgetvalue(pgresult, i, REP_WAL_INSERT));
			update_str(&n->sent, PQgetvalue(pgresult, i, REP_SENT));
			update_str(&n->write, PQgetvalue(pgresult, i, REP_WRITE));
			update_str(&n->flush, PQgetvalue(pgresult, i, REP_FLUSH));
			update_str(&n->replay, PQgetvalue(pgresult, i, REP_REPLAY));
			n->sent_lag = atol(PQgetvalue(pgresult, i, REP_SENT_LAG));
			n->write_lag = atol(PQgetvalue(pgresult, i, REP_WRITE_LAG));
			n->flush_lag = atol(PQgetvalue(pgresult, i, REP_FLUSH_LAG));
			n->replay_lag = atol(PQgetvalue(pgresult, i, REP_REPLAY_LAG));
		}
		else
		{
			update_str(&n->name, PQgetvalue(pgresult, i, PROC_QUERY));
			printable(n->name);
			update_state(&n->pgstate, PQgetvalue(pgresult, i, PROC_STATE));
			update_str(&n->usename, PQgetvalue(pgresult, i, PROC_USENAME));
			n->xtime = atol(PQgetvalue(pgresult, i, PROC_XSTART));
			n->qtime = atol(PQgetvalue(pgresult, i, PROC_QSTART));
			n->locks = atoi(PQgetvalue(pgresult, i, PROC_LOCKS));
		}
		++i;
	}

	/* if requested, sort the "interesting" processes */
	if (compare_index >= 0)
		qsort((char *) pref, active_procs,
			  sizeof(struct kinfo_proc *), proc_compares[compare_index]);
	/* remember active and total counts */
	si->p_total = total_procs;
	si->p_active = pref_len = active_procs;

	/* pass back a handle */
	handle.next_proc = pref;
	handle.remaining = active_procs;
	return ((caddr_t) & handle);
}

char		fmt[MAX_COLS];		/* static area where result is built */

static char *
format_comm(struct kinfo_proc *kp)
{
#define ARG_SIZE 60
	static char **s,
				buf[ARG_SIZE];
	size_t		siz = 100;
	char	  **p;
	int			mib[4];

	for (;; siz *= 2)
	{
		if ((s = realloc(s, siz)) == NULL)
			err(1, NULL);
		mib[0] = CTL_KERN;
		mib[1] = KERN_PROC_ARGS;
		mib[2] = kp->p_pid;
		mib[3] = KERN_PROC_ARGV;
		if (sysctl(mib, 4, s, &siz, NULL, 0) == 0)
			break;
		if (errno != ENOMEM)
			return (kp->p_comm);
	}
	buf[0] = '\0';
	for (p = s; *p != NULL; p++)
	{
		if (p != s)
			strlcat(buf, " ", sizeof(buf));
		strlcat(buf, *p, sizeof(buf));
	}
	if (buf[0] == '\0')
		return (kp->p_comm);
	return (buf);
}

char *
format_next_process(caddr_t handle)
{
	char	   *p_wait;
	struct kinfo_proc *pp;
	struct handle *hp;
	int			cputime;
	double		pct;
	struct pg_proc n, *p = NULL;

	/* find and remember the next proc structure */
	hp = (struct handle *) handle;
	pp = *(hp->next_proc++);
	hp->remaining--;

	memset(&n, 0, sizeof(struct pg_proc));
	n.pid = pp->p_pid;
	p = RB_FIND(pgproc, &head_proc, &n);

	cputime = pp->p_rtime_sec + ((pp->p_rtime_usec + 500000) / 1000000);

	/* calculate the base for cpu percentages */
	pct = pctdouble(pp->p_pctcpu);

	if (pp->p_wmesg[0])
		p_wait = pp->p_wmesg;
	else
		p_wait = "-";

	/* format this entry */
	snprintf(fmt, sizeof fmt, Proc_format,
			 pp->p_pid, p->usename,
			 format_k(pagetok(PROCSIZE(pp))),
			 format_k(pagetok(pp->p_vm_rssize)),
			 backendstatenames[p->pgstate],
			 format_time(p->xtime),
			 format_time(p->qtime),
			 100.0 * pct,
			 p->locks,
			 printable(format_comm(pp)));

	/* return the result */
	return (fmt);
}

char *
format_next_replication(caddr_t handle)
{
	static char fmt[MAX_COLS];	/* static area where result is built */
	register struct kinfo_proc *pp;
	struct handle *hp;
	struct pg_proc n, *p = NULL;

	/* find and remember the next proc structure */
	hp = (struct handle *) handle;
	pp = *(hp->next_proc++);
	hp->remaining--;

	memset(&n, 0, sizeof(struct pg_proc));
	n.pid = pp->p_pid;
	p = RB_FIND(pgproc, &head_proc, &n);

	snprintf(fmt, sizeof(fmt),
			 "%7d %-8.8s %-11.11s %15s %-9.9s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %5s %5s %5s %5s",
			 p->pid,
			 p->usename,
			 p->application_name,
			 p->client_addr,
			 p->repstate,
			 p->primary,
			 p->sent,
			 p->write,
			 p->flush,
			 p->replay,
			 format_b(p->sent_lag),
			 format_b(p->write_lag),
			 format_b(p->flush_lag),
			 format_b(p->replay_lag));

	/* return the result */
	return (fmt);
}

/* comparison routine for qsort */
static unsigned char sorted_state[] =
{
	0,							/* not used		 */
	4,							/* start		 */
	5,							/* run			 */
	2,							/* sleep		 */
	3,							/* stop			 */
	1							/* zombie		 */
};

/*
 *  proc_compares - comparison functions for "qsort"
 */

/*
 * First, the possible comparison keys.  These are defined in such a way
 * that they can be merely listed in the source code to define the actual
 * desired ordering.
 */

#define ORDERKEY_PCTCPU \
	if (lresult = (pctcpu)p2->p_pctcpu - (pctcpu)p1->p_pctcpu, \
	    (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0)
#define ORDERKEY_CPUTIME \
	if ((result = p2->p_rtime_sec - p1->p_rtime_sec) == 0) \
		if ((result = p2->p_rtime_usec - p1->p_rtime_usec) == 0)
#define ORDERKEY_STATE \
	if ((result = sorted_state[(unsigned char)p2->p_stat] - \
	    sorted_state[(unsigned char)p1->p_stat])  == 0)
#define ORDERKEY_PRIO \
	if ((result = p2->p_priority - p1->p_priority) == 0)
#define ORDERKEY_RSSIZE \
	if ((result = p2->p_vm_rssize - p1->p_vm_rssize) == 0)
#define ORDERKEY_MEM \
	if ((result = PROCSIZE(p2) - PROCSIZE(p1)) == 0)

/* compare_cpu - the comparison function for sorting by cpu percentage */
static int
compare_cpu(const void *v1, const void *v2)
{
	struct proc **pp1 = (struct proc **) v1;
	struct proc **pp2 = (struct proc **) v2;
	struct kinfo_proc *p1,
			   *p2;
	pctcpu		lresult;
	int			result;

	/* remove one level of indirection */
	p1 = *(struct kinfo_proc **) pp1;
	p2 = *(struct kinfo_proc **) pp2;

	ORDERKEY_PCTCPU
		ORDERKEY_CPUTIME
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_RSSIZE
		ORDERKEY_MEM
		;
	return (result);
}

/* compare_size - the comparison function for sorting by total memory usage */
static int
compare_size(const void *v1, const void *v2)
{
	struct proc **pp1 = (struct proc **) v1;
	struct proc **pp2 = (struct proc **) v2;
	struct kinfo_proc *p1,
			   *p2;
	pctcpu		lresult;
	int			result;

	/* remove one level of indirection */
	p1 = *(struct kinfo_proc **) pp1;
	p2 = *(struct kinfo_proc **) pp2;

	ORDERKEY_MEM
		ORDERKEY_RSSIZE
		ORDERKEY_PCTCPU
		ORDERKEY_CPUTIME
		ORDERKEY_STATE
		ORDERKEY_PRIO
		;
	return (result);
}

/* compare_res - the comparison function for sorting by resident set size */
static int
compare_res(const void *v1, const void *v2)
{
	struct proc **pp1 = (struct proc **) v1;
	struct proc **pp2 = (struct proc **) v2;
	struct kinfo_proc *p1,
			   *p2;
	pctcpu		lresult;
	int			result;

	/* remove one level of indirection */
	p1 = *(struct kinfo_proc **) pp1;
	p2 = *(struct kinfo_proc **) pp2;

	ORDERKEY_RSSIZE
		ORDERKEY_MEM
		ORDERKEY_PCTCPU
		ORDERKEY_CPUTIME
		ORDERKEY_STATE
		ORDERKEY_PRIO
		;
	return (result);
}

/* compare_time - the comparison function for sorting by CPU time */
static int
compare_time(const void *v1, const void *v2)
{
	struct proc **pp1 = (struct proc **) v1;
	struct proc **pp2 = (struct proc **) v2;
	struct kinfo_proc *p1,
			   *p2;
	pctcpu		lresult;
	int			result;

	/* remove one level of indirection */
	p1 = *(struct kinfo_proc **) pp1;
	p2 = *(struct kinfo_proc **) pp2;

	ORDERKEY_CPUTIME
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;
	return (result);
}

/* compare_prio - the comparison function for sorting by CPU time */
static int
compare_prio(const void *v1, const void *v2)
{
	struct proc **pp1 = (struct proc **) v1;
	struct proc **pp2 = (struct proc **) v2;
	struct kinfo_proc *p1,
			   *p2;
	pctcpu		lresult;
	int			result;

	/* remove one level of indirection */
	p1 = *(struct kinfo_proc **) pp1;
	p2 = *(struct kinfo_proc **) pp2;

	ORDERKEY_PRIO
		ORDERKEY_PCTCPU
		ORDERKEY_CPUTIME
		ORDERKEY_STATE
		ORDERKEY_RSSIZE
		ORDERKEY_MEM
		;
	return (result);
}

/*
 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
 *		the process does not exist.
 *		It is EXTREMELY IMPORTANT that this function work correctly.
 *		If pg_top runs setuid root (as in SVR4), then this function
 *		is the only thing that stands in the way of a serious
 *		security problem.  It validates requests for the "kill"
 *		and "renice" commands.
 */
uid_t
proc_owner(pid_t pid)
{
	struct kinfo_proc **prefp,
			   *pp;
	int			cnt;

	prefp = pref;
	cnt = pref_len;
	while (--cnt >= 0)
	{
		pp = *prefp++;
		if (pp->p_pid == pid)
			return ((uid_t) pp->p_ruid);
	}
	return (uid_t) (-1);
}

/*
 * swapmode is rewritten by Tobias Weingartner <weingart@openbsd.org>
 * to be based on the new swapctl(2) system call.
 */
static long
swapmode(long *used, long *total)
{
	struct swapent *swdev;
	int			nswap,
				rnswap,
				i;

	nswap = swapctl(SWAP_NSWAP, 0, 0);
	if (nswap == 0)
		return 0;

	swdev = calloc(nswap, sizeof(*swdev));
	if (swdev == NULL)
		return 0;

	rnswap = swapctl(SWAP_STATS, swdev, nswap);
	if (rnswap == -1)
	{
		free(swdev);
		return 0;
	}

	/* if rnswap != nswap, then what? */

	/* Total things up */
	*total = *used = 0;
	for (i = 0; i < nswap; i++)
	{
		if (swdev[i].se_flags & SWF_ENABLE)
		{
			*used += (swdev[i].se_inuse / (1024 / DEV_BSIZE));
			*total += (swdev[i].se_nblks / (1024 / DEV_BSIZE));
		}
	}
	free(swdev);
	return 1;
}

int
topproccmp(struct pg_proc *e1, struct pg_proc *e2)
{
	return (e1->pid < e2->pid ? -1 : e1->pid > e2->pid);
}

void
get_io_info(struct io_info *io_info)
{
	/* Not supported yet */
	memset(io_info, 0, sizeof(*io_info));
}
0707010000002E000081A400000000000000000000000166622DE800006A0C000000000000000000000000000000000000002000000000pg_top-4.1.1/machine/m_remote.c/*
 * Copyright (c) 2008-2009, Mark Wong
 */

#ifdef __linux__
#define _GNU_SOURCE
#endif /* __linux__ */

#include <stdlib.h>
#if defined(__FreeBSD__) || defined(__OpenBSD__)
#include <sys/tree.h>
#endif /* __FreeBSD__*/
#ifdef __linux__
#include <bsd/stdlib.h>
#include <bsd/sys/tree.h>
#endif							/* __linux__ */
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <libpq-fe.h>

#include "pg.h"

#include "remote.h"
#include "utils.h"

#define QUERY_CPUTIME \
		"SELECT user, nice, system, idle, iowait\n" \
		"FROM pg_cputime()"

#define QUERY_LOADAVG \
		"SELECT load1, load5, load15, last_pid\n" \
		"FROM pg_loadavg()"

#define QUERY_MEMUSAGE \
		"SELECT memused, memfree, memshared, membuffers, memcached,\n" \
		"       swapused, swapfree, swapcached\n" \
		"FROM pg_memusage()"

#define QUERY_PROCTAB \
		"WITH lock_activity AS\n" \
		"(\n" \
		"     SELECT pid, count(*) AS lock_count\n" \
		"     FROM pg_locks\n" \
		"     WHERE relation IS NOT NULL\n" \
		"     GROUP BY pid\n" \
		")\n" \
		"SELECT a.pid, comm, fullcomm, a.state, utime, stime,\n" \
		"       starttime, vsize, rss, usename, rchar, wchar,\n" \
		"       syscr, syscw, reads, writes, cwrites, b.state,\n" \
		"       extract(EPOCH FROM age(clock_timestamp(),\n" \
		"                              xact_start))::BIGINT,\n" \
		"       extract(EPOCH FROM age(clock_timestamp(),\n" \
		"                              query_start))::BIGINT,\n" \
		"       coalesce(lock_count, 0) AS lock_count\n" \
		"FROM pg_proctab() a LEFT OUTER JOIN pg_stat_activity b\n" \
		"                    ON a.pid = b.pid\n" \
		"     LEFT OUTER JOIN lock_activity c\n" \
		"  ON a.pid = c.pid;"

#define QUERY_PROCTAB_QUERY \
		"WITH lock_activity AS\n" \
		"(\n" \
		"     SELECT pid, count(*) AS lock_count\n" \
		"     FROM pg_locks\n" \
		"     GROUP BY pid\n" \
		")\n" \
		"SELECT a.pid, comm, query, a.state, utime, stime,\n" \
		"       starttime, vsize, rss, usename, rchar, wchar,\n" \
		"       syscr, syscw, reads, writes, cwrites, b.state,\n" \
		"       extract(EPOCH FROM age(clock_timestamp(),\n" \
		"                              xact_start))::BIGINT,\n" \
		"       extract(EPOCH FROM age(clock_timestamp(),\n" \
		"                              query_start))::BIGINT,\n" \
		"       coalesce(lock_count, 0) AS lock_count\n" \
		"FROM pg_proctab() a LEFT OUTER JOIN pg_stat_activity b\n" \
		"                    ON a.pid = b.pid\n" \
		"     LEFT OUTER JOIN lock_activity c\n" \
		"  ON a.pid = c.pid;"

#define QUERY_PG_PROC \
		"SELECT COUNT(*)\n" \
		"FROM pg_catalog.pg_proc\n" \
		"WHERE proname = '%s'"

enum column_cputime
{
	c_cpu_user, c_cpu_nice, c_cpu_system, c_cpu_idle,
	c_cpu_iowait
};
enum column_loadavg
{
	c_load1, c_load5, c_load15, c_last_pid
};
enum column_memusage
{
	c_memused, c_memfree, c_memshared, c_membuffers,
	c_memcached, c_swapused, c_swapfree, c_swapcached
};
enum column_proctab
{
	c_pid, c_comm, c_fullcomm, c_state, c_utime, c_stime,
	c_starttime, c_vsize, c_rss, c_username,
	c_rchar, c_wchar, c_syscr, c_syscw, c_reads, c_writes, c_cwrites,
	c_pgstate, c_xtime, c_qtime, c_locks
};

#define bytetok(x)  (((x) + 512) >> 10)

#define INITIAL_ACTIVE_SIZE (256)
#define PROCBLOCK_SIZE (32)

#define NCPUSTATES 5
#define NMEMSTATS 5
#define NSWAPSTATS 3

#define MEMUSED 0
#define MEMFREE 1
#define MEMSHARED 2
#define MEMBUFFERS 3
#define MEMCACHED 4
#define NMEMSTATS 5

#define SWAPUSED 0
#define SWAPFREE 1
#define SWAPCACHED 2

struct top_proc_r
{
	RB_ENTRY(top_proc_r) entry;
	pid_t		pid;
	char	   *name;
	char	   *usename;
	unsigned long size;
	unsigned long rss;			/* in k */
	int			state;
	int			pgstate;
	unsigned long time;
	unsigned long start_time;
	unsigned long xtime;
	unsigned long qtime;
	unsigned int locks;
	double		pcpu;

	/* The change in the previous values and current values. */
	long long	rchar_diff;
	long long	wchar_diff;
	long long	syscr_diff;
	long long	syscw_diff;
	long long	read_bytes_diff;
	long long	write_bytes_diff;
	long long	cancelled_write_bytes_diff;

	/* The absolute values. */
	long long	rchar;
	long long	wchar;
	long long	syscr;
	long long	syscw;
	long long	read_bytes;
	long long	write_bytes;
	long long	cancelled_write_bytes;

	/* Replication data */
	char	   *application_name;
	char	   *client_addr;
	char	   *repstate;
	char	   *primary;
	char	   *sent;
	char	   *write;
	char	   *flush;
	char	   *replay;
	long long	sent_lag;
	long long	write_lag;
	long long	flush_lag;
	long long	replay_lag;
};

static time_t boottime = -1;
static struct top_proc_r *pgrtable;
static int	proc_r_index;

int			topprocrcmp(struct top_proc_r *, struct top_proc_r *);

RB_HEAD(pgprocr, top_proc_r) head_proc_r = RB_INITIALIZER(&head_proc_r);
RB_PROTOTYPE(pgprocr, top_proc_r, entry, topprocrcmp)
RB_GENERATE(pgprocr, top_proc_r, entry, topprocrcmp)

static char *cpustatenames[NCPUSTATES + 1] =
{
	"user", "nice", "system", "idle", "iowait", NULL
};

static char *memorynames[NMEMSTATS + 1] =
{
	"K used, ", "K free, ", "K shared, ", "K buffers, ", "K cached", NULL
};

/* these are names given to allowed sorting orders -- first is default */
static char *ordernames[] =
{
	"cpu", "size", "res", "xtime", "qtime", "rchar", "wchar", "syscr",
	"syscw", "reads", "writes", "cwrites", "locks", "command", "flag",
	"rlag", "slag", "wlag", NULL
};

static char *swapnames[NSWAPSTATS + 1] =
{
	"K used, ", "K free, ", "K cached", NULL
};

static char fmt_header[] =
"      PID X           SIZE   RES STATE   XTIME  QTIME  %CPU LOCKS COMMAND";

char		fmt_header_io_r[] =
"    PID RCHAR WCHAR   SYSCR   SYSCW READS WRITES CWRITES COMMAND";

char		fmt_header_replication_r[] =
"    PID USERNAME APPLICATION          CLIENT STATE     PRIMARY    SENT       WRITE      FLUSH      REPLAY      SLAG  WLAG  FLAG  RLAG";

/* Now the array that maps process state to a weight. */

unsigned char sort_state_r[] =
{
	0,							/* empty */
	6,							/* run */
	3,							/* sleep */
	5,							/* disk wait */
	1,							/* zombie */
	2,							/* stop */
	4							/* swap */
};

static int64_t cpu_states[NCPUSTATES];
static long memory_stats[NMEMSTATS];
static int	process_states[NPROCSTATES];
static long swap_stats[NSWAPSTATS];

static struct timeval lasttime;

static int64_t cp_time[NCPUSTATES];
static int64_t cp_old[NCPUSTATES];
static int64_t cp_diff[NCPUSTATES];

#define ORDERKEY_PCTCPU  if ((result = (int)(p2->pcpu - p1->pcpu)) == 0)
#define ORDERKEY_STATE	 if ((result = p1->pgstate < p2->pgstate))
#define ORDERKEY_RSSIZE  if ((result = p2->rss - p1->rss) == 0)
#define ORDERKEY_LAG_FLUSH  if ((result = p2->flush_lag - p1->flush_lag) == 0)
#define ORDERKEY_LAG_REPLAY if ((result = p2->replay_lag - \
                                          p1->replay_lag) == 0)
#define ORDERKEY_LAG_SENT   if ((result = p2->sent_lag - p1->sent_lag) == 0)
#define ORDERKEY_LAG_WRITE  if ((result = p2->write_lag - p1->write_lag) == 0)
#define ORDERKEY_MEM	 if ((result = p2->size - p1->size) == 0)
#define ORDERKEY_NAME	if ((result = strcmp(p1->name, p2->name)) == 0)
#define ORDERKEY_RCHAR	 if ((result = p1->rchar - p2->rchar) == 0)
#define ORDERKEY_WCHAR	 if ((result = p1->wchar - p2->wchar) == 0)
#define ORDERKEY_SYSCR	 if ((result = p1->syscr - p2->syscr) == 0)
#define ORDERKEY_SYSCW	 if ((result = p1->syscw - p2->syscw) == 0)
#define ORDERKEY_READS	 if ((result = p1->read_bytes - p2->read_bytes) == 0)
#define ORDERKEY_WRITES	 if ((result = p1->write_bytes - p2->write_bytes) == 0)
#define ORDERKEY_CWRITES if ((result = p1->cancelled_write_bytes - p2->cancelled_write_bytes) == 0)
#define ORDERKEY_XTIME if ((result = p2->xtime - p1->xtime) == 0)
#define ORDERKEY_QTIME if ((result = p2->qtime - p1->qtime) == 0)
#define ORDERKEY_LOCKS if ((result = p2->locks - p1->locks) == 0)

int			check_for_function(PGconn *, char *);
static int	compare_cmd_r(const void *, const void *);
static int	compare_cpu_r(const void *, const void *);
static int	compare_cwrites_r(const void *, const void *);
static int	compare_lag_flush(const void *, const void *);
static int	compare_lag_replay(const void *, const void *);
static int	compare_lag_sent(const void *, const void *);
static int	compare_lag_write(const void *, const void *);
static int	compare_locks_r(const void *, const void *);
static int	compare_qtime_r(const void *, const void *);
static int	compare_rchar_r(const void *, const void *);
static int	compare_reads_r(const void *, const void *);
static int	compare_res_r(const void *, const void *);
static int	compare_size_r(const void *, const void *);
static int	compare_syscr_r(const void *, const void *);
static int	compare_syscw_r(const void *, const void *);
static int	compare_wchar_r(const void *, const void *);
static int	compare_writes_r(const void *, const void *);
static int	compare_xtime_r(const void *, const void *);

int
check_for_function(PGconn *pgconn, char *procname)
{
	PGresult   *pgresult = NULL;
	int			rows = 0;
	int			count;
	char		sql[128];

	sprintf(sql, QUERY_PG_PROC, procname);
	pgresult = PQexec(pgconn, sql);
	rows = PQntuples(pgresult);
	/* Don't need to clean up on error, the program will exit shortly after. */
	if (rows == 0)
	{
		fprintf(stderr, "Error executing '%s'.\n", sql);
		return -1;
	}
	count = atoi(PQgetvalue(pgresult, 0, 0));
	if (count == 0)
	{
		fprintf(stderr, "Stored function '%s' is missing.\n", procname);
		return -1;
	}
	if (pgresult != NULL)
		PQclear(pgresult);
	return 0;
}

int			(*proc_compares_r[]) () =
{
	compare_cpu_r,
		compare_size_r,
		compare_res_r,
		compare_xtime_r,
		compare_qtime_r,
		compare_rchar_r,
		compare_wchar_r,
		compare_syscr_r,
		compare_syscw_r,
		compare_reads_r,
		compare_writes_r,
		compare_cwrites_r,
		compare_locks_r,
		compare_cmd_r,
		compare_lag_flush,
		compare_lag_replay,
		compare_lag_sent,
		compare_lag_write,
		NULL
};

/* The comparison function for sorting by command name. */

static int
compare_cmd_r(const void *v1, const void *v2)
{
	struct top_proc_r *p1 = (struct top_proc_r *) v1;
	struct top_proc_r *p2 = (struct top_proc_r *) v2;
	int			result;

	ORDERKEY_NAME
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_RSSIZE
		ORDERKEY_MEM
		;

	return (result);
}

/* compare_cpu_r - the comparison function for sorting by cpu percentage */

static int
compare_cpu_r(const void *v1, const void *v2)
{
	struct top_proc_r *p1 = (struct top_proc_r *) v1;
	struct top_proc_r *p2 = (struct top_proc_r *) v2;
	int			result;

	ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_RSSIZE
		ORDERKEY_MEM
		;

	return (result);
}

static int
compare_cwrites_r(const void *v1, const void *v2)
{
	struct top_proc_r *p1 = (struct top_proc_r *) v1;
	struct top_proc_r *p2 = (struct top_proc_r *) v2;
	int			result;

	ORDERKEY_CWRITES
		ORDERKEY_RCHAR
		ORDERKEY_WCHAR
		ORDERKEY_SYSCR
		ORDERKEY_SYSCW
		ORDERKEY_READS
		ORDERKEY_WRITES
		ORDERKEY_NAME
		;

	return (result);
}

static int
compare_lag_flush(const void *v1, const void *v2)
{
	struct top_proc_r *p1 = (struct top_proc_r *) v1;
	struct top_proc_r *p2 = (struct top_proc_r *) v2;
	int			result;

	ORDERKEY_LAG_FLUSH
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return (result);
}

static int
compare_lag_replay(const void *v1, const void *v2)
{
	struct top_proc_r *p1 = (struct top_proc_r *) v1;
	struct top_proc_r *p2 = (struct top_proc_r *) v2;
	int			result;

	ORDERKEY_LAG_REPLAY
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return (result);
}

static int
compare_lag_sent(const void *v1, const void *v2)
{
	struct top_proc_r *p1 = (struct top_proc_r *) v1;
	struct top_proc_r *p2 = (struct top_proc_r *) v2;
	int			result;

	ORDERKEY_LAG_SENT
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return (result);
}

static int
compare_lag_write(const void *v1, const void *v2)
{
	struct top_proc_r *p1 = (struct top_proc_r *) v1;
	struct top_proc_r *p2 = (struct top_proc_r *) v2;
	int			result;

	ORDERKEY_LAG_WRITE
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return (result);
}

/*
 * compare_locks_r - the comparison function for sorting by total locks
 * acquired
 */

int
compare_locks_r(const void *v1, const void *v2)
{
	struct top_proc_r *p1 = (struct top_proc_r *) v1;
	struct top_proc_r *p2 = (struct top_proc_r *) v2;
	int			result;

	ORDERKEY_LOCKS
		ORDERKEY_QTIME
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return (result);
}

/* compare_qtime_r - the comparison function for sorting by total cpu qtime */

static int
compare_qtime_r(const void *v1, const void *v2)
{
	struct top_proc_r *p1 = (struct top_proc_r *) v1;
	struct top_proc_r *p2 = (struct top_proc_r *) v2;
	int			result;

	ORDERKEY_QTIME
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return (result);
}

/* The comparison function for sorting by resident set size. */

static int
compare_res_r(const void *v1, const void *v2)
{
	struct top_proc_r *p1 = (struct top_proc_r *) v1;
	struct top_proc_r *p2 = (struct top_proc_r *) v2;
	int			result;

	ORDERKEY_RSSIZE
		ORDERKEY_MEM
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		;

	return (result);
}

/* The comparison function for sorting by total memory usage. */

static int
compare_rchar_r(const void *v1, const void *v2)
{
	struct top_proc_r *p1 = (struct top_proc_r *) v1;
	struct top_proc_r *p2 = (struct top_proc_r *) v2;
	int			result;

	ORDERKEY_RCHAR
		ORDERKEY_WCHAR
		ORDERKEY_SYSCR
		ORDERKEY_SYSCW
		ORDERKEY_READS
		ORDERKEY_WRITES
		ORDERKEY_CWRITES
		ORDERKEY_NAME
		;

	return (result);
}

static int
compare_reads_r(const void *v1, const void *v2)
{
	struct top_proc_r *p1 = (struct top_proc_r *) v1;
	struct top_proc_r *p2 = (struct top_proc_r *) v2;
	int			result;

	ORDERKEY_READS
		ORDERKEY_RCHAR
		ORDERKEY_WCHAR
		ORDERKEY_SYSCR
		ORDERKEY_SYSCW
		ORDERKEY_WRITES
		ORDERKEY_CWRITES
		ORDERKEY_NAME
		;

	return (result);
}

static int
compare_size_r(const void *v1, const void *v2)
{
	struct top_proc_r *p1 = (struct top_proc_r *) v1;
	struct top_proc_r *p2 = (struct top_proc_r *) v2;
	int			result;

	ORDERKEY_MEM
		ORDERKEY_RSSIZE
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		;

	return (result);
}

static int
compare_syscr_r(const void *v1, const void *v2)
{
	struct top_proc_r *p1 = (struct top_proc_r *) v1;
	struct top_proc_r *p2 = (struct top_proc_r *) v2;
	int			result;

	ORDERKEY_SYSCR
		ORDERKEY_RCHAR
		ORDERKEY_WCHAR
		ORDERKEY_SYSCW
		ORDERKEY_READS
		ORDERKEY_WRITES
		ORDERKEY_CWRITES
		ORDERKEY_NAME
		;

	return (result);
}

static int
compare_syscw_r(const void *v1, const void *v2)
{
	struct top_proc_r *p1 = (struct top_proc_r *) v1;
	struct top_proc_r *p2 = (struct top_proc_r *) v2;
	int			result;

	ORDERKEY_SYSCW
		ORDERKEY_RCHAR
		ORDERKEY_WCHAR
		ORDERKEY_SYSCR
		ORDERKEY_READS
		ORDERKEY_WRITES
		ORDERKEY_CWRITES
		ORDERKEY_NAME
		;

	return (result);
}

/* compare_xtime_r - the comparison function for sorting by total cpu xtime */

static int
compare_xtime_r(const void *v1, const void *v2)
{
	struct top_proc_r *p1 = (struct top_proc_r *) v1;
	struct top_proc_r *p2 = (struct top_proc_r *) v2;
	int			result;

	ORDERKEY_XTIME
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return (result);
}

static int
compare_wchar_r(const void *v1, const void *v2)
{
	struct top_proc_r *p1 = (struct top_proc_r *) v1;
	struct top_proc_r *p2 = (struct top_proc_r *) v2;
	int			result;

	ORDERKEY_WCHAR
		ORDERKEY_RCHAR
		ORDERKEY_SYSCR
		ORDERKEY_SYSCW
		ORDERKEY_READS
		ORDERKEY_WRITES
		ORDERKEY_CWRITES
		ORDERKEY_NAME
		;

	return (result);
}

static int
compare_writes_r(const void *v1, const void *v2)
{
	struct top_proc_r *p1 = (struct top_proc_r *) v1;
	struct top_proc_r *p2 = (struct top_proc_r *) v2;
	int			result;

	ORDERKEY_WRITES
		ORDERKEY_RCHAR
		ORDERKEY_WCHAR
		ORDERKEY_SYSCR
		ORDERKEY_SYSCW
		ORDERKEY_READS
		ORDERKEY_CWRITES
		ORDERKEY_NAME
		;

	return (result);
}

char *
format_header_r(char *uname_field)
{
	int			uname_len = strlen(uname_field);

	if (uname_len > 8)
		uname_len = 8;
	memcpy(strchr(fmt_header, 'X'), uname_field, uname_len);
	return fmt_header;
}

char *
format_next_io_r(caddr_t handler)
{
	static char fmt[MAX_COLS];	/* static area where result is built */
	struct top_proc_r *p = &pgrtable[proc_r_index++];

	snprintf(fmt, sizeof(fmt),
			"%7d %5s %5s %7lld %7lld %5s %6s %7s %s",
			(int) p->pid,
			format_b(p->rchar_diff),
			format_b(p->wchar_diff),
			p->syscr_diff,
			p->syscw_diff,
			format_b(p->read_bytes_diff),
			format_b(p->write_bytes_diff),
			format_b(p->cancelled_write_bytes_diff),
			p->name);

	return (fmt);
}

char *
format_next_process_r(caddr_t handler)
{
	static char fmt[MAX_COLS];	/* static area where result is built */
	struct top_proc_r *p = &pgrtable[proc_r_index++];

	snprintf(fmt, sizeof(fmt),
			 "%7d %-8.8s %5s %5s %-6s %5s %5s %5.1f %5d %s",
			 (int) p->pid,		/* Some OS's need to cast pid_t to int. */
			 p->usename,
			 format_k(p->size),
			 format_k(p->rss),
			 backendstatenames[p->pgstate],
			 format_time(p->xtime),
			 format_time(p->qtime),
			 p->pcpu * 100.0,
			 p->locks,
			 p->name);

	return (fmt);
}

char *
format_next_replication_r(caddr_t handle)
{
	static char fmt[MAX_COLS];	/* static area where result is built */
	struct top_proc_r *p = &pgrtable[proc_r_index++];

	snprintf(fmt, sizeof(fmt),
			 "%7d %-8.8s %-11.11s %15s %-9.9s %9s %9s %9s %9s %9s %5s %5s %5s %5s",
			 p->pid,
			 p->usename,
			 p->application_name,
			 p->client_addr,
			 p->repstate,
			 p->primary,
			 p->sent,
			 p->write,
			 p->flush,
			 p->replay,
			 format_b(p->sent_lag),
			 format_b(p->write_lag),
			 format_b(p->flush_lag),
			 format_b(p->replay_lag));

	/* return the result */
	return (fmt);
}

void
get_system_info_r(struct system_info *info, struct pg_conninfo_ctx *conninfo)
{
	PGresult   *pgresult = NULL;
	int			rows = 0;

	connect_to_db(conninfo);
	if (conninfo->connection != NULL)
	{
		pgresult = PQexec(conninfo->connection, QUERY_LOADAVG);
		rows = PQntuples(pgresult);
	}

	/* Get load averages. */
	if (rows > 0)
	{
		info->load_avg[0] = atof(PQgetvalue(pgresult, 0, c_load1));
		info->load_avg[1] = atof(PQgetvalue(pgresult, 0, c_load5));
		info->load_avg[2] = atof(PQgetvalue(pgresult, 0, c_load15));
		info->last_pid = atoi(PQgetvalue(pgresult, 0, c_last_pid));
	}
	else
	{
		info->load_avg[0] = 0;
		info->load_avg[1] = 0;
		info->load_avg[2] = 0;
		info->last_pid = 0;
	}

	/* Get processor time info. */
	if (conninfo->connection != NULL)
	{
		pgresult = PQexec(conninfo->connection, QUERY_CPUTIME);
		rows = PQntuples(pgresult);
	}
	if (rows > 0)
	{
		cp_time[0] = atol(PQgetvalue(pgresult, 0, c_cpu_user));
		cp_time[1] = atol(PQgetvalue(pgresult, 0, c_cpu_nice));
		cp_time[2] = atol(PQgetvalue(pgresult, 0, c_cpu_system));
		cp_time[3] = atol(PQgetvalue(pgresult, 0, c_cpu_idle));
		cp_time[4] = atol(PQgetvalue(pgresult, 0, c_cpu_iowait));

		/* convert cp_time counts to percentages */
		percentages(NCPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
	}
	else
	{
		cpu_states[0] = 0;
		cpu_states[1] = 0;
		cpu_states[2] = 0;
		cpu_states[3] = 0;
		cpu_states[4] = 0;
	}

	/* Get system wide memory usage. */
	if (conninfo->connection != NULL)
	{
		pgresult = PQexec(conninfo->connection, QUERY_MEMUSAGE);
		rows = PQntuples(pgresult);
	}
	if (rows > 0)
	{
		memory_stats[MEMUSED] = atol(PQgetvalue(pgresult, 0, c_memused));
		memory_stats[MEMFREE] = atol(PQgetvalue(pgresult, 0, c_memfree));
		memory_stats[MEMSHARED] = atol(PQgetvalue(pgresult, 0, c_memshared));
		memory_stats[MEMBUFFERS] = atol(PQgetvalue(pgresult, 0, c_membuffers));
		memory_stats[MEMCACHED] = atol(PQgetvalue(pgresult, 0, c_memcached));
		swap_stats[SWAPUSED] = atol(PQgetvalue(pgresult, 0, c_swapused));
		swap_stats[SWAPFREE] = atol(PQgetvalue(pgresult, 0, c_swapfree));
		swap_stats[SWAPCACHED] = atol(PQgetvalue(pgresult, 0, c_swapcached));
	}
	else
	{
		memory_stats[MEMUSED] = 0;
		memory_stats[MEMFREE] = 0;
		memory_stats[MEMSHARED] = 0;
		memory_stats[MEMBUFFERS] = 0;
		memory_stats[MEMCACHED] = 0;
		swap_stats[SWAPUSED] = 0;
		swap_stats[SWAPFREE] = 0;
		swap_stats[SWAPCACHED] = 0;
	}

	info->cpustates = cpu_states;
	info->memory = memory_stats;
	info->swap = swap_stats;

	if (pgresult != NULL)
		PQclear(pgresult);
	disconnect_from_db(conninfo);
}

caddr_t
get_process_info_r(struct system_info *si, struct process_select *sel,
				   int compare_index, struct pg_conninfo_ctx *conninfo, int mode)
{
	int			i;

	PGresult   *pgresult = NULL;
	int			rows;

	struct timeval thistime;
	double		timediff;

	int			active_procs = 0;
	int			total_procs = 0;

	int			show_idle = sel->idle;

	struct top_proc_r *n,
			   *p;

	memset(process_states, 0, sizeof(process_states));

	/* Calculate the time difference since our last check. */
	gettimeofday(&thistime, 0);
	if (lasttime.tv_sec)
	{
		timediff = ((thistime.tv_sec - lasttime.tv_sec) +
					(thistime.tv_usec - lasttime.tv_usec) * 1e-6);
	}
	else
	{
		timediff = 0;
	}
	lasttime = thistime;

	timediff *= HZ;				/* Convert to ticks. */

	connect_to_db(conninfo);
	if (conninfo->connection != NULL)
	{
		switch (mode)
		{
			case MODE_REPLICATION:
				pgresult = pg_replication(conninfo->connection);
				break;
			default:
				if (sel->fullcmd == 2)
				{
					pgresult = PQexec(conninfo->connection, QUERY_PROCTAB_QUERY);
				}
				else
				{
					pgresult = PQexec(conninfo->connection, QUERY_PROCTAB);
				}
		}
		rows = PQntuples(pgresult);
	}
	else
	{
		rows = 0;
	}

	if (rows > 0)
	{
		p = realloc(pgrtable, sizeof(struct top_proc_r) * rows);
		if (p == NULL)
		{
			fprintf(stderr, "realloc error\n");
			if (pgresult != NULL)
				PQclear(pgresult);
			disconnect_from_db(conninfo);
			exit(1);
		}
		pgrtable = p;
	}

	for (i = 0; i < rows; i++)
	{
		unsigned long otime;
		long long	value;

		n = malloc(sizeof(struct top_proc_r));
		if (n == NULL)
		{
			fprintf(stderr, "malloc error\n");
			if (pgresult != NULL)
				PQclear(pgresult);
			disconnect_from_db(conninfo);
			exit(1);
		}
		memset(n, 0, sizeof(struct top_proc_r));
		n->pid = atoi(PQgetvalue(pgresult, i, c_pid));
		p = RB_INSERT(pgprocr, &head_proc_r, n);
		if (p != NULL)
		{
			free(n);
			n = p;
		}
		else
		{
			n->time = 0;
		}

		otime = n->time;

		switch (mode)
		{
			case MODE_REPLICATION:
				update_str(&n->usename, PQgetvalue(pgresult, i, 1));
				update_str(&n->application_name, PQgetvalue(pgresult, i, 2));
				update_str(&n->client_addr, PQgetvalue(pgresult, i, 3));
				update_str(&n->repstate, PQgetvalue(pgresult, i, 4));
				update_str(&n->primary, PQgetvalue(pgresult, i, 5));
				update_str(&n->sent, PQgetvalue(pgresult, i, 6));
				update_str(&n->write, PQgetvalue(pgresult, i, 7));
				update_str(&n->flush, PQgetvalue(pgresult, i, 8));
				update_str(&n->replay, PQgetvalue(pgresult, i, 9));
				n->sent_lag = atol(PQgetvalue(pgresult, i, 10));
				n->write_lag = atol(PQgetvalue(pgresult, i, 11));
				n->flush_lag = atol(PQgetvalue(pgresult, i, 12));
				n->replay_lag = atol(PQgetvalue(pgresult, i, 13));

				memcpy(&pgrtable[active_procs++], n, sizeof(struct top_proc_r));
				break;
			default:
				if (sel->fullcmd && PQgetvalue(pgresult, i, c_fullcomm))
					update_str(&n->name, printable(PQgetvalue(pgresult, i, c_fullcomm)));
				else
					update_str(&n->name, printable(PQgetvalue(pgresult, i, c_comm)));

				switch (PQgetvalue(pgresult, i, c_state)[0])
				{
					case 'R':
						n->state = 1;
						break;
					case 'S':
						n->state = 2;
						break;
					case 'D':
						n->state = 3;
						break;
					case 'Z':
						n->state = 4;
						break;
					case 'T':
						n->state = 5;
						break;
					case 'W':
						n->state = 6;
						break;
					case '\0':
						continue;
				}
				update_state(&n->pgstate, PQgetvalue(pgresult, i, c_pgstate));

				n->time = (unsigned long) atol(PQgetvalue(pgresult, i, c_utime));
				n->time += (unsigned long) atol(PQgetvalue(pgresult, i, c_stime));
				n->start_time = (unsigned long)
					atol(PQgetvalue(pgresult, i, c_starttime));
				n->size = bytetok((unsigned long)
								  atol(PQgetvalue(pgresult, i, c_vsize)));
				n->rss = bytetok((unsigned long)
								 atol(PQgetvalue(pgresult, i, c_rss)));

				update_str(&n->usename, PQgetvalue(pgresult, i, c_username));

				n->xtime = atol(PQgetvalue(pgresult, i, c_xtime));
				n->qtime = atol(PQgetvalue(pgresult, i, c_qtime));

				n->locks = atol(PQgetvalue(pgresult, i, c_locks));

				value = atoll(PQgetvalue(pgresult, i, c_rchar));
				n->rchar_diff = value - n->rchar;
				n->rchar = value;

				value = atoll(PQgetvalue(pgresult, i, c_wchar));
				n->wchar_diff = value - n->wchar;
				n->wchar = value;

				value = atoll(PQgetvalue(pgresult, i, c_syscr));
				n->syscr_diff = value - n->syscr;
				n->syscr = value;

				value = atoll(PQgetvalue(pgresult, i, c_syscw));
				n->syscw_diff = value - n->syscw;
				n->syscw = value;

				value = atoll(PQgetvalue(pgresult, i, c_reads));
				n->read_bytes_diff = value - n->read_bytes;
				n->read_bytes = value;

				value = atoll(PQgetvalue(pgresult, i, c_writes));
				n->write_bytes_diff = value - n->write_bytes;
				n->write_bytes = value;

				value = atoll(PQgetvalue(pgresult, i, c_cwrites));
				n->cancelled_write_bytes_diff = value - n->cancelled_write_bytes;
				n->cancelled_write_bytes = value;

				++total_procs;
				++process_states[n->pgstate];

				if (timediff > 0.0)
				{
					if ((n->pcpu = (n->time - otime) / timediff) < 0.0001)
						n->pcpu = 0;
				}

				if ((show_idle || n->pgstate != STATE_IDLE) &&
					(sel->usename[0] == '\0' ||
					 strcmp(n->usename, sel->usename) == 0))
					memcpy(&pgrtable[active_procs++], n, sizeof(struct top_proc_r));
		}
	}

	if (pgresult != NULL)
		PQclear(pgresult);
	disconnect_from_db(conninfo);

	si->p_active = active_procs;
	si->p_total = total_procs;
	si->procstates = process_states;

	/* Sort the "active" procs if specified. */
	if (compare_index >= 0 && si->p_active)
		qsort(pgrtable, si->p_active, sizeof(struct top_proc_r),
			  proc_compares_r[compare_index]);

	/* don't even pretend that the return value thing here isn't bogus */
	proc_r_index = 0;
	return 0;
}

int
machine_init_r(struct statics *statics, struct pg_conninfo_ctx *conninfo)
{
	/* Make sure the remote system has the stored function installed. */
	connect_to_db(conninfo);
	if (conninfo->connection == NULL)
	{
		fprintf(stderr, "Cannot connect to database.\n");
		return -1;
	}

	if (check_for_function(conninfo->connection, "pg_cputime") != 0)
		return -1;
	if (check_for_function(conninfo->connection, "pg_loadavg") != 0)
		return -1;
	if (check_for_function(conninfo->connection, "pg_memusage") != 0)
		return -1;
	if (check_for_function(conninfo->connection, "pg_proctab") != 0)
		return -1;
	disconnect_from_db(conninfo);

	/* fill in the statics information */
	statics->procstate_names = procstatenames;
	statics->cpustate_names = cpustatenames;
	statics->memory_names = memorynames;
	statics->swap_names = swapnames;
	statics->order_names = ordernames;
	statics->boottime = boottime;
	statics->flags.fullcmds = 1;
	statics->flags.warmup = 1;

	return 0;
}

int
topprocrcmp(struct top_proc_r *e1, struct top_proc_r *e2)
{
	return (e1->pid < e2->pid ? -1 : e1->pid > e2->pid);
}
0707010000002F000081A400000000000000000000000166622DE80000532E000000000000000000000000000000000000001E00000000pg_top-4.1.1/machine/m_sco5.c/*
 * pg_top - a top PostgreSQL users display for Unix
 *
 * SYNOPSIS:  SCO UNIX OpenServer5
 *
 * DESCRIPTION:
 * This is the machine-dependent module for SCO OpenServer5.
 * Originally written for BSD4.3 system by Christos Zoulas.
 * Modified to m_sco.c (3.2v4.2)  by Gregory Shilin <shilin@onyx.co.il>
 * Modified to m_sco5.c (3.2v5.*) by Mike Hopkirk <hops@sco.com>
 * Works for:
 * SCO UNIX 3.2v5.*
 *
 * CFLAGS: -DHAVE_GETOPT -DORDER
 *
 * AUTHOR: Mike Hopkirk (hops@sco.com)
 * hops 10-Jul-98 - added sort fields
 *		17-Jul-98 - add philiph's chopped cmd string support
 *					  (define NO_COMMAND_ARGS to enable )
 *		09-Dec-98 - provide RSS calculation
 *		15-Mar-2000 - Fix broken lines and cleanup sysinfo access w macros
 */

#include <sys/types.h>
#include <sys/param.h>

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <nlist.h>
#include <math.h>
#include <signal.h>
#include <string.h>

#include <sys/dir.h>
#include <sys/immu.h>
#include <sys/region.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/sysinfo.h>
#include <sys/systm.h>
#include <sys/sysmacros.h>
#include <sys/var.h>
#include <sys/sysi86.h>

#include "pg_top.h"
#include "machine.h"
#include "utils.h"
#include "loadavg.h"

/*
typedef unsigned long  ulong;
typedef unsigned int   uint;
typedef unsigned short ushort;
*/
typedef unsigned char uchar;

#define VMUNIX	"/unix"
#define KMEM	"/dev/kmem"
#define MEM		"/dev/mem"

#define SI_ACTIVE(p)   p->p_active
#define SI_TOTAL(p)    p->p_total

/* get_process_info passes back a handle. This is what it looks like: */
struct handle
{
	struct proc **next_proc;	/* points to next valid proc pointer */
	int			remaining;		/* number of pointers remaining */
};

/* define what weighted cpu is */
#define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \
			 ((pct) / (1.0 - exp((pp)->p_time * logcpu))))

#define bytetok(bytes) ((bytes) >> 10)

/* what we consider to be process size: */
#define PROCSIZE(up) bytetok(ctob((up)->u_tsize + (up)->u_dsize+(up)->u_ssize))

/* definitions for indices in the nlist array */
#define X_V				0		/* System configuration information */
#define X_PROC			1		/* process tables */
#define X_FREEMEM		2		/* current free memory */
#define X_AVAILRMEM		3		/* available resident (not swappable) mem in
								 * pages */
#define X_AVAILSMEM		4		/* available swappable memory in pages */
#define X_MAXMEM		5		/* maximum available free memory in clicks */
#define X_PHYSMEM		6		/* physical memory in clicks */
#define X_NSWAP			7		/* size of swap space in blocks */
#define X_HZ			8		/* ticks/second of the clock */
#define X_MPID			9		/* last process id */
#define X_SYSINFO		10		/* system information (cpu states) */
#define X_CUR_CPU		11

static struct nlist nlst[] = {
	{"v"},						/* 0 */
	{"proc"},					/* 1 */
	{"freemem"},				/* 2 */
	{"availrmem"},				/* 3 */
	{"availsmem"},				/* 4 */
	{"maxmem"},					/* 5 */
	{"physmem"},				/* 6 */
	{"nswap"},					/* 7 */
	{"Hz"},						/* 8 */
	{"mpid"},					/* 9 */
	{"sysinfo"},				/* 10 */
	{"cur_cpu"},				/* 11 */
	{NULL}
};

/*
 *	These definitions control the format of the per-process area
 */

static char header[] =
"  PID X        PRI NICE   SIZE   RES  STATE   TIME  COMMAND";

/* 0123456	 -- field to fill in starts at header+6 */
#define UNAME_START 6

#define Proc_format \
	"%5d %-8.8s %3d %4d  %5s %5dK %-5s %6s  %.28s"

static int	kmem,
			mem;

static double logcpu;

/* these are retrieved from the kernel in _init */
static int	Hz;
static struct var v;
static ulong proca;
static load_avg cur_cpu;

/* these are for detailing the process states */
int			process_states[8];
char	   *procstatenames[] = {
	"", " sleeping, ", " running, ", " zombie, ", " stopped, ",
	" created, ", " onproc, ", " xswapped, ",
	NULL
};

/* process state names for the "STATE" column of the display */
char	   *state_abbrev[] = {
	"", "sleep", "run", "zomb", "stop", "create", "onpr", "swap"
};

/* these are for calculating cpu state percentages */
#define CPUSTATES		5		/* definition from struct sysinfo */
static time_t cp_time[CPUSTATES];
static time_t cp_old[CPUSTATES];
static time_t cp_diff[CPUSTATES];

/* these are for detailing the cpu states */
int			cpu_states[CPUSTATES];
char	   *cpustatenames[] = {
	"idle", "user", "system", "wait", "sxbrk",
	NULL
};

/* these are for detailing the memory statistics */
unsigned long memory_stats[6];
char	   *memorynames[] = {
	"K phys, ", "K max, ", "K free, ", "K lck, ", "K unlck, ",
	"K swap,", NULL
};

/* these are for keeping track of the proc array */
static int	bytes;
static int	pref_len;
static struct proc *pbase;
static struct proc **pref;

/* forward definitions for comparison functions */
int			proc_compare();
int			compare_cpu();
int			compare_size();
int			compare_time();

int			(*proc_compares[]) () =
{
	proc_compare,				/* state, pri, time, size */
		compare_cpu,			/* cpu, time, state, pri, size */
		compare_size,			/* size, cpu, time, state pri  */
		compare_time,			/* time, cpu, state, pri, size */
/* compare_res,		/* res,  cpu, time, state pri  */
		NULL
};

/* these are names given to allowed sorting orders -- first is default */
char	   *ordernames[] = {
	"state", "cpu", "size", "time", NULL
};								/* hops */

/* useful externals */
extern int	errno;
extern char *sys_errlist[];

long		time();
long		percentages();

int
machine_init(struct statics *statics)

{
	ulong		ptr;

	if ((kmem = open(KMEM, O_RDONLY)) == -1)
	{
		perror(KMEM);
		return -1;
	}
	if ((mem = open(MEM, O_RDONLY)) == -1)
	{
		perror(MEM);
		return -1;
	}

	/* get the list of symbols we want to access in the kernel */
	if (nlist(VMUNIX, nlst) == -1)
	{
		fprintf(stderr, "pg_top: nlist failed\n");
		return -1;
	}
	/* make sure they were all found */

	/*
	 * ZZ if (check_nlist(nlst) > 0) return -1;
	 */

	proca = nlst[X_PROC].n_value;

	/* get the symbol values out of kmem */
	(void) getkval(nlst[X_CUR_CPU].n_value, (int *) (&cur_cpu), sizeof(cur_cpu),
				   nlst[X_CUR_CPU].n_name);
	(void) getkval(nlst[X_HZ].n_value, (int *) (&Hz), sizeof(Hz),
				   nlst[X_HZ].n_name);
	(void) getkval(nlst[X_V].n_value, (int *) (&v), sizeof(v),
				   nlst[X_V].n_name);

	/* this is used in calculating WCPU -- calculate it ahead of time */
	logcpu = log(fabs(loaddouble(cur_cpu)));

	/* allocate space for proc structure array and array of pointers */
	bytes = v.v_proc * sizeof(struct proc);
	pbase = (struct proc *) malloc(bytes);
	pref = (struct proc **) malloc(v.v_proc * sizeof(struct proc *));
	if (pbase == (struct proc *) NULL || pref == (struct proc **) NULL)
	{
		fprintf(stderr, "pg_top: cannot allocate sufficient memory\n");
		return -1;
	}

	/* fill in the statics information */
	statics->procstate_names = procstatenames;
	statics->cpustate_names = cpustatenames;
	statics->memory_names = memorynames;
	statics->order_names = ordernames;	/* hops */

	return 0;
}

char *
format_header(register char *uname_field)

{
	register char *ptr;

	ptr = header + UNAME_START;
	while (*uname_field != '\0')
	{
		*ptr++ = *uname_field++;
	}

	return (header);
}


/* philiph - get run ave fm /dev/table info */
static int
tab_avenrun(double runave[])
{
	FILE	   *fp = fopen("/dev/table/avenrun", "r");
	int			i;

	for (i = 0; i < 3; i++)
		runave[i] = -1.0;

	if (fp == NULL)
		return -1;
	else
	{
		short		rawave[3];

		if (fread(rawave, sizeof(short), 3, fp) != 3)
		{
			fclose(fp);
			return -1;
		}
		else
		{
			int			i;

			for (i = 0; i < 3; i++)
				runave[i] = (double) (rawave[i] / 256.0);

			fclose(fp);
			return 0;
		}
	}
}

struct pregion *
get_pregion(void *ptr)
{
	static struct pregion preg;
	long		addr = (long) ptr;

	(void) getkval(addr, (struct pregion *) (&preg),
				   sizeof(struct pregion), "pregion");
	return &preg;
}

struct region *
get_region(void *ptr)
{
	static struct region reg;
	long		addr = (long) ptr;

	(void) getkval(addr, (struct region *) (&reg),
				   sizeof(struct region), "region");
	return &reg;
}

static unsigned char shareable[RT_VM86 + 1];	/* 1 if shareable */

/*
 * sum private referenced pages,
 * treat shared pages depending on value of TREAT_SHARABLE_PAGES macro
 *		undefined : ignore (don't account for - default)
 *		1:	divide among # of references
 *		2:	accumulate as if private
 */
/* #define TREAT_SHAREABLE_PAGES 1 */
static long
proc_residentsize(struct proc *pp)
{
	struct pregion *prp;
	struct region *rp;
	long		rtot = 0;
	long		stot = 0;
	long		s1tot = 0;

	/* init shareable region array */
	if (shareable[RT_STEXT] == 0)
		shareable[RT_STEXT] = shareable[RT_SHMEM] = shareable[RT_MAPFILE] = 1
			;

	prp = pp->p_region;

	if (prp == 0)
		return 0;

	for (; prp && (prp = get_pregion((void *) (prp))) &&
		 prp->p_reg && (rp = get_region((void *) (prp->p_reg)));
		 prp = prp->p_next)
	{
		if (shareable[rp->r_type])	/* account for shared pgs separately */
		{
			stot += (rp->r_nvalid / rp->r_refcnt);
			s1tot += rp->r_nvalid;
		}
		else
			rtot += rp->r_nvalid;

	}
#if defined(TREAT_SHAREABLE_PAGES) && TREAT_SHAREABLE_PAGES == 1
	rtot += stot;				/* accumulate and spread over users */
#endif

#if defined(TREAT_SHAREABLE_PAGES) && TREAT_SHAREABLE_PAGES == 1
	rtot += s1tot;				/* accumulate as if private */
#endif

	return rtot * NBPP / 1024;;
}

void
get_system_info(struct system_info *si)

{
	long		total;

	/* get process id of the last process */
	(void) getkval(nlst[X_MPID].n_value, &(si->last_pid),
				   sizeof(si->last_pid),
				   nlst[X_MPID].n_name);
	/* get the cp_time array */
	(void) getkval(nlst[X_SYSINFO].n_value, (int *) cp_time, sizeof(cp_time),
				   nlst[X_SYSINFO].n_name);

	/* convert cp_time counts to persentages */
	total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);

	/* sum memory statistics */
	(void) getkval(nlst[X_PHYSMEM].n_value, &memory_stats[0],
				   sizeof(memory_stats[0]), nlst[X_PHYSMEM].n_name);
	(void) getkval(nlst[X_MAXMEM].n_value, &memory_stats[1],
				   sizeof(memory_stats[1]), nlst[X_MAXMEM].n_name);
	(void) getkval(nlst[X_FREEMEM].n_value, &memory_stats[2],
				   sizeof(memory_stats[2]), nlst[X_FREEMEM].n_name);
	(void) getkval(nlst[X_AVAILRMEM].n_value, &memory_stats[3],
				   sizeof(memory_stats[3]), nlst[X_AVAILRMEM].n_name);
	(void) getkval(nlst[X_AVAILSMEM].n_value, &memory_stats[4],
				   sizeof(memory_stats[4]), nlst[X_AVAILSMEM].n_name);
	(void) getkval(nlst[X_NSWAP].n_value, &memory_stats[5],
				   sizeof(memory_stats[5]), nlst[X_NSWAP].n_name);
	memory_stats[0] = bytetok(ctob(memory_stats[0]));	/* clicks -> bytes */
	memory_stats[1] = bytetok(ctob(memory_stats[1]));	/* clicks -> bytes */
	memory_stats[2] = bytetok(ctob(memory_stats[2]));	/* clicks -> bytes */
	memory_stats[3] = bytetok(memory_stats[3] * NBPP);	/* # bytes per page */
	memory_stats[4] = bytetok(memory_stats[4] * NBPP);	/* # bytes per page */
	memory_stats[5] = bytetok(memory_stats[5] * NBPSCTR);	/* # bytes per sector */

	/* set arrays and strings */

	/*
	 * Note: we keep memory_stats as an unsigned long to avoid sign extension
	 * problems when shifting in bytetok. But the module interface requires an
	 * array of signed longs. So we just cast the pointer here and hope for
	 * the best.   --wnl
	 */
	si->cpustates = cpu_states;
	si->memory = (long *) memory_stats;

	tab_avenrun(si->load_avg);	/* philiph */
}

static struct handle handle;

caddr_t
get_process_info(struct system_info *si,
				 struct process_select *sel,
				 int idx)

{
	register int i;
	register int total_procs;
	register int active_procs;
	register struct proc **prefp;
	register struct proc *pp;

/* set up flags of what we are going to select */
/* these are copied out of sel for simplicity */
	int			show_idle = sel->idle;
	int			show_system = sel->system;
	int			show_uid = sel->uid != -1;
	int			show_command = sel->command != NULL;

	/* read all the proc structures in one fell swoop */
	(void) getkval(proca, (int *) pbase, bytes, "proc array");

	/* get a pointer to the states summary array */
	si->procstates = process_states;

	/* count up process states and get pointers to interesting procs */
	total_procs = active_procs = 0;
	memset((char *) process_states, 0, sizeof(process_states));
	prefp = pref;
	for (pp = pbase, i = 0; i < v.v_proc; pp++, i++)
	{
		/*
		 * Place pointers to each valid proc structure in pref[]. Process
		 * slots that are actually in use have a non-zero status field.
		 * Processes with SSYS set are system processes -- these are ignored
		 * unless show_system is set.
		 */
		if (pp->p_stat && (show_system || ((pp->p_flag & SSYS) == 0)))
		{
			total_procs++;
			process_states[pp->p_stat]++;
			if ((pp->p_stat != SZOMB) &&
				(show_idle || (pp->p_stat == SRUN) || (pp->p_stat == SONPROC)) &&
				(!show_uid || pp->p_uid == (ushort) sel->uid))
			{
				*prefp++ = pp;
				active_procs++;
			}
		}
	}

	/* if requested, sort the "interesting" processes */
	qsort((char *) pref, active_procs, sizeof(struct proc *), proc_compares[idx]);

	/* remember active and total counts */
	SI_TOTAL(si) = total_procs;
	SI_ACTIVE(si) = pref_len = active_procs;

	/* pass back a handle */
	handle.next_proc = pref;
	handle.remaining = active_procs;
	return ((caddr_t) & handle);
}

char		fmt[128];			/* static area where result is built */

char *
format_next_process(caddr_t handle, char *(*get_userid) ())

{
	register struct proc *pp;
	register time_t cputime;
	register double pct;
	int			where;
	struct user u;
	struct handle *hp;
	char		command[29];
	char	   *process;
	char	   *process2;

	/* find and remember the next proc structure */
	hp = (struct handle *) handle;
	pp = *(hp->next_proc++);
	hp->remaining--;

	/* get the process's user struct and set cputime */
	if ((where = sysi86(RDUBLK, pp->p_pid, &u, sizeof(struct user))) != -1)
		where = (pp->p_flag & SLOAD) ? 0 : 1;
	if (where == -1)
	{
		strcpy(command, "<swapped>");
		cputime = 0;
	}
	else
	{
		/* set u_comm for system processes */
		if (u.u_comm[0] == '\0')
		{
			if (pp->p_pid == 0)
				strcpy(command, "Swapper");
			else if (pp->p_pid == 2)
				strcpy(command, "Pager");
			else if (pp->p_pid == 3)
				strcpy(command, "Sync'er");
		}
		else if (where == 1)
		{
			/* print swapped processes as <pname> */
			register char *s1;

			u.u_psargs[28 - 3] = '\0';
			strcpy(command, "<");
			strcat(command, strtok(u.u_psargs, " "));
			strcat(command, ">");
			while (s1 = (char *) strtok(NULL, " "))
				strcat(command, s1);
		}
		else
		{
			sprintf(command, "%s", u.u_psargs);
		}
		cputime = u.u_utime + u.u_stime;
/*	   cputime = pp->p_utime + pp->p_stime;  */
	}
	/* calculate the base for cpu percentages */
	pct = pctdouble(pp->p_cpu);

	/*
	 * psargs gives the absolute path of the process... strip it to only the
	 * command - [Changes by D. Currie & M. Muldner Aitt NS Canada]
	 */
	process = printable(command);
#if NO_COMMAND_ARGS
	strtok(process, " ");
#endif
	process2 = strrchr(process, '/');
	if (process2)
	{
		process = process2;
		process++;
	}


	/* format this entry */
	sprintf(fmt,
			Proc_format,
			pp->p_pid,
			(*get_userid) (pp->p_uid),
			pp->p_pri - PZERO,
			pp->p_nice - NZERO,
			format_k(PROCSIZE(&u)), /* same as	pp->p_size * 4 */
			proc_residentsize(pp),
			state_abbrev[pp->p_stat],
			format_time(cputime / Hz),
			printable(process));

	return (fmt);
}

/*
 * Checks the nlist to see if any symbols were not found.
 * For every symbol that was not found, a one-line message
 * is printed to stderr. The routine returns the number of
 * symbols NOT founded.
 */

int
check_nlist(register struct nlist *nlst)

{
	register int i = 0;

	while (nlst->n_name)
	{
		if (nlst->n_type == 0)
		{
			fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
			i++;
		}
		nlst++;
	}
	return i;
}

/*
 *	getkval(offset, ptr, size, refstr) - get a value out of the kernel.
 *		"offset" is the byte offset into the kernel for the desired value,
 *		"ptr" points to a buffer into which the value is retrieved,
 *		"size" is the size of the buffer (and the object to retrieve),
 *		"refstr" is a reference string used when printing error meessages,
 *			if "refstr" starts with a '!', then a failure on read will not
 *			be fatal (this may seem like a silly way to do things, but I
 *			really didn't want the overhead of another argument).
 *
 */

int
getkval(unsigned long offset, int *ptr, int size, char *refstr)

{
	if (lseek(kmem, (long) offset, SEEK_SET) == -1)
	{
		if (*refstr == '!')
			refstr++;
		fprintf(stderr, "%s: lseek to %s: %s\n", KMEM,
				refstr, errmsg(errno));
		quit(23);
	}
	if (read(kmem, (char *) ptr, size) == -1)
	{
		if (*refstr == '!')
			return 0;
		fprintf(stderr, "%s: reading %s: %s\n", KMEM,
				refstr, errmsg(errno));
		quit(23);
	}
	return (1);
}

/* comparison routine for qsort */
/* NOTE: this is specific to the BSD proc structure, but it should
   give you a good place to start. */

/*
 *	proc_compare - comparison function for "qsort"
 *		Compares the resource consumption of two processes using five
 *		distinct keys.	The keys (in descending order of importance) are:
 *		percent cpu, cpu ticks, state, resident set size, total virtual
 *		memory usage.  The process states are ordered as follows (from least
 *		to most important):  WAIT, zombie, sleep, stop, start, run.  The
 *		array declaration below maps a process state index into a number
 *		that reflects this ordering.
 */

static unsigned char sorted_state[] =
{
	0,							/* not used				*/
	5,							/* sleep				*/
	6,							/* run					*/
	2,							/* zombie				*/
	4,							/* stop					*/
	1,							/* start				*/
	7,							/* onpr					*/
	3,							/* swap					*/
};

int
proc_compare(struct proc **pp1, struct proc **pp2)

{
	register struct proc *p1;
	register struct proc *p2;
	register int result;
	register ulong lresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;

	/* use process state to break the tie */
	if ((result = sorted_state[p2->p_stat] -
		 sorted_state[p1->p_stat]) == 0)
	{
		/* use priority to break the tie */
		if ((result = p2->p_pri - p1->p_pri) == 0)
		{
			/* use time to break the tie */
			if ((result = (p2->p_utime + p2->p_stime) -
				 (p1->p_utime + p1->p_stime)) == 0)
			{
				/* use resident set size (rssize) to break the tie */
				if ((result = p2->p_size - p1->p_size) == 0)
				{
					result = 0;
				}
			}
		}
	}

	return (result);
}

/* returns uid of owner of process pid */
int
proc_owner(int pid)

{
	register int cnt;
	register struct proc **prefp;
	register struct proc *pp;

	prefp = pref;
	cnt = pref_len;
	while (--cnt >= 0)
	{
		if ((pp = *prefp++)->p_pid == (short) pid)
			return ((int) pp->p_uid);
	}
	return (-1);
}

#if 0
int
setpriority(int dummy, int who, int nicewal)
{
	errno = 1;
	return -1;
}
#endif

/* sigblock is not POSIX conformant */
sigset_t
sigblock(sigset_t mask)
{
	sigset_t	oset;

	sigemptyset(&oset);
	sigprocmask(SIG_BLOCK, &mask, &oset);
	return oset;
}

/* sigsetmask is not POSIX conformant */
sigsetmask(sigset_t mask)
{
	sigset_t	oset;

	sigemptyset(&oset);
	sigprocmask(SIG_SETMASK, &mask, &oset);
	return oset;
}


/* ---------------- hops - comparison/ordering support ---------------- */

#define ORDERKEY_PCTCPU  if (dresult = pctdouble(p2->p_cpu) - pctdouble(p1->p_cpu),\
				 (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0)
#define ORDERKEY_MEMSIZE if ((result = (p2->p_size - p1->p_size)) == 0)
#define ORDERKEY_CPTIME  if ((result = (long)(p2->p_utime + p2->p_stime) -\
					   (long)(p1->p_utime + p1->p_stime)) == 0)

#define ORDERKEY_STATE	 if ((result = (sorted_state[p2->p_stat] - \
				   sorted_state[p1->p_stat])) == 0)
#define ORDERKEY_PRIO	 if ((result = p2->p_pri - p1->p_pri) == 0)


int
compare_cpu(struct proc **pp1, struct proc **pp2)
{
	register struct proc *p1;
	register struct proc *p2;
	register int result;
	double		dresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;

	ORDERKEY_PCTCPU
		ORDERKEY_CPTIME
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_MEMSIZE
		;

	return (result);
}



/* compare_size - the comparison function for sorting by process size */
int
compare_size(struct proc **pp1, struct proc **pp2)
{
	register struct proc *p1;
	register struct proc *p2;
	register int result;
	double		dresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;


	ORDERKEY_MEMSIZE
		ORDERKEY_PCTCPU
		ORDERKEY_CPTIME
		ORDERKEY_STATE
		ORDERKEY_PRIO
		;

	return (result);
}

/* compare_res - the comparison function for sorting by resident set size */
/* TODO: add shadow proc struct updating usr + sys times and RSS for use
 * in comparison rtns, implement compare_res rtn as per compare_size()
 */

/* compare_time - the comparison function for sorting by total cpu time */
/* This is giving wrong results since its using the proc structure vals not
 * the u struct vals we display above
 * TODO: add shadow proc struct updating usr + sys times and RSS for use
 * in comparison rtns
 */
int
compare_time(struct proc **pp1, struct proc **pp2)
{
	register struct proc *p1;
	register struct proc *p2;
	register int result;
	double		dresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;

	ORDERKEY_CPTIME
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_MEMSIZE
		;

	return (result);
}

void
get_io_info(struct io_info *io_info)
{
	/* Not supported yet */
	memset(io_info, 0, sizeof(*io_info));
}
07070100000030000081A400000000000000000000000166622DE800000145000000000000000000000000000000000000002400000000pg_top-4.1.1/machine/m_sco5.man.rstSCO UNIX NOTES
==============

The SCO OpenServer5 port is a modification of the SCO Unix port
done by Mike Hopkirk (hops@sco.com).
OpenServer5 is a more normal Unix although the proc variables are still
somewhat funky.  No easy access to RSS memory and CPUTICKS.
Added support for ordering and enabled use of setpriority().
07070100000031000081A400000000000000000000000166622DE80000A2D2000000000000000000000000000000000000002000000000pg_top-4.1.1/machine/m_sunos5.c/*
 * pg_top - a top PostgreSQL users display for Unix
 *
 * SYNOPSIS:  Any Sun running SunOS 5.x (Solaris 2.x)
 *
 * DESCRIPTION:
 * This is the machine-dependent module for SunOS 5.x (Solaris 2).
 * There is some support for MP architectures.
 * This makes pg_top work on all revisions of SunOS 5 from 5.0
 * through 5.9 (otherwise known as Solaris 9).	It has not been
 * tested on SunOS 5.10.
 *
 * LIBS: -lelf -lkvm -lkstat
 *
 * CFLAGS: -DHAVE_GETOPT -DORDER -DHAVE_STRERROR -DUSE_SIZE_T
 *
 *
 * AUTHORS:		 Torsten Kasch		<torsten@techfak.uni-bielefeld.de>
 *				 Robert Boucher		<boucher@sofkin.ca>
 * CONTRIBUTORS: Marc Cohen			<marc@aai.com>
 *				 Charles Hedrick	<hedrick@geneva.rutgers.edu>
 *				 William L. Jones	<jones@chpc>
 *				 Petri Kutvonen		<kutvonen@cs.helsinki.fi>
 *				 Casper Dik			<casper.dik@sun.com>
 *				 Tim Pugh			<tpugh@oce.orst.edu>
 *				 Mark Wong			<markwkm@gmail.com>
 */

#define _KMEMUSER

#include "config.h"

#if (OSREV == 551)
#undef OSREV
#define OSREV 55
#endif

/* kernels 5.4 and above track pctcpu in the proc structure,
   but the results are less than desirable, so we continue to
   pretend that they don't and just calculate it on our own
*/
#if (OSREV >= 54)
/* #define PROC_HAS_PCTCPU */
#endif

#define USE_NEW_PROC
#if defined(USE_NEW_PROC) && OSREV >= 56
#define _STRUCTURED_PROC 1
#define prpsinfo psinfo
#include <sys/procfs.h>
#define pr_fill pr_nlwp
/* These require an ANSI C compiler "Reisser cpp" doesn't like this */
#define pr_state pr_lwp.pr_state
#define pr_oldpri pr_lwp.pr_oldpri
#define pr_nice pr_lwp.pr_nice
#define pr_pri pr_lwp.pr_pri
#define pr_onpro pr_lwp.pr_onpro
#define ZOMBIE(p)	((p)->pr_nlwp == 0)
#define SIZE_K(p)	(long)((p)->pr_size)
#define RSS_K(p)	(long)((p)->pr_rssize)
#else
#undef USE_NEW_PROC
#define ZOMBIE(p)	((p)->pr_zomb)
#define SIZE_K(p)	(long)((p)->pr_bysize/1024)
#define RSS_K(p)	(long)((p)->pr_byrssize/1024)
#define pr_onpro	pr_filler[5]
#endif

#include "pg_top.h"
#include "machine.h"
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <dirent.h>
#include <nlist.h>
#include <string.h>
#include <kvm.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/signal.h>
#include <sys/fault.h>
#include <sys/sysinfo.h>
#include <sys/sysmacros.h>
#include <sys/syscall.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/procfs.h>
#include <sys/vm.h>
#include <sys/var.h>
#include <sys/cpuvar.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/priocntl.h>
#include <sys/tspriocntl.h>
#include <sys/processor.h>
#include <sys/swap.h>
#include <vm/anon.h>
#include <math.h>
#include <utmpx.h>
#include "utils.h"

#if OSREV >= 53
#define USE_KSTAT
#endif
#ifdef USE_KSTAT
#include <kstat.h>
/*
 * Some kstats are fixed at 32 bits, these will be specified as ui32; some
 * are "natural" size (32 bit on 32 bit Solaris, 64 on 64 bit Solaris
 * we'll make those unsigned long)
 * Older Solaris doesn't define KSTAT_DATA_UINT32, those are always 32 bit.
 */
#ifndef KSTAT_DATA_UINT32
#define ui32 ul
#endif
#endif

#define UNIX "/dev/ksyms"
#define KMEM "/dev/kmem"
#define PROCFS "/proc"
#define CPUSTATES	  5
#ifndef PRIO_MIN
#define PRIO_MIN	-20
#endif
#ifndef PRIO_MAX
#define PRIO_MAX	20
#endif

#ifndef FSCALE
#define FSHIFT	8				/* bits to right of fixed binary point */
#define FSCALE	(1<<FSHIFT)
#endif							/* FSCALE */

#define loaddouble(la) ((double)(la) / FSCALE)
#define dbl_align(x)	(((unsigned long)(x)+(sizeof(double)-1)) & \
						~(sizeof(double)-1))
#ifdef PROC_HAS_PCTCPU

 /*
  * snarfed from <sys/procfs.h>: The following percent numbers are 16-bit
  * binary fractions [0 .. 1] with the binary point to the right of the
  * high-order bit (one == 0x8000)
  */
#define percent_cpu(pp) (((double)pp->pr_pctcpu)/0x8000*100)
#define weighted_cpu(pp) (*(double *)dbl_align(pp->pr_filler))
#else
#define percent_cpu(pp) (*(double *)dbl_align(&pp->pr_filler[0]))
#define weighted_cpu(pp) (*(double *)dbl_align(&pp->pr_filler[2]))
#endif

/* definitions for indices in the nlist array */
#define X_V			 0
#define X_MPID			 1
#define X_ANONINFO		 2
#define X_MAXMEM		 3
#define X_FREEMEM		 4
#define X_AVENRUN		 5
#define X_CPU			 6
#define X_NPROC			 7
#define X_NCPUS			 8

static struct nlist nlst[] =
{
	{"v"},						/* 0 */	/* replaced by dynamic allocation */
	{"mpid"},					/* 1 */
#if OSREV >= 56
	/* this structure really has some extra fields, but the first three match */
	{"k_anoninfo"},				/* 2 */
#else
	{"anoninfo"},				/* 2 */
#endif
	{"maxmem"},					/* 3 */	/* use sysconf */
	{"freemem"},				/* 4 */	/* available from kstat >= 2.5 */
	{"avenrun"},				/* 5 */	/* available from kstat */
	{"cpu"},					/* 6 */	/* available from kstat */
	{"nproc"},					/* 7 */	/* available from kstat */
	{"ncpus"},					/* 8 */	/* available from kstat */
	{0}
};

static unsigned long avenrun_offset;
static unsigned long mpid_offset;

#ifdef USE_KSTAT
static kstat_ctl_t * kc = NULL;
static kid_t kcid = 0;
#else
static unsigned long *cpu_offset;
#endif
static unsigned long nproc_offset;
static unsigned long freemem_offset;
static unsigned long maxmem_offset;
static unsigned long anoninfo_offset;
static void reallocproc(int n);
static int	maxprocs = 0;

/* get_process_info passes back a handle.  This is what it looks like: */
struct handle
{
	struct prpsinfo **next_proc;	/* points to next valid proc pointer */
	int			remaining;		/* number of pointers remaining */
};

/*
 * Structure for keeping track of CPU times from last time around
 * the program.  We keep these things in a hash table, which is
 * recreated at every cycle.
 */
struct oldproc
{
	pid_t		oldpid;
	double		oldtime;
	double		oldpct;
};
int			oldprocs;			/* size of table */

#define HASH(x) ((x << 1) % oldprocs)

/*
 * GCC assumes that all doubles are aligned.  Unfortunately it
 * doesn't round up the structure size to be a multiple of 8.
 * Thus we'll get a coredump when going through array.  The
 * following is a size rounded up to 8.
 */
#define PRPSINFOSIZE dbl_align(sizeof(struct prpsinfo))

/*
 *	These definitions control the format of the per-process area
 */
#if OSREV >= 58
static char header[] =
"   PID X        LWP PRI NICE  SIZE   RES STATE    TIME    CPU COMMAND";

/* 0123456	 -- field to fill in starts at header+6 */
#define UNAME_START 7

#define Proc_format \
		"%6d %-8.8s %3d %3d %4d %5s %5s %-6s %6s %5s%% %s"
#else
static char header[] =
"  PID X        LWP PRI NICE  SIZE   RES STATE    TIME    CPU COMMAND";

/* 0123456	 -- field to fill in starts at header+6 */
#define UNAME_START 6

#define Proc_format \
		"%5d %-8.8s %3d %3d %4d %5s %5s %-6s %6s %5s%% %s"
#endif

/* process state names for the "STATE" column of the display */
char	   *state_abbrev[] =
{"", "sleep", "run", "zombie", "stop", "start", "cpu", "swap"};

int			process_states[8];
char	   *procstatenames[] =
{
	"", " sleeping, ", " running, ", " zombie, ", " stopped, ",
	" starting, ", " on cpu, ", " swapped, ",
	NULL
};

int64_t		cpu_states[CPUSTATES];
char	   *cpustatenames[] =
{"idle", "user", "kernel", "iowait", "swap", NULL};

#define CPUSTATE_IOWAIT 3
#define CPUSTATE_SWAP	4


/* these are for detailing the memory statistics */
long		memory_stats[5];
char	   *memorynames[] =
{"K phys mem, ", "K free mem, ", "K total swap, ", "K free swap", NULL};

#define MEMORY_TOTALMEM  0
#define MEMORY_FREEMEM	 1
#define MEMORY_TOTALSWAP 2
#define MEMORY_FREESWAP  3

/* these are names given to allowed sorting orders -- first is default */
char	   *ordernames[] = {"cpu", "size", "res", "time", NULL};
char	   *ordernames_io[] = {NULL};

/* forward definitions for comparison functions */
int			compare_cpu();
int			compare_size();
int			compare_res();
int			compare_time();

int			(*proc_compares[]) () =
{
	compare_cpu,
		compare_size,
		compare_res,
		compare_time,
		NULL
};

kvm_t	   *kd;
static DIR *procdir;
static int	nproc;

/* "cpucount" is used to store the value for the kernel variable "ncpus".
   But since <sys/cpuvar.h> actually defines a variable "ncpus" we need
   to use a different name here.   --wnl */
static int	cpucount;

/* these are for keeping track of the proc array */
static struct prpsinfo *pbase;
static struct prpsinfo **pref;
static struct oldproc *oldbase;

/* pagetok function is really a pointer to an appropriate function */
static int	pageshift;
static long (*p_pagetok) ();

#define pagetok(size) ((*p_pagetok)(size))

/* useful externals */
extern char *myname;
extern int	check_nlist();
extern int	gettimeofday();
extern void perror();
extern void getptable(struct prpsinfo *, PGresult *);
extern void quit();
extern int	nlist();

/* p_pagetok points to one of the following, depending on which
   direction data has to be shifted: */

long
pagetok_none(long size)

{
	return (size);
}

long
pagetok_left(long size)

{
	return (size << pageshift);
}

long
pagetok_right(long size)

{
	return (size >> pageshift);
}

/*
 *	getkval(offset, ptr, size, refstr) - get a value out of the kernel.
 *	"offset" is the byte offset into the kernel for the desired value,
 *		"ptr" points to a buffer into which the value is retrieved,
 *		"size" is the size of the buffer (and the object to retrieve),
 *		"refstr" is a reference string used when printing error meessages,
 *		if "refstr" starts with a '!', then a failure on read will not
 *			be fatal (this may seem like a silly way to do things, but I
 *			really didn't want the overhead of another argument).
 *
 */
int
getkval(unsigned long offset,
		int *ptr,
		int size,
		char *refstr)
{
#ifdef DEBUG
	dprintf("getkval(%08x, %08x, %d, %s)\n", offset, ptr, size, refstr);
#endif							/* DEBUG */

	if (kvm_read(kd, offset, (char *) ptr, size) != size)
	{
#ifdef DEBUG
		dprintf("getkval: read failed\n");
#endif							/* DEBUG */
		if (*refstr == '!')
		{
			return (0);
		}
		else
		{
			fprintf(stderr, "pg_top: kvm_read for %s: %s\n", refstr, strerror(errno));
			quit(23);
		}
	}

#ifdef DEBUG
	dprintf("getkval read %d (%08x)\n", *ptr);
#endif							/* DEBUG */

	return (1);

}

int
machine_init(struct statics *statics)
{
	struct utmpx ut;
	struct utmpx *up;
	int			i;
	char	   *p;

#ifndef USE_KSTAT
	int			offset;
#endif

	/*
	 * There's a buffer overflow bug in curses that can be exploited when we
	 * run as root.  By making sure that TERMINFO is set to something this bug
	 * is avoided.	This code thanks to Casper
	 */
	if ((p = getenv("TERMINFO")) == NULL || *p == '\0')
	{
		putenv("TERMINFO=/usr/share/lib/terminfo/");
	}

	/* perform the kvm_open - suppress error here */
	if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) == NULL)
	{
		/* save the error message: we may need it later */
		p = strerror(errno);
	}
#ifdef DEBUG
	dprintf("kvm_open: fd %d\n", kd);
#endif							/* DEBUG */

	/*
	 * turn off super group/user privs - but beware; we might want the privs
	 * back later and we still have a fd to /dev/kmem open so we can't use
	 * setgid()/setuid() as that would allow a debugger to attach to this
	 * process. CD
	 */
	setegid(getgid());
	seteuid(getuid());			/* super user not needed for NEW_PROC */

#ifdef USE_KSTAT
	if ((kc = kstat_open()) == NULL)
	{
		fprintf(stderr, "Unable to open kstat.\n");
		return (-1);
	}
	kcid = kc->kc_chain_id;
#ifdef DEBUG
	dprintf("kstat_open: chain %d\n", kcid);
#endif							/* DEBUG */
#endif

	/* fill in the statics information */
	statics->procstate_names = procstatenames;
	statics->cpustate_names = cpustatenames;
	statics->memory_names = memorynames;
	statics->order_names = ordernames;
	statics->flags.fullcmds = 1;
	statics->flags.warmup = 1;

	/* get boot time */
	ut.ut_type = BOOT_TIME;
	if ((up = getutxid(&ut)) != NULL)
	{
		statics->boottime = up->ut_tv.tv_sec;
	}
	endutxent();

	/* if the kvm_open succeeded, get the nlist */
	if (kd)
	{
		if (kvm_nlist(kd, nlst) < 0)
		{
			perror("kvm_nlist");
			return (-1);
		}
		if (check_nlist(nlst) != 0)
			return (-1);
	}
#ifndef USE_KSTAT

	/*
	 * if KSTAT is not available to us and we can't open /dev/kmem, this is a
	 * serious problem.
	 */
	else
	{
		/* Print the error message here */
		(void) fprintf(stderr, "kvm_open: %s\n", p);
		return (-1);
	}
#endif

	/* stash away certain offsets for later use */
	mpid_offset = nlst[X_MPID].n_value;
	nproc_offset = nlst[X_NPROC].n_value;
	avenrun_offset = nlst[X_AVENRUN].n_value;
	anoninfo_offset = nlst[X_ANONINFO].n_value;
	freemem_offset = nlst[X_FREEMEM].n_value;
	maxmem_offset = nlst[X_MAXMEM].n_value;


#ifndef USE_KSTAT
	(void) getkval(nlst[X_NCPUS].n_value, (int *) (&cpucount),
				   sizeof(cpucount), "ncpus");

	cpu_offset = (unsigned long *) malloc(cpucount * sizeof(unsigned long));
	for (i = offset = 0; i < cpucount; offset += sizeof(unsigned long))
	{
		(void) getkval(nlst[X_CPU].n_value + offset,
					   (int *) (&cpu_offset[i]), sizeof(unsigned long),
					   nlst[X_CPU].n_name);
		if (cpu_offset[i] != 0)
			i++;
	}
#endif

	/* calculate pageshift value */
	i = sysconf(_SC_PAGESIZE);
	pageshift = 0;
	while ((i >>= 1) > 0)
	{
		pageshift++;
	}

	/* calculate an amount to shift to K values */
	/* remember that log base 2 of 1024 is 10 (i.e.: 2^10 = 1024) */
	pageshift -= 10;

	/*
	 * now determine which pageshift function is appropriate for the result
	 * (have to because x << y is undefined for y < 0)
	 */
	if (pageshift > 0)
	{
		/* this is the most likely */
		p_pagetok = pagetok_left;
	}
	else if (pageshift == 0)
	{
		p_pagetok = pagetok_none;
	}
	else
	{
		p_pagetok = pagetok_right;
		pageshift = -pageshift;
	}

	if (!(procdir = opendir(PROCFS)))
	{
		(void) fprintf(stderr, "Unable to open %s\n", PROCFS);
		return (-1);
	}

	if (chdir(PROCFS))
	{							/* handy for later on when we're reading it */
		(void) fprintf(stderr, "Unable to chdir to %s\n", PROCFS);
		return (-1);
	}

	/* all done! */
	return (0);
}

char *
format_header(register char *uname_field)
{
	register char *ptr;

	ptr = header + UNAME_START;
	while (*uname_field != '\0')
		*ptr++ = *uname_field++;

	return (header);
}

#ifdef USE_KSTAT

long
kstat_data_value_l(kstat_named_t * kn)

{
	switch (kn->data_type)
	{
		case KSTAT_DATA_INT32:
			return ((long) (kn->value.i32));
		case KSTAT_DATA_UINT32:
			return ((long) (kn->value.ui32));
		case KSTAT_DATA_INT64:
			return ((long) (kn->value.i64));
		case KSTAT_DATA_UINT64:
			return ((long) (kn->value.ui64));
	}
	return 0;
}

int
kstat_safe_retrieve(kstat_t * *ksp,
					char *module, int instance, char *name, void *buf)

{
	kstat_t    *ks;
	kid_t		new_kcid;
	int			changed;

#ifdef DEBUG
	dprintf("kstat_safe_retrieve(%08x -> %08x, %s, %d, %s, %08x)\n",
			ksp, *ksp, module, instance, name, buf);
#endif							/* DEBUG */

	ks = *ksp;
	do
	{
		changed = 0;
		/* if we dont already have the kstat, retrieve it */
		if (ks == NULL)
		{
			if ((ks = kstat_lookup(kc, module, instance, name)) == NULL)
			{
				return (-1);
			}
			*ksp = ks;
		}

		/* attempt to read it */
		new_kcid = kstat_read(kc, ks, buf);

		/*
		 * chance for an infinite loop here if kstat_read keeps returning -1
		 */

		/* if the chain changed, update it */
		if (new_kcid != kcid)
		{
#ifdef DEBUG
			dprintf("kstat_safe_retrieve: chain changed to %d...updating\n",
					new_kcid);
#endif							/* DEBUG */
			changed = 1;
			kcid = kstat_chain_update(kc);
		}
	} while (changed);

	return (0);
}

/*
 * int kstat_safe_namematch(int num, kstat_t *ksp, char *name, void *buf)
 *
 * Safe scan of kstat chain for names starting with "name".  Matches
 * are copied in to "ksp", and kstat_read is called on each match using
 * "buf" as a buffer of length "size".	The actual number of records
 * found is returned.  Up to "num" kstats are copied in to "ksp", but
 * no more.  If any kstat_read indicates that the chain has changed, then
 * the whole process is restarted.
 */

int
kstat_safe_namematch(int num, kstat_t * *ksparg, char *name, void *buf, int size)

{
	kstat_t    *ks;
	kstat_t   **ksp;
	kid_t		new_kcid;
	int			namelen;
	int			count;
	int			changed;
	char	   *cbuf;

#ifdef DEBUG
	dprintf("kstat_safe_namematch(%d, %08x, %s, %08x, %d)\n",
			num, ksp, name, buf, size);
#endif							/* DEBUG */

	namelen = strlen(name);

	do
	{
		/* initialize before the scan */
		cbuf = (char *) buf;
		ksp = ksparg;
		count = 0;
		changed = 0;

		/* scan the chain for matching kstats */
		for (ks = kc->kc_chain; ks != NULL; ks = ks->ks_next)
		{
			if (strncmp(ks->ks_name, name, namelen) == 0)
			{
				/* this kstat matches: save it if there is room */
				if (count++ < num)
				{
					/* read the kstat */
					new_kcid = kstat_read(kc, ks, cbuf);

					/* if the chain changed, update it */
					if (new_kcid != kcid)
					{
#ifdef DEBUG
						dprintf("kstat_safe_namematch: chain changed to %d...updating\n",
								new_kcid);
#endif							/* DEBUG */
						changed = 1;
						kcid = kstat_chain_update(kc);

						/* there's no sense in continuing the scan */
						/* so break out of the for loop */
						break;
					}

					/* move to the next buffers */
					cbuf += size;
					*ksp++ = ks;
				}
			}
		}
	} while (changed);

#ifdef DEBUG
	dprintf("kstat_safe_namematch returns %d\n", count);
#endif							/* DEBUG */

	return count;
}

static kstat_t * ks_system_misc = NULL;
#endif							/* USE_KSTAT */


int
get_avenrun(int avenrun[3])

{
#ifdef USE_KSTAT
	int			status;
	kstat_named_t *kn;

#ifdef DEBUG
	dprintf("get_avenrun(%08x)\n", avenrun);
#endif							/* DEBUG */

	if ((status = kstat_safe_retrieve(&ks_system_misc,
									  "unix", 0, "system_misc", NULL)) == 0)
	{
		if ((kn = kstat_data_lookup(ks_system_misc, "avenrun_1min")) != NULL)
		{
			avenrun[0] = kn->value.ui32;
		}
		if ((kn = kstat_data_lookup(ks_system_misc, "avenrun_5min")) != NULL)
		{
			avenrun[1] = kn->value.ui32;
		}
		if ((kn = kstat_data_lookup(ks_system_misc, "avenrun_15min")) != NULL)
		{
			avenrun[2] = kn->value.ui32;
		}
	}
#ifdef DEBUG
	dprintf("get_avenrun returns %d\n", status);
#endif							/* DEBUG */
	return (status);
#else							/* !USE_KSTAT */

	(void) getkval(avenrun_offset, (int *) avenrun, sizeof(int[3]), "avenrun");

	return 0;
#endif							/* USE_KSTAT */
}

int
get_ncpus()

{
#ifdef USE_KSTAT
	kstat_named_t *kn;
	int			ret = -1;

	if ((kn = kstat_data_lookup(ks_system_misc, "ncpus")) != NULL)
	{
		ret = (int) (kn->value.ui32);
	}

	return ret;
#else
	int			ret;

	(void) getkval(nlst[X_NCPUS].n_value, (int *) (&ret), sizeof(ret), "ncpus");
	return ret;
#endif
}

int
get_nproc()

{
#ifdef USE_KSTAT
	kstat_named_t *kn;
	int			ret = -1;

	if ((kn = kstat_data_lookup(ks_system_misc, "nproc")) != NULL)
	{
		ret = (int) (kn->value.ui32);
	}

	return ret;
#else
	int			ret;

	(void) getkval(nproc_offset, (int *) (&ret), sizeof(ret), "nproc");
	return ret;
#endif
}

int64_t
(*get_cpustats(int *cnt, int64_t(*cp_stats)[CPUSTATES]))[CPUSTATES]

{
#ifdef USE_KSTAT
	static kstat_t * *cpu_ks = NULL;
	static cpu_stat_t * cpu_stat = NULL;
	static unsigned int nelems = 0;
	cpu_stat_t *cpu_stat_p;
	int			i,
				cpu_num;

	int64_t(*cp_stats_p)[CPUSTATES];

#ifdef DEBUG
	dprintf("get_cpustats(%d -> %d, %08x)\n", cnt, *cnt, cp_stats);
#endif							/* DEBUG */

	while (nelems > 0 ?
		   (cpu_num = kstat_safe_namematch(nelems,
										   cpu_ks,
										   "cpu_stat",
										   cpu_stat,
										   sizeof(cpu_stat_t))) > nelems :
		   (cpu_num = get_ncpus()) > 0)
	{
		/* reallocate the arrays */
#ifdef DEBUG
		dprintf("realloc from %d to %d\n", nelems, cpu_num);
#endif							/* DEBUG */
		nelems = cpu_num;
		if (cpu_ks != NULL)
		{
			free(cpu_ks);
		}
		cpu_ks = (kstat_t * *) calloc(nelems, sizeof(kstat_t *));
		if (cpu_stat != NULL)
		{
			free(cpu_stat);
		}
		cpu_stat = (cpu_stat_t *) malloc(nelems * sizeof(cpu_stat_t));
	}

	/* do we have more cpus than our caller? */
	if (cpu_num > *cnt)
	{
		/* yes, so realloc their array, too */
#ifdef DEBUG
		dprintf("realloc array from %d to %d\n", *cnt, cpu_num);
#endif							/* DEBUG */
		*cnt = cpu_num;
		cp_stats = (int64_t(*)[CPUSTATES]) realloc(cp_stats,
												   cpu_num * sizeof(int64_t) * CPUSTATES);
	}

	cpu_stat_p = cpu_stat;
	cp_stats_p = cp_stats;
	for (i = 0; i < cpu_num; i++)
	{
#ifdef DEBUG
		dprintf("cpu %d %08x: idle %u, user %u, syscall %u\n", i, cpu_stat_p,
				cpu_stat_p->cpu_sysinfo.cpu[0],
				cpu_stat_p->cpu_sysinfo.cpu[1],
				cpu_stat_p->cpu_sysinfo.syscall);
#endif							/* DEBUG */

		(*cp_stats_p)[CPU_IDLE] = (int64_t) cpu_stat_p->cpu_sysinfo.cpu[CPU_IDLE];
		(*cp_stats_p)[CPU_USER] = (int64_t) cpu_stat_p->cpu_sysinfo.cpu[CPU_USER];
		(*cp_stats_p)[CPU_KERNEL] = (int64_t) cpu_stat_p->cpu_sysinfo.cpu[CPU_KERNEL];
		(*cp_stats_p)[CPUSTATE_IOWAIT] = (int64_t) cpu_stat_p->cpu_sysinfo.wait[W_IO] +
			cpu_stat_p->cpu_sysinfo.wait[W_PIO];
		(*cp_stats_p)[CPUSTATE_SWAP] = (int64_t) cpu_stat_p->cpu_sysinfo.wait[W_SWAP];
		cp_stats_p++;
		cpu_stat_p++;
	}

	cpucount = cpu_num;

#ifdef DEBUG
	dprintf("get_cpustats sees %d cpus and returns %08x\n", cpucount, cp_stats);
#endif							/* DEBUG */

	return (cp_stats);
#else							/* !USE_KSTAT */
	int			i;
	struct cpu	cpu;

	int64_t(*cp_stats_p)[CPUSTATES];

	/* do we have more cpus than our caller? */
	if (cpucount > *cnt)
	{
		/* yes, so realloc their array, too */
#ifdef DEBUG
		dprintf("realloc array from %d to %d\n", *cnt, cpucount);
#endif							/* DEBUG */
		*cnt = cpucount;
		cp_stats = (int64_t(*)[CPUSTATES]) realloc(cp_stats,
												   cpucount * sizeof(int64_t) * CPUSTATES);
	}

	cp_stats_p = cp_stats;
	for (i = 0; i < cpucount; i++)
	{
		if (cpu_offset[i] != 0)
		{
			/* get struct cpu for this processor */
			(void) getkval(cpu_offset[i], (int *) (&cpu), sizeof(struct cpu), "cpu");

			(*cp_stats_p)[CPU_IDLE] = (int64_t) cpu.cpu_stat.cpu_sysinfo.cpu[CPU_IDLE];
			(*cp_stats_p)[CPU_USER] = (int64_t) cpu.cpu_stat.cpu_sysinfo.cpu[CPU_USER];
			(*cp_stats_p)[CPU_KERNEL] = (int64_t) cpu.cpu_stat.cpu_sysinfo.cpu[CPU_KERNEL];
			(*cp_stats_p)[CPUSTATE_IOWAIT] = (int64_t) cpu.cpu_stat.cpu_sysinfo.wait[W_IO] +
				cpu.cpu_stat.cpu_sysinfo.wait[W_PIO];
			(*cp_stats_p)[CPUSTATE_SWAP] = (int64_t) cpu.cpu_stat.cpu_sysinfo.wait[W_SWAP];
			cp_stats_p++;
		}
	}

	return (cp_stats);
#endif							/* USE_KSTAT */
}

/*
 * void get_meminfo(long *total, long *fr)
 *
 * Get information about the system's physical memory.  Pass back values
 * for total available and amount of memory that is free (in kilobytes).
 * It returns 0 on success and -1 on any kind of failure.
 */

int
get_meminfo(long *total, long *fr)

{
	long		freemem;
	static kstat_t * ks = NULL;
	kstat_named_t *kn;

	/* total comes from sysconf */
	*total = pagetok(sysconf(_SC_PHYS_PAGES));

	/* free comes from the kernel's freemem or from kstat */

	/*
	 * prefer kmem for this because kstat unix:0:system_pages can be slow on
	 * systems with lots of memory
	 */
	if (kd)
	{
		(void) getkval(freemem_offset, (int *) (&freemem), sizeof(freemem),
					   "freemem");
	}
	else
	{
#ifdef USE_KSTAT
		/* only need to grab kstat chain once */
		if (ks == NULL)
		{
			ks = kstat_lookup(kc, "unix", 0, "system_pages");
		}

		if (ks != NULL &&
			kstat_read(kc, ks, 0) != -1 &&
			(kn = kstat_data_lookup(ks, "freemem")) != NULL)
		{
			freemem = kstat_data_value_l(kn);
		}
		else
		{
			freemem = -1;
		}
#else
		freemem = -1;
#endif
	}

	*fr = freemem == -1 ? -1 : pagetok(freemem);

	return (0);
}

/*
 * void get_swapinfo(long *total, long *fr)
 *
 * Get information about the system's swap.  Pass back values for
 * total swap available and amount of swap that is free (in kilobytes).
 * It returns 0 on success and -1 on any kind of failure.
 */

int
get_swapinfo(long *total, long *fr)

{
	register int cnt,
				i;
	register long t,
				f;
	struct swaptable *swt;
	struct swapent *ste;
	static char path[256];

	/* preset values to 0 just in case we have to return early */
	*total = 0;
	*fr = 0;

	/* get total number of swap entries */
	if ((cnt = swapctl(SC_GETNSWP, 0)) == -1)
	{
		return (-1);
	}

	/* allocate enough space to hold count + n swapents */
	swt = (struct swaptable *) malloc(sizeof(int) +
									  cnt * sizeof(struct swapent));
	if (swt == NULL)
	{
		return (-1);
	}
	swt->swt_n = cnt;

	/*
	 * fill in ste_path pointers: we don't care about the paths, so we point
	 * them all to the same buffer
	 */
	ste = &(swt->swt_ent[0]);
	i = cnt;
	while (--i >= 0)
	{
		ste++->ste_path = path;
	}

	/* grab all swap info */
	if (swapctl(SC_LIST, swt) == -1)
	{
		return (-1);
	}

	/* walk thru the structs and sum up the fields */
	t = f = 0;
	ste = &(swt->swt_ent[0]);
	i = cnt;
	while (--i >= 0)
	{
		/* dont count slots being deleted */
		if (!(ste->ste_flags & ST_INDEL) &&
			!(ste->ste_flags & ST_DOINGDEL))
		{
			t += ste->ste_pages;
			f += ste->ste_free;
		}
		ste++;
	}

	/* fill in the results */
	*total = pagetok(t);
	*fr = pagetok(f);
	free(swt);

	/* good to go */
	return (0);
}

void
get_system_info(struct system_info *si)
{
	int			avenrun[3];

	static int64_t cp_time[CPUSTATES];
	static int64_t cp_old[CPUSTATES];
	static int64_t cp_diff[CPUSTATES];
	static int64_t(*cp_stats)[CPUSTATES] = NULL;
	static int	cpus;
	register int j,
				i;

	/* get important information */
	get_avenrun(avenrun);

	/* get the cpu statistics arrays */
	cp_stats = get_cpustats(&cpus, cp_stats);

	/* zero the cp_time array */
	for (j = 0; j < CPUSTATES; j++)
	{
		cp_time[j] = 0;
	}

	/* sum stats in to a single array */
	for (i = 0; i < cpus; i++)
	{
		for (j = 0; j < CPUSTATES; j++)
		{
			cp_time[j] += cp_stats[i][j];
		}
	}

	/* convert cp_time counts to percentages */
	(void) percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);

	/* get mpid -- process id of last process */
	if (kd)
		(void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid), "mpid");
	else
		si->last_pid = -1;

	/* convert load averages to doubles */
	for (i = 0; i < 3; i++)
		si->load_avg[i] = loaddouble(avenrun[i]);

	/* get physical memory data */
	if (get_meminfo(&(memory_stats[MEMORY_TOTALMEM]),
					&(memory_stats[MEMORY_FREEMEM])) == -1)
	{
		memory_stats[MEMORY_TOTALMEM] = memory_stats[MEMORY_FREEMEM] = -1;
	}

	/* get swap data */
	if (get_swapinfo(&(memory_stats[MEMORY_TOTALSWAP]),
					 &(memory_stats[MEMORY_FREESWAP])) == -1)
	{
		memory_stats[MEMORY_TOTALSWAP] = memory_stats[MEMORY_FREESWAP] = -1;
	}

	/* set arrays and strings */
	si->cpustates = cpu_states;
	si->memory = memory_stats;

#ifdef DEBUG
	dprintf("get_system_info returns\n");
#endif							/* DEBUG */
}

static struct handle handle;
static int	show_fullcmd;

caddr_t
get_process_info(
				 struct system_info *si,
				 struct process_select *sel,
				 int compare_index,
				 const char *values[],
				 int mode)
{
	register int i;
	register int total_procs;
	register int active_procs;
	register struct prpsinfo **prefp;
	register struct prpsinfo *pp;

	/* these are copied out of sel for speed */
	int			show_idle;
	int			show_system;
	int			show_uid;

	PGconn	   *pgconn;
	PGresult   *pgresult = NULL;

	/* allocate enough space for twice our current needs */
	nproc = 20;
	pgconn = connect_to_db(values);
	if (pgconn != NULL)
	{
		pgresult = pg_processes(pgconn);
		nproc = PQntuples(pgresult);
		if (nproc > maxprocs)
		{
			reallocproc(2 * nproc);
		}

		/* read all the proc structures */
		getptable(pbase, pgresult);
		PQclear(pgresult);
		PQfinish(pgconn);
	}
	else
		nproc = 0;


	/* get a pointer to the states summary array */
	si->procstates = process_states;

	/* set up flags which define what we are going to select */
	show_idle = sel->idle;
	show_uid = sel->uid != -1;
	show_fullcmd = sel->fullcmd;

	/* count up process states and get pointers to interesting procs */
	total_procs = 0;
	active_procs = 0;
	(void) memset(process_states, 0, sizeof(process_states));
	prefp = pref;

	for (pp = pbase, i = 0; i < nproc;
		 i++, pp = (struct prpsinfo *) ((char *) pp + PRPSINFOSIZE))
	{
		/*
		 * Place pointers to each valid proc structure in pref[]. Process
		 * slots that are actually in use have a non-zero status field.
		 * Processes with SSYS set are system processes---these get ignored
		 * unless show_sysprocs is set.
		 */
		if (pp->pr_state != 0 &&
			(show_system || ((pp->pr_flag & SSYS) == 0)))
		{
			total_procs++;
			process_states[(int) pp->pr_state]++;
			if ((!ZOMBIE(pp)) &&
				(show_idle || percent_cpu(pp) || (pp->pr_state == SRUN) || (pp->pr_state == SONPROC)) &&
				(!show_uid || pp->pr_uid == (uid_t) sel->uid))
			{
				*prefp++ = pp;
				active_procs++;
			}
		}
	}

	/* if requested, sort the "interesting" processes */
	qsort((char *) pref, active_procs, sizeof(struct prpsinfo *),
		  proc_compares[compare_index]);

	/* remember active and total counts */
	si->p_total = total_procs;
	si->p_active = active_procs;

	/* pass back a handle */
	handle.next_proc = pref;
	handle.remaining = active_procs;
	return ((caddr_t) & handle);
}

char		fmt[MAX_COLS];		/* static area where result is built */

char *
format_next_io(caddr_t handle, char *(*get_userid) ())
{
	fmt[0] = '\0';
	return (fmt);
}

char *
format_next_process(
					caddr_t handle,
					char *(*get_userid) ())
{
	register struct prpsinfo *pp;
	struct handle *hp;
	register long cputime;
	register double pctcpu;
	char		sb[10];

	/* find and remember the next proc structure */
	hp = (struct handle *) handle;
	pp = *(hp->next_proc++);
	hp->remaining--;

	/* get the cpu usage and calculate the cpu percentages */
	cputime = pp->pr_time.tv_sec;
	pctcpu = percent_cpu(pp) / cpucount;

	if (pp->pr_state == SONPROC && cpucount > 1)
		sprintf(sb, "cpu/%-2d", pp->pr_onpro);	/* XXX large #s may overflow
												 * colums */
	else
		*sb = '\0';

	/* format this entry */
#ifdef HAVE_SNPRINTF
	snprintf(fmt, sizeof(fmt),
#else
	sprintf(fmt,
#endif
			Proc_format,
			(int) pp->pr_pid,
			(*get_userid) (pp->pr_uid),
			(u_short) pp->pr_fill < 999 ? (u_short) pp->pr_fill : 999,
			pp->pr_pri,
			pp->pr_nice - NZERO,
			format_k(SIZE_K(pp)),
			format_k(RSS_K(pp)),
			*sb ? sb : state_abbrev[(int) pp->pr_state],
			format_time(cputime),
			format_percent(pctcpu),
			printable(show_fullcmd ? pp->pr_psargs : pp->pr_fname));

	/* return the result */
	return (fmt);
}

/*
 * check_nlist(nlst) - checks the nlist to see if any symbols were not
 *		found.	For every symbol that was not found, a one-line
 *		message is printed to stderr.  The routine returns the
 *		number of symbols NOT found.
 */
int
check_nlist(register struct nlist *nlst)
{
	register int i;

	/* check to see if we got ALL the symbols we requested */
	/* this will write one line to stderr for every symbol not found */

	i = 0;
	while (nlst->n_name != NULL)
	{
		if (nlst->n_type == 0)
		{
			/* this one wasn't found */
			fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
			i = 1;
		}
		nlst++;
	}
	return (i);
}


/* comparison routines for qsort */

/*
 * There are currently four possible comparison routines.  main selects
 * one of these by indexing in to the array proc_compares.
 *
 * Possible keys are defined as macros below.  Currently these keys are
 * defined:  percent cpu, cpu ticks, process state, resident set size,
 * total virtual memory usage.	The process states are ordered as follows
 * (from least to most important):	WAIT, zombie, sleep, stop, start, run.
 * The array declaration below maps a process state index into a number
 * that reflects this ordering.
 */

/* First, the possible comparison keys.  These are defined in such a way
   that they can be merely listed in the source code to define the actual
   desired ordering.
 */

#define ORDERKEY_PCTCPU  if (dresult = percent_cpu (p2) - percent_cpu (p1),\
				 (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0)
#define ORDERKEY_CPTICKS if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0)
#define ORDERKEY_STATE	 if ((result = \
			(long) (sorted_state[(int) p2->pr_state] - \
			sorted_state[(int) p1->pr_state])) == 0)
#define ORDERKEY_PRIO	 if ((result = p2->pr_oldpri - p1->pr_oldpri) == 0)
#define ORDERKEY_RSSIZE  if ((result = p2->pr_rssize - p1->pr_rssize) == 0)
#define ORDERKEY_MEM	 if ((result = (p2->pr_size - p1->pr_size)) == 0)

/* Now the array that maps process state to a weight */

unsigned char sorted_state[] =
{
	0,							/* not used		*/
	3,							/* sleep		*/
	6,							/* run			*/
	2,							/* zombie		*/
	4,							/* stop			*/
	5,							/* start		*/
	7,							/* run on a processor	*/
	1							/* being swapped (WAIT) */
};


/* compare_cpu - the comparison function for sorting by cpu percentage */

int
compare_cpu(
			struct prpsinfo **pp1,
			struct prpsinfo **pp2)
{
	register struct prpsinfo *p1;
	register struct prpsinfo *p2;
	register long result;
	double		dresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;

	ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_RSSIZE
		ORDERKEY_MEM
		;

	return (result);
}

/* compare_size - the comparison function for sorting by total memory usage */

int
compare_size(
			 struct prpsinfo **pp1,
			 struct prpsinfo **pp2)
{
	register struct prpsinfo *p1;
	register struct prpsinfo *p2;
	register long result;
	double		dresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;

	ORDERKEY_MEM
		ORDERKEY_RSSIZE
		ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_STATE
		ORDERKEY_PRIO
		;

	return (result);
}

/* compare_res - the comparison function for sorting by resident set size */

int
compare_res(
			struct prpsinfo **pp1,
			struct prpsinfo **pp2)
{
	register struct prpsinfo *p1;
	register struct prpsinfo *p2;
	register long result;
	double		dresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;

	ORDERKEY_RSSIZE
		ORDERKEY_MEM
		ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_STATE
		ORDERKEY_PRIO
		;

	return (result);
}

/* compare_time - the comparison function for sorting by total cpu time */

int
compare_time(
			 struct prpsinfo **pp1,
			 struct prpsinfo **pp2)
{
	register struct prpsinfo *p1;
	register struct prpsinfo *p2;
	register long result;
	double		dresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;

	ORDERKEY_CPTICKS
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return (result);
}

/*
get process table
 V.4 only has a linked list of processes so we want to follow that
 linked list, get all the process structures, and put them in our own
 table
*/
void
getptable(struct prpsinfo *baseptr, PGresult *pgresult)
{
	struct prpsinfo *currproc;	/* pointer to current proc structure	*/

#ifndef USE_NEW_PROC
	struct prstatus prstatus;	/* for additional information */
#endif
	int			numprocs = 0;
	int			i;
	struct oldproc *op;
	static struct timeval lasttime =
	{0, 0};
	struct timeval thistime;
	double		timediff;
	double		alpha,
				beta;
	struct oldproc *endbase;

	gettimeofday(&thistime, NULL);

	/*
	 * To avoid divides, we keep times in nanoseconds.	This is scaled by 1e7
	 * rather than 1e9 so that when we divide we get percent.
	 */
	if (lasttime.tv_sec)
		timediff = ((double) thistime.tv_sec * 1.0e7 +
					((double) thistime.tv_usec * 10.0)) -
			((double) lasttime.tv_sec * 1.0e7 +
			 ((double) lasttime.tv_usec * 10.0));
	else
		timediff = 1.0e7;

	/*
	 * constants for exponential average.  avg = alpha * new + beta * avg The
	 * goal is 50% decay in 30 sec.  However if the sample period is greater
	 * than 30 sec, there's not a lot we can do.
	 */
	if (timediff < 30.0e7)
	{
		alpha = 0.5 * (timediff / 30.0e7);
		beta = 1.0 - alpha;
	}
	else
	{
		alpha = 0.5;
		beta = 0.5;
	}

	endbase = oldbase + oldprocs;
	currproc = baseptr;

	/* before reading /proc files, turn on root privs */
	/* (we don't care if this fails since it will be caught later) */
#ifndef USE_NEW_PROC
	seteuid(0);
#endif

	for (i = 0; i < nproc; i++)
	{
		int			fd;
		char		buf[30];
		char	   *procpid = PQgetvalue(pgresult, i, 0);

#ifdef USE_NEW_PROC
#ifdef HAVE_SNPRINTF
		snprintf(buf, sizeof(buf), "%s/%s/psinfo", PROCFS, procpid);
#else
		sprintf(buf, "%s/psinfo", direntp->d_name);
#endif

		if ((fd = open(buf, O_RDONLY)) < 0)
			continue;

		if (read(fd, currproc, sizeof(psinfo_t)) != sizeof(psinfo_t))
		{
			(void) close(fd);
			continue;
		}
#else
		if ((fd = open(direntp->d_name, O_RDONLY)) < 0)
			continue;

		if (ioctl(fd, PIOCPSINFO, currproc) < 0)
		{
			(void) close(fd);
			continue;
		}

		if (ioctl(fd, PIOCSTATUS, &prstatus) < 0)
		{
			/* not a show stopper -- just fill in the needed values */
			currproc->pr_fill = 0;
			currproc->pr_onpro = 0;
		}
		else
		{
			/* copy over the values we need from prstatus */
			currproc->pr_fill = (short) prstatus.pr_nlwp;
			currproc->pr_onpro = prstatus.pr_processor;
		}
#endif

		/*
		 * SVr4 doesn't keep track of CPU% in the kernel, so we have to do our
		 * own.  See if we've heard of this process before. If so, compute %
		 * based on CPU since last time. NOTE:	Solaris 2.4 and higher do
		 * maintain CPU% in prpsinfo.
		 */
		op = oldbase + HASH(currproc->pr_pid);
		while (1)
		{
			if (op->oldpid == -1)	/* not there */
				break;
			if (op->oldpid == currproc->pr_pid)
			{					/* found old data */
#ifndef PROC_HAS_PCTCPU
				percent_cpu(currproc) =
					((currproc->pr_time.tv_sec * 1.0e9 +
					  currproc->pr_time.tv_nsec)
					 - op->oldtime) / timediff;
#endif
				weighted_cpu(currproc) =
					op->oldpct * beta + percent_cpu(currproc) * alpha;

				break;
			}
			op++;				/* try next entry in hash table */
			if (op == endbase)	/* table wrapped around */
				op = oldbase;
		}

		/* Otherwise, it's new, so use all of its CPU time */
		if (op->oldpid == -1)
		{
#ifdef PROC_HAS_PCTCPU
			weighted_cpu(currproc) =
				percent_cpu(currproc);
#else
			if (lasttime.tv_sec)
			{
				percent_cpu(currproc) =
					(currproc->pr_time.tv_sec * 1.0e9 +
					 currproc->pr_time.tv_nsec) / timediff;
				weighted_cpu(currproc) =
					percent_cpu(currproc);
			}
			else
			{					/* first screen -- no difference is possible */
				percent_cpu(currproc) = 0.0;
				weighted_cpu(currproc) = 0.0;
			}
#endif
		}

		numprocs++;
		currproc = (struct prpsinfo *) ((char *) currproc + PRPSINFOSIZE);
		(void) close(fd);

		/* Atypical place for growth */
		if (numprocs >= maxprocs)
		{
			reallocproc(2 * numprocs);
			currproc = (struct prpsinfo *)
				((char *) baseptr + PRPSINFOSIZE * numprocs);
		}
	}

#ifndef USE_NEW_PROC
	/* turn off root privs */
	seteuid(getuid());
#endif

	if (nproc != numprocs)
		nproc = numprocs;

	/*
	 * Save current CPU time for next time around For the moment recreate the
	 * hash table each time, as the code is easier that way.
	 */
	oldprocs = 2 * nproc;
	endbase = oldbase + oldprocs;
	for (op = oldbase; op < endbase; op++)
		op->oldpid = -1;
	for (i = 0, currproc = baseptr;
		 i < nproc;
		 i++, currproc = (struct prpsinfo *) ((char *) currproc + PRPSINFOSIZE))
	{
		/* find an empty spot */
		op = oldbase + HASH(currproc->pr_pid);
		while (1)
		{
			if (op->oldpid == -1)
				break;
			op++;
			if (op == endbase)
				op = oldbase;
		}
		op->oldpid = currproc->pr_pid;
		op->oldtime = (currproc->pr_time.tv_sec * 1.0e9 +
					   currproc->pr_time.tv_nsec);
		op->oldpct = weighted_cpu(currproc);
	}
	lasttime = thistime;
}

/*
 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
 *				the process does not exist.
 *				It is EXTREMLY IMPORTANT that this function work correctly.
 *				If pg_top runs setuid root (as in SVR4), then this function
 *				is the only thing that stands in the way of a serious
 *				security problem.  It validates requests for the "kill"
 *				and "renice" commands.
 */
uid_t
proc_owner(pid_t pid)
{
	register struct prpsinfo *p;
	int			i;

	for (i = 0, p = pbase; i < nproc;
		 i++, p = (struct prpsinfo *) ((char *) p + PRPSINFOSIZE))
	{
		if (p->pr_pid == (pid_t) pid)
			return ((int) p->pr_uid);
	}
	return (-1);
}

/* older revisions don't supply a setpriority */
#if (OSREV < 55)
int
setpriority(int dummy, int who, int niceval)
{
	int			scale;
	int			prio;
	pcinfo_t	pcinfo;
	pcparms_t	pcparms;
	tsparms_t  *tsparms;

	strcpy(pcinfo.pc_clname, "TS");
	if (priocntl(0, 0, PC_GETCID, (caddr_t) & pcinfo) == -1)
		return (-1);

	prio = niceval;
	if (prio > PRIO_MAX)
		prio = PRIO_MAX;
	else if (prio < PRIO_MIN)
		prio = PRIO_MIN;

	tsparms = (tsparms_t *) pcparms.pc_clparms;
	scale = ((tsinfo_t *) pcinfo.pc_clinfo)->ts_maxupri;
	tsparms->ts_uprilim = tsparms->ts_upri = -(scale * prio) / 20;
	pcparms.pc_cid = pcinfo.pc_cid;

	if (priocntl(P_PID, who, PC_SETPARMS, (caddr_t) & pcparms) == -1)
		return (-1);

	return (0);
}
#endif


/*
 * When we reach a proc limit, we need to realloc the stuff.
 */
static void
reallocproc(int n)
{
	int			bytes;
	struct oldproc *op,
			   *endbase;

	if (n < maxprocs)
		return;

#ifdef DEBUG
	dprintf("reallocproc(%d): reallocating from %d\n", n, maxprocs);
#endif							/* DEBUG */

	maxprocs = n;

	/* allocate space for proc structure array and array of pointers */
	bytes = maxprocs * PRPSINFOSIZE;
	pbase = (struct prpsinfo *) realloc(pbase, bytes);
	pref = (struct prpsinfo **) realloc(pref,
										maxprocs * sizeof(struct prpsinfo *));
	oldbase = (struct oldproc *) realloc(oldbase,
										 2 * maxprocs * sizeof(struct oldproc));

	/* Just in case ... */
	if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL
		|| oldbase == (struct oldproc *) NULL)
	{
		fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
		quit(1);
	}

	/*
	 * We're growing from 0 to some number, only then we need to init the
	 * oldproc stuff
	 */
	if (!oldprocs)
	{
		oldprocs = 2 * maxprocs;

		endbase = oldbase + oldprocs;
		for (op = oldbase; op < endbase; op++)
			op->oldpid = -1;
	}
}

void
get_io_info(struct io_info *io_info)
{
	/* Not supported yet */
	memset(io_info, 0, sizeof(*io_info));
}
07070100000032000081A400000000000000000000000166622DE8000007A3000000000000000000000000000000000000002600000000pg_top-4.1.1/machine/m_sunos5.man.rstSUNOS 5 NOTES
=============

CPU percentage is calculated as a fraction of total available computing
resources.  Hence on a multiprocessor machine a single threaded process 
can never consume cpu time in excess of 1 divided by the number of processors.
For example, on a 4 processor machine, a single threaded process will 
never show a cpu percentage higher than 25%.  The CPU percentage column
will always total approximately 100, regardless of the number of processors.

The memory summary line displays the following: "phys mem" is the total
amount of physical memory that can be allocated for use by processes
(it does not include memory reserved for the kernel's use), "free mem" is
the amount of unallocated physical memory, "total swap" is the amount
of swap area on disk that is being used, "free swap" is the amount of
swap area on disk that is still available.  Unlike previous versions of
*pg_top*, The swap figures will differ from the summary output of *swap (1M)*
since the latter includes physical memory as well.

The column "THR" indicates the number of execution threads in the process.

In BSD Unix, process priority was represented internally as a signed
offset from a zero value with an unsigned value.  The "zero" value
was usually something like 20, allowing for a range of priorities
from -20 to 20.  As implemented on SunOS 5, older versions of top
continued to interpret process priority in this manner, even though
it was no longer correct.  Starting with top version 3.5, this was
changed to agree with the rest of the system.

The SunOS 5 (Solaris 2) port was originally written by Torsten Kasch,
<torsten@techfak.uni-bielefeld.de>.  Many contributions have been
provided by Casper Dik <Casper.Dik@sun.com>.
Support for multi-cpu, calculation of CPU% and memory stats provided by
Robert Boucher <boucher@sofkin.ca>, Marc Cohen <marc@aai.com>, 
Charles Hedrick <hedrick@geneva.rutgers.edu>, and
William L. Jones <jones@chpc>.
07070100000033000081A400000000000000000000000166622DE800004994000000000000000000000000000000000000001E00000000pg_top-4.1.1/machine/m_svr4.c/*
 * pg_top - a top PostgreSQL users display for Unix
 *
 * SYNOPSIS:  Intel based System V Release 4
 *
 * DESCRIPTION:
 *	System V release 4.0.x for i486
 *	System V release 4	   for Okidata M88100
 *	System V release 4	   for NCR 3000 series OS Rel 1.00 to 2.02
 *	System V release 4	   for NCR 3000 series OS Rel 02.03.00 and above
 *	and probably other svr4 ports
 *
 * LIBS:  -lelf
 *
 * AUTHORS:  Andrew Herbert		<andrew@werple.apana.org.au>
 *			 Robert Boucher		<boucher@sofkin.ca>
 * Ported to System 3000 Release 2.03 by:
 *		 Jeff Janvrin		<jeff.janvrinColumbiaSC.NCR.COM>
 */

#include "pg_top.h"
#include "machine.h"
#include "utils.h"
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <dirent.h>
#include <nlist.h>
#include <string.h>
#if TIME_WITH_SYS_TIME
#include <sys/time.h>
#include <time.h>
#else
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <time.h>
#endif
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/procfs.h>
#include <sys/sysinfo.h>
#include <sys/sysmacros.h>
#include <sys/vmmeter.h>
#include <vm/anon.h>
#include <sys/priocntl.h>
#include <sys/rtpriocntl.h>
#include <sys/tspriocntl.h>
#include <sys/procset.h>
#include <sys/var.h>

#define UNIX "/stand/unix"
#define KMEM "/dev/kmem"
#define PROCFS "/proc"
#define CPUSTATES	5

#ifndef PRIO_MAX
#define PRIO_MAX	20
#endif
#ifndef PRIO_MIN
#define PRIO_MIN	-20
#endif

#ifndef FSCALE
#define FSHIFT	8				/* bits to right of fixed binary point */
#define FSCALE	(1<<FSHIFT)
#endif

#define loaddouble(x) ((double)(x) / FSCALE)
#define percent_cpu(x) ((double)(x)->pr_cpu / FSCALE)
#define weighted_cpu(pct, pp) ( ((pp)->pr_time.tv_sec) == 0 ? 0.0 : \
		((pp)->pr_cpu) / ((pp)->pr_time.tv_sec) )
#define pagetok(size) ctob(size) >> LOG1024

/* definitions for the index in the nlist array */
#define X_AVENRUN	0
#define X_MPID		1
#define X_V		2
#define X_NPROC		3
#define X_ANONINFO	4
#define X_TOTAL		5
#define X_SYSINFO	6

static struct nlist nlst[] =
{
	{"avenrun"},				/* 0 */
	{"mpid"},					/* 1 */
	{"v"},						/* 2 */
	{"nproc"},					/* 3 */
	{"anoninfo"},				/* 4 */
	{"total"},					/* 5 */
	{"sysinfo"},				/* 6 */
	{NULL}
};

static unsigned long avenrun_offset;
static unsigned long mpid_offset;
static unsigned long nproc_offset;
static unsigned long anoninfo_offset;
static unsigned long total_offset;
static unsigned long sysinfo_offset;

/* get_process_info passes back a handle.  This is what it looks like: */

struct handle
{
	struct prpsinfo **next_proc;	/* points to next valid proc pointer */
	int			remaining;		/* number of pointers remaining */
};

/*
 *	These definitions control the format of the per-process area
 */

static char header[] =
"  PID X        PRI NICE  SIZE   RES STATE   TIME   WCPU    CPU COMMAND";

/* 0123456	 -- field to fill in starts at header+6 */
#define UNAME_START 6
#define Proc_format \
	"%5d %-8.8s %3d %4d %5s %5s %-5s %6s %3d.0%% %5.2f%% %.16s"

char	   *state_abbrev[] =
{"", "sleep", "run", "zombie", "stop", "start", "cpu", "swap"};

int			process_states[8];
char	   *procstatenames[] =
{
	"", " sleeping, ", " running, ", " zombie, ", " stopped, ",
	" starting, ", " on cpu, ", " swapped, ",
	NULL
};

int			cpu_states[CPUSTATES];
char	   *cpustatenames[] =
{"idle", "user", "kernel", "wait", "swap", NULL};

/* these are for detailing the memory statistics */

long		memory_stats[5];
char	   *memorynames[] =
{"K real, ", "K active, ", "K free, ", "K swap, ", "K free swap", NULL};

/* forward reference for qsort comparison function */
int			proc_compare();

static int	kmem = -1;
static int	nproc;
static int	bytes;
static int	use_stats = 0;
static struct prpsinfo *pbase;
static struct prpsinfo **pref;
static DIR *proc_dir;

/* useful externals */
extern int	errno;
extern char *sys_errlist[];
extern char *myname;
extern int	check_nlist();
extern int	getkval();
extern void perror();
extern void getptable();
extern void quit();
extern int	nlist();

int
machine_init(struct statics *statics)
{
	static struct var v;

	/* fill in the statics information */
	statics->procstate_names = procstatenames;
	statics->cpustate_names = cpustatenames;
	statics->memory_names = memorynames;

	/* get the list of symbols we want to access in the kernel */
	if (nlist(UNIX, nlst))
	{
		(void) fprintf(stderr, "Unable to nlist %s\n", UNIX);
		return (-1);
	}

	/* make sure they were all found */
	if (check_nlist(nlst) > 0)
		return (-1);

	/* open kernel memory */
	if ((kmem = open(KMEM, O_RDONLY)) == -1)
	{
		perror(KMEM);
		return (-1);
	}

	/* get the symbol values out of kmem */
	/* NPROC Tuning parameter for max number of processes */
	(void) getkval(nlst[X_V].n_value, &v, sizeof(struct var), nlst[X_V].n_name);
	nproc = v.v_proc;

	/* stash away certain offsets for later use */
	mpid_offset = nlst[X_MPID].n_value;
	nproc_offset = nlst[X_NPROC].n_value;
	avenrun_offset = nlst[X_AVENRUN].n_value;
	anoninfo_offset = nlst[X_ANONINFO].n_value;
	total_offset = nlst[X_TOTAL].n_value;
/* JJ this may need to be changed */
	sysinfo_offset = nlst[X_SYSINFO].n_value;

	/* allocate space for proc structure array and array of pointers */
	bytes = nproc * sizeof(struct prpsinfo);
	pbase = (struct prpsinfo *) malloc(bytes);
	pref = (struct prpsinfo **) malloc(nproc * sizeof(struct prpsinfo *));

	/* Just in case ... */
	if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL)
	{
		(void) fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
		return (-1);
	}

	if (!(proc_dir = opendir(PROCFS)))
	{
		(void) fprintf(stderr, "Unable to open %s\n", PROCFS);
		return (-1);
	}

	if (chdir(PROCFS))
	{							/* handy for later on when we're reading it */
		(void) fprintf(stderr, "Unable to chdir to %s\n", PROCFS);
		return (-1);
	}

	/* all done! */
	return (0);
}

char *
format_header(char *uname_field)
{
	register char *ptr;

	ptr = header + UNAME_START;
	while (*uname_field != '\0')
		*ptr++ = *uname_field++;

	return (header);
}

void
get_system_info(struct system_info *si)
{
	long		avenrun[3];
	struct sysinfo sysinfo;
	static struct sysinfo *mpinfo = NULL;	/* array, per-processor sysinfo
											 * structures. */
	struct vmtotal total;
	struct anoninfo anoninfo;
	static long cp_old[CPUSTATES];
	static long cp_diff[CPUSTATES]; /* for cpu state percentages */
	static int	num_cpus;
	static int	fd_cpu = 0;
	register int i;

	if (use_stats == 1)
	{
		if (fd_cpu == 0)
		{
			if ((fd_cpu = open("/stats/cpuinfo", O_RDONLY)) == -1)
			{
				(void) fprintf(stderr, "%s: Open of /stats/cpuinfo failed\n", myname);
				quit(2);
			}
			if (read(fd_cpu, &num_cpus, sizeof(int)) != sizeof(int))
			{
				(void) fprintf(stderr, "%s: Read of /stats/cpuinfo failed\n", myname);
				quit(2);
			}
			close(fd_cpu);
		}
		if (mpinfo == NULL)
		{
			mpinfo = (struct sysinfo *) calloc(num_cpus, sizeof(mpinfo[0]));
			if (mpinfo == NULL)
			{
				(void) fprintf(stderr, "%s: can't allocate space for per-processor sysinfos\n", myname);
				quit(12);
			}
		}
		/* Read the per cpu sysinfo structures into mpinfo struct. */
		read_sysinfos(num_cpus, mpinfo);
		/* Add up all of the percpu sysinfos to get global sysinfo */
		sysinfo_data(num_cpus, &sysinfo, mpinfo);
	}
	else
	{
		(void) getkval(sysinfo_offset, &sysinfo, sizeof(struct sysinfo), "sysinfo");
	}

	/* convert cp_time counts to percentages */
	(void) percentages(CPUSTATES, cpu_states, sysinfo.cpu, cp_old, cp_diff);

	/* get mpid -- process id of last process */
	(void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid),
				   "mpid");

	/* get load average array */
	(void) getkval(avenrun_offset, (int *) avenrun, sizeof(avenrun), "avenrun");

	/* convert load averages to doubles */
	for (i = 0; i < 3; i++)
		si->load_avg[i] = loaddouble(avenrun[i]);

	/* get total -- systemwide main memory usage structure */
	(void) getkval(total_offset, (int *) (&total), sizeof(total), "total");
	/* convert memory stats to Kbytes */
	memory_stats[0] = pagetok(total.t_rm);
	memory_stats[1] = pagetok(total.t_arm);
	memory_stats[2] = pagetok(total.t_free);
	(void) getkval(anoninfo_offset, (int *) (&anoninfo), sizeof(anoninfo),
				   "anoninfo");
	memory_stats[3] = pagetok(anoninfo.ani_max - anoninfo.ani_free);
	memory_stats[4] = pagetok(anoninfo.ani_max - anoninfo.ani_resv);

	/* set arrays and strings */
	si->cpustates = cpu_states;
	si->memory = memory_stats;
}

static struct handle handle;

caddr_t
get_process_info(
				 struct system_info *si,
				 struct process_select *sel,
				 int x)
{
	register int i;
	register int total_procs;
	register int active_procs;
	register struct prpsinfo **prefp;
	register struct prpsinfo *pp;

	/* these are copied out of sel for speed */
	int			show_idle;
	int			show_system;
	int			show_uid;

	/* Get current number of processes */
	(void) getkval(nproc_offset, (int *) (&nproc), sizeof(nproc), "nproc");

	/* read all the proc structures */
	getptable(pbase);

	/* get a pointer to the states summary array */
	si->procstates = process_states;

	/* set up flags which define what we are going to select */
	show_idle = sel->idle;
	show_system = sel->system;
	show_uid = sel->uid != -1;

	/* count up process states and get pointers to interesting procs */
	total_procs = 0;
	active_procs = 0;
	(void) memset(process_states, 0, sizeof(process_states));
	prefp = pref;

	for (pp = pbase, i = 0; i < nproc; pp++, i++)
	{
		/*
		 * Place pointers to each valid proc structure in pref[]. Process
		 * slots that are actually in use have a non-zero status field.
		 * Processes with SSYS set are system processes---these get ignored
		 * unless show_sysprocs is set.
		 */
		if (pp->pr_state != 0 &&
			(show_system || ((pp->pr_flag & SSYS) == 0)))
		{
			total_procs++;
			process_states[pp->pr_state]++;
			if ((!pp->pr_zomb) &&
				(show_idle || (pp->pr_state == SRUN) || (pp->pr_state == SONPROC)) &&
				(!show_uid || pp->pr_uid == (uid_t) sel->uid))
			{
				*prefp++ = pp;
				active_procs++;
			}
		}
	}

	/* if requested, sort the "interesting" processes */
	qsort((char *) pref, active_procs, sizeof(struct prpsinfo *), proc_compare);

	/* remember active and total counts */
	si->p_total = total_procs;
	si->p_active = active_procs;

	/* pass back a handle */
	handle.next_proc = pref;
	handle.remaining = active_procs;
	return ((caddr_t) & handle);
}

char		fmt[MAX_COLS];		/* static area where result is built */

char *
format_next_process(
					caddr_t handle,
					char *(*get_userid) ())
{
	register struct prpsinfo *pp;
	struct handle *hp;
	register long cputime;
	register double pctcpu;

	/* find and remember the next proc structure */
	hp = (struct handle *) handle;
	pp = *(hp->next_proc++);
	hp->remaining--;

	/* get the cpu usage and calculate the cpu percentages */
	cputime = pp->pr_time.tv_sec;
	pctcpu = percent_cpu(pp);

	/* format this entry */
	(void) sprintf(fmt,
				   Proc_format,
				   pp->pr_pid,
				   (*get_userid) (pp->pr_uid),
				   pp->pr_pri - PZERO,
				   pp->pr_nice - NZERO,
				   format_k(pagetok(pp->pr_size)),
				   format_k(pagetok(pp->pr_rssize)),
				   state_abbrev[pp->pr_state],
				   format_time(cputime),
				   (pp->pr_cpu & 0377),
				   100.0 * pctcpu,
				   printable(pp->pr_fname));

	/* return the result */
	return (fmt);
}

/*
 * check_nlist(nlst) - checks the nlist to see if any symbols were not
 *		found.	For every symbol that was not found, a one-line
 *		message is printed to stderr.  The routine returns the
 *		number of symbols NOT found.
 */
int
check_nlist(register struct nlist *nlst)
{
	register int i;
	struct stat stat_buf;

	/* check to see if we got ALL the symbols we requested */
	/* this will write one line to stderr for every symbol not found */

	i = 0;
	while (nlst->n_name != NULL)
	{
		if (nlst->n_type == 0)
		{
			if (strcmp("sysinfo", nlst->n_name) == 0)
			{
				/* check to see if /stats file system exists. If so,	*/
				/* ignore error.					*/
				if (!((stat("/stats/sysinfo", &stat_buf) == 0) &&
					  (stat_buf.st_mode & S_IFREG)))
				{
					(void) fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
					i = 1;
				}
				else
				{
					use_stats = 1;
				}
			}
			else
			{

				/* this one wasn't found */
				(void) fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
				i = 1;
			}
		}
		nlst++;
	}
	return (i);
}


/*
 *	getkval(offset, ptr, size, refstr) - get a value out of the kernel.
 *	"offset" is the byte offset into the kernel for the desired value,
 *		"ptr" points to a buffer into which the value is retrieved,
 *		"size" is the size of the buffer (and the object to retrieve),
 *		"refstr" is a reference string used when printing error meessages,
 *		if "refstr" starts with a '!', then a failure on read will not
 *			be fatal (this may seem like a silly way to do things, but I
 *			really didn't want the overhead of another argument).
 *
 */
int
getkval(
		unsigned long offset,
		int *ptr,
		int size,
		char *refstr)
{
#ifdef MIPS
	if (lseek(kmem, (long) (offset & 0x7fffffff), 0) == -1)
#else
	if (lseek(kmem, (long) offset, 0) == -1)
#endif
	{
		if (*refstr == '!')
			refstr++;
		(void) fprintf(stderr, "%s: lseek to %s: %s\n",
					   myname, refstr, sys_errlist[errno]);
		quit(22);
	}
	if (read(kmem, (char *) ptr, size) == -1)
		if (*refstr == '!')
			/* we lost the race with the kernel, process isn't in memory */
			return (0);
		else
		{
			(void) fprintf(stderr, "%s: reading %s: %s\n",
						   myname, refstr, sys_errlist[errno]);
			quit(23);
		}
	return (1);
}

/* comparison routine for qsort */

/*
 *	proc_compare - comparison function for "qsort"
 *	Compares the resource consumption of two processes using five
 *		distinct keys.	The keys (in descending order of importance) are:
 *		percent cpu, cpu ticks, state, resident set size, total virtual
 *		memory usage.  The process states are ordered as follows (from least
 *		to most important):  WAIT, zombie, sleep, stop, start, run.  The
 *		array declaration below maps a process state index into a number
 *		that reflects this ordering.
 */


unsigned char sorted_state[] =
{
	0,							/* not used		*/
	3,							/* sleep		*/
	6,							/* run			*/
	2,							/* zombie		*/
	4,							/* stop			*/
	5,							/* start		*/
	7,							/* run on a processor	*/
	1							/* being swapped (WAIT) */
};

int
proc_compare(
			 struct prpsinfo **pp1,
			 struct prpsinfo **pp2)
{
	register struct prpsinfo *p1;
	register struct prpsinfo *p2;
	register long result;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;

	/* compare percent cpu (pctcpu) */
	if ((result = (long) (p2->pr_cpu - p1->pr_cpu)) == 0)
	{
		/* use cpticks to break the tie */
		if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0)
		{
			/* use process state to break the tie */
			if ((result = (long) (sorted_state[p2->pr_state] -
								  sorted_state[p1->pr_state])) == 0)
			{
				/* use priority to break the tie */
				if ((result = p2->pr_oldpri - p1->pr_oldpri) == 0)
				{
					/* use resident set size (rssize) to break the tie */
					if ((result = p2->pr_rssize - p1->pr_rssize) == 0)
					{
						/* use total memory to break the tie */
						result = (p2->pr_size - p1->pr_size);
					}
				}
			}
		}
	}
	return (result);
}

/*
get process table
*/
void
getptable(struct prpsinfo *baseptr)
{
	struct prpsinfo *currproc;	/* pointer to current proc structure	*/
	int			numprocs = 0;
	struct dirent *direntp;

	for (rewinddir(proc_dir); direntp = readdir(proc_dir);)
	{
		int			fd;

		if ((fd = open(direntp->d_name, O_RDONLY)) < 0)
			continue;

		currproc = &baseptr[numprocs];
		if (ioctl(fd, PIOCPSINFO, currproc) < 0)
		{
			(void) close(fd);
			continue;
		}

		numprocs++;
		(void) close(fd);
	}

	if (nproc != numprocs)
		nproc = numprocs;
}

/* return the owner of the specified process, for use in commands.c as we're
   running setuid root */
int
proc_owner(int pid)
{
	register struct prpsinfo *p;
	int			i;

	for (i = 0, p = pbase; i < nproc; i++, p++)
		if (p->pr_pid == (pid_t) pid)
			return (p->pr_uid);

	return (-1);
}

#ifndef HAVE_SETPRIORITY
int
setpriority(int dummy, int who, int niceval)
{
	int			scale;
	int			prio;
	pcinfo_t	pcinfo;
	pcparms_t	pcparms;
	tsparms_t  *tsparms;

	strcpy(pcinfo.pc_clname, "TS");
	if (priocntl(0, 0, PC_GETCID, (caddr_t) & pcinfo) == -1)
		return (-1);

	prio = niceval;
	if (prio > PRIO_MAX)
		prio = PRIO_MAX;
	else if (prio < PRIO_MIN)
		prio = PRIO_MIN;

	tsparms = (tsparms_t *) pcparms.pc_clparms;
	scale = ((tsinfo_t *) pcinfo.pc_clinfo)->ts_maxupri;
	tsparms->ts_uprilim = tsparms->ts_upri = -(scale * prio) / 20;
	pcparms.pc_cid = pcinfo.pc_cid;

	if (priocntl(P_PID, who, PC_SETPARMS, (caddr_t) & pcparms) == -1)
		return (-1);

	return (0);
}
#endif

/****************************************************************
 * read_sysinfos() -						*
 *	Read all of the CPU specific sysinfo sturctures in from *
 *	the /stats file system.					*
 ****************************************************************/
read_sysinfos(num_cpus, buf)
int			num_cpus;
struct sysinfo *buf;

{

	static int	fd1 = 0;		/* file descriptor for /stats/sysinfo */
	int			read_sz;

	/* Open /stats/sysinfo one time only and leave it open */
	if (fd1 == 0)
	{
		if ((fd1 = open("/stats/sysinfo", O_RDONLY)) == -1)
			(void) fprintf(stderr, "%s: Open of /stats/sysinfo failed\n", myname);
	}
	/* reset the read pointer to the beginning of the file */
	if (lseek(fd1, 0L, SEEK_SET) == -1)
		(void) fprintf(stderr, "%s: lseek to beginning of /stats/sysinfo failed\n", myname);
	read_sz = num_cpus * sizeof(buf[0]);
	if (read(fd1, buf, read_sz) != read_sz)
		(void) fprintf(stderr, "%s: Read of /stats/sysinfo failed\n", myname);
}

/****************************************************************
 * sysinfo_data() -						*
 *	Add up all of the CPU specific sysinfo sturctures to	*
 *	make the GLOBAL sysinfo.				*
 ****************************************************************/
sysinfo_data(num_cpus, global_si, percpu_si)
int			num_cpus;
struct sysinfo *global_si;
struct sysinfo *percpu_si;

{
	struct sysinfo *percpu_p;
	int			cpu,
				i,
			   *global,
			   *src;

	/* null out the global statistics from last sample */
	memset(global_si, 0, sizeof(struct sysinfo));

	percpu_p = (struct sysinfo *) percpu_si;
	for (cpu = 0; cpu < num_cpus; cpu++)
	{
		global = (int *) global_si;

		src = (int *) percpu_p;

		/* assume sysinfo ends on an int boundary */

		/*
		 * Currently, all of the struct sysinfo members are the same size as
		 * an int. If that changes, we may not be able to do this. But this
		 * should be safe.
		 */
		for (i = 0; i < sizeof(struct sysinfo) / sizeof(int); i++)
		{
			*global +++= *src++;
		}
		percpu_p++;
	}
}

void
get_io_info(struct io_info *io_info)
{
	/* Not supported yet */
	memset(io_info, 0, sizeof(*io_info));
}
07070100000034000081A400000000000000000000000166622DE800000150000000000000000000000000000000000000002400000000pg_top-4.1.1/machine/m_svr4.man.rstSVR4 CREDITS
============

The SVR4 port was initially written by Andrew Herbert.  He was guided by a SVR4
port of top version 2.1 which was done by Andy Crump (andyc@bucky.intel.com).
Robert Boucher (boucher@sofkin.ca) adapted it to top version 3.1.
Ported to System 3000 Release 2.03 by 
Jeff Janvrin (jeff.janvrinColumbiaSC.NCR.COM)
07070100000035000081A400000000000000000000000166622DE800007E92000000000000000000000000000000000000001E00000000pg_top-4.1.1/machine/m_svr5.c/*
 * pg_top - a top PostgreSQL users display for Unix
 *
 * SYNOPSIS:  For Intel based System V Release 5 (Unixware7)
 *
 * DESCRIPTION:
 * System V release 5 for i[3456]86
 * Works for:
 * i586-sco-sysv5uw7  i386 SCO UNIX_SVR5 (UnixWare 7)
 *
 * LIBS:  -lelf -lmas
 *
 * CFLAGS: -DHAVE_GETOPT -DORDER
 *
 * AUTHORS: Mike Hopkirk	   <hops@sco.com>
 *			David Cutter	   <dpc@grail.com>
 *			Andrew Herbert	   <andrew@werple.apana.org.au>
 *			Robert Boucher	   <boucher@sofkin.ca>
 */

/* build config
 *	SHOW_NICE - process nice fields don't seem to be being updated so changed
 *	   default to display # of threads in use instead.
 *	   define this to display nice fields (values always 0)
 * #define SHOW_NICE 1
 */

#define _KMEMUSER
#define prpsinfo psinfo
#include <sys/procfs.h>

#define pr_state pr_lwp.pr_state
#define pr_nice pr_lwp.pr_nice
#define pr_pri pr_lwp.pr_pri
#define pr_onpro pr_lwp.pr_onpro
#define ZOMBIE(p)	((p)->pr_nlwp == 0)
#define SIZE_K(p)	pagetok((p)->pr_size)
#define RSS_K(p)	pagetok((p)->pr_rssize)


#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <dirent.h>
#include <nlist.h>
#include <string.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/sysmacros.h>
#include <vm/anon.h>
#include <sys/priocntl.h>
#include <sys/tspriocntl.h>
#include <sys/var.h>

#include "pg_top.h"
#include "machine.h"
#include "utils.h"

#define UNIX "/stand/unix"
#define KMEM "/dev/kmem"
#define PROCFS "/proc"
#define CPUSTATES	5

#ifndef PRIO_MAX
#define PRIO_MAX	20
#endif
#ifndef PRIO_MIN
#define PRIO_MIN	-20
#endif

#ifndef FSCALE
#define FSHIFT	8				/* bits to right of fixed binary point */
#define FSCALE	(1<<FSHIFT)
#endif

#define loaddouble(x) ((double)x/FSCALE)
#define pagetok(size) ((size) * pagesz) >> LOG1024

/* definitions for the index in the nlist array */
#define X_AVENRUN	0
#define X_V		1
#define X_MPID		2

static struct nlist nlst[] =
{
	{"avenrun"},				/* 0 */
	{"v"},						/* 1 */
	{"nextpid"},				/* 2 */
	{NULL}
};

static unsigned long avenrun_offset;
static unsigned long mpid_offset;

static unsigned int pagesz;

static void reallocproc(int n);
static int	maxprocs;

/* get_process_info passes back a handle.  This is what it looks like: */

struct handle
{
	struct prpsinfo **next_proc;	/* points to next valid proc pointer */
	int			remaining;		/* number of pointers remaining */
};

/*
 *	These definitions control the format of the per-process area
 */

static char header[] =
#ifdef SHOW_NICE
"  PID X        PRI NICE  SIZE   RES STATE   TIME      CPU  COMMAND";
#else
"  PID X        PRI  THR  SIZE   RES STATE   TIME      CPU  COMMAND";
#endif
/* 0123456	 -- field to fill in starts at header+6 */
#define UNAME_START 6
#define Proc_format \
	"%5d %-8.8s %3d %4d %5s %5s %-5s %6s %8.4f%% %.16s"

char	   *state_abbrev[] =
{"oncpu", "run", "sleep", "stop", "idle", "zombie"};

#define sZOMB 5
int			process_states[8];
char	   *procstatenames[] =
{
	" on cpu, ", " running, ", " sleeping, ", " stopped, ",
	" idling ", " zombie, ",
	NULL
};

int			cpu_states[CPUSTATES];
char	   *cpustatenames[] =
{"idle", "user", "kernel", "wait", NULL};


/* these are for detailing the memory statistics */
long		memory_stats[5];
char	   *memorynames[] =
{"K phys, ", "K used, ", "K free, ", "K swapUsed, ", "K swapFree", NULL};

/* these are names given to allowed sorting orders -- first is default */
char	   *ordernames[] = {
	"state", "cpu", "size", "res", "time", "pid", "uid", "rpid", "ruid", NULL
};

/* forward definitions for comparison functions */
int			proc_compare();
int			compare_cpu();
int			compare_size();
int			compare_res();
int			compare_time();
int			compare_pid();
int			compare_uid();
int			compare_rpid();
int			compare_ruid();

int			(*proc_compares[]) () =
{
	proc_compare,
		compare_cpu,
		compare_size,
		compare_res,
		compare_time,
		compare_pid,
		compare_uid,
		compare_rpid,
		compare_ruid,
		NULL
};


static int	kmem = -1;
static int	nproc;
static int	bytes;
static struct prpsinfo *pbase;
static struct prpsinfo **pref;
static DIR *procdir;

/* useful externals */
extern int	errno;
extern char *sys_errlist[];
extern char *myname;
extern long percentages();
extern int	check_nlist();
extern int	getkval();
extern void perror();
extern void getptable();
extern void quit();
extern int	nlist();

/* fwd dcls */
static int	kmet_init(void);
static int	get_cpustates(int *new);


int
machine_init(struct statics *statics)
{
	static struct var v;
	int			i;

	/* fill in the statics information */
	statics->procstate_names = procstatenames;
	statics->cpustate_names = cpustatenames;
	statics->memory_names = memorynames;
	statics->order_names = ordernames;

	/* get the list of symbols we want to access in the kernel */
	if (nlist(UNIX, nlst))
	{
		(void) fprintf(stderr, "Unable to nlist %s\n", UNIX);
		return (-1);
	}

	/* make sure they were all found */
	if (check_nlist(nlst) > 0)
		return (-1);

	/* open kernel memory */
	if ((kmem = open(KMEM, O_RDONLY)) == -1)
	{
		perror(KMEM);
		return (-1);
	}

	v.v_proc = 200;				/* arbitrary default */
	/* get the symbol values out of kmem */
	/* NPROC Tuning parameter for max number of processes */
	(void) getkval(nlst[X_V].n_value, &v, sizeof(struct var), nlst[X_V].n_name);
	nproc = v.v_proc;
	maxprocs = nproc;

	/* stash away certain offsets for later use */
	mpid_offset = nlst[X_MPID].n_value;
	avenrun_offset = nlst[X_AVENRUN].n_value;

	/* allocate space for proc structure array and array of pointers */
	bytes = nproc * sizeof(struct prpsinfo);
	pbase = (struct prpsinfo *) malloc(bytes);
	pref = (struct prpsinfo **) malloc(nproc * sizeof(struct prpsinfo *));

	pagesz = sysconf(_SC_PAGESIZE);


	/* Just in case ... */
	if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL)
	{
		(void) fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
		return (-1);
	}

	if (!(procdir = opendir(PROCFS)))
	{
		(void) fprintf(stderr, "Unable to open %s\n", PROCFS);
		return (-1);
	}

	if (chdir(PROCFS))
	{							/* handy for later on when we're reading it */
		(void) fprintf(stderr, "Unable to chdir to %s\n", PROCFS);
		return (-1);
	}


	kmet_init();

	/* all done! */
	return (0);
}

char *
format_header(char *uname_field)
{
	register char *ptr;

	ptr = header + UNAME_START;
	while (*uname_field != '\0')
		*ptr++ = *uname_field++;

	return (header);
}

void
get_system_info(struct system_info *si)
{
	long		avenrun[3];
	long		mem;
	static time_t cp_old[CPUSTATES];
	static time_t cp_diff[CPUSTATES];	/* for cpu state percentages */
	register int i;
	static long swap_total;
	static long swap_free;
	int			new_states[CPUSTATES];

	get_cpustates(new_states);

	/* convert cp_time counts to percentages */
	(void) percentages(CPUSTATES, cpu_states, new_states, cp_old, cp_diff);


	si->last_pid = -1;

	/*
	 * get mpid -- process id of last process svr5 is nextpid - next pid to be
	 * assigned (already incremented)
	 */
	(void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid),
				   "nextpid");
	(si->last_pid)--;			/* so we shld decrement for display */


	/* get load average array */
	(void) getkval(avenrun_offset, (int *) avenrun, sizeof(avenrun), "avenrun");
	/* convert load averages to doubles */
	for (i = 0; i < 3; i++)
		si->load_avg[i] = loaddouble(avenrun[i]);

	mem = sysconf(_SC_TOTAL_MEMORY);	/* physical mem */
	memory_stats[0] = pagetok(mem);

	mem = kmet_get_freemem();	/* free mem */
	memory_stats[2] = pagetok(mem);

	/* mem = sysconf(_SC_GENERAL_MEMORY);	 */
	memory_stats[1] = memory_stats[0] - memory_stats[2];	/* active */

	get_swapinfo(&swap_total, &swap_free);
	memory_stats[3] = pagetok(swap_total - swap_free);
	memory_stats[4] = pagetok(swap_free);


	/* set arrays and strings */
	si->cpustates = cpu_states;
	si->memory = memory_stats;
}

static struct handle handle;

caddr_t
get_process_info(
				 struct system_info *si,
				 struct process_select *sel,
				 int idx)
{
	register int i;
	register int total_procs;
	register int active_procs;
	register struct prpsinfo **prefp;
	register struct prpsinfo *pp;

	/* these are copied out of sel for speed */
	int			show_idle;
	int			show_system;
	int			show_uid;

	/* Get current number of processes */

	/* read all the proc structures */
	getptable(pbase);

	/* get a pointer to the states summary array */
	si->procstates = process_states;

	/* set up flags which define what we are going to select */
	show_idle = sel->idle;
	show_system = sel->system;
	show_uid = sel->uid != -1;

	nproc = kmet_get_nproc();

	/* count up process states and get pointers to interesting procs */
	total_procs = 0;
	active_procs = 0;
	(void) memset(process_states, 0, sizeof(process_states));
	prefp = pref;

	for (pp = pbase, i = 0; i < nproc; pp++, i++)
	{
		/*
		 * Place pointers to each valid proc structure in pref[]. Process
		 * slots that are actually in use have a non-zero status field.
		 * Processes with PR_ISSYS set are system processes---these get
		 * ignored unless show_sysprocs is set.
		 */
		if ((pp->pr_state >= SONPROC && pp->pr_state <= SIDL) &&
			(show_system || ((pp->pr_flag & PR_ISSYS) == 0)))
		{
			total_procs++;
			process_states[pp->pr_state]++;
			if ((!ZOMBIE(pp)) &&
				(show_idle || (pp->pr_state == SRUN) || (pp->pr_state == SONPROC)) &&
				(!show_uid || pp->pr_uid == (uid_t) sel->uid))
			{
				*prefp++ = pp;
				active_procs++;
			}
			if (ZOMBIE(pp))
				process_states[sZOMB]++;	/* invented */

		}
	}

	/* if requested, sort the "interesting" processes */
	qsort((char *) pref, active_procs, sizeof(struct prpsinfo *),
		  proc_compares[idx]);

	/* remember active and total counts */
	si->p_total = total_procs;
	si->P_ACTIVE = active_procs;

	/* pass back a handle */
	handle.next_proc = pref;
	handle.remaining = active_procs;
	return ((caddr_t) & handle);
}

/*
 * cpu percentage calculation is as fm ps.c
 * seems to be ratio of (sys+user time used)/(elapsed time)
 * i.e percent of cpu utilised when on cpu
 */
static double
percent_cpu(struct prpsinfo *pp)
{
	static time_t tim = 0L;
	time_t		starttime;
	time_t		ctime;
	time_t		etime;

	/* if (tim == 0L) */
	tim = time((time_t *) 0);
	starttime = pp->pr_start.tv_sec;
	if (pp->pr_start.tv_nsec > 500000000)
		starttime++;
	etime = (tim - starttime);
	ctime = pp->pr_time.tv_sec;
	if (pp->pr_time.tv_nsec > 500000000)
		ctime++;
	if (etime)
	{
		/* return  (float)(ctime * 100) / (unsigned)etime; */

		/*
		 * this was ocasionally giving vals >100 for some unknown reason so
		 * the below normalises it
		 */

		double		pct;

		pct = (float) (ctime * 100) / (unsigned) etime;
		return (pct < 100.0) ? pct : 100.00;
	}
	return 0.00;
}


char		fmt[MAX_COLS];		/* static area where result is built */

char *
format_next_process(
					caddr_t handle,
					char *(*get_userid) ())
{
	register struct prpsinfo *pp;
	struct handle *hp;
	register long cputime;
	register double pctcpu;

	/* find and remember the next proc structure */
	hp = (struct handle *) handle;
	pp = *(hp->next_proc++);
	hp->remaining--;

	/* get the cpu usage and calculate the cpu percentages */
	cputime = pp->pr_time.tv_sec;
	pctcpu = percent_cpu(pp);


	/* format this entry */
	(void) sprintf(fmt,
				   Proc_format,
				   pp->pr_pid,
				   (*get_userid) (pp->pr_uid),
				   pp->pr_pri,
#ifdef SHOW_NICE
				   pp->pr_nice,
#else
				   (u_short) pp->pr_nlwp < 999 ? (u_short) pp->pr_nlwp : 999,
#endif
				   format_k(SIZE_K(pp)),
				   format_k(RSS_K(pp)),
				   (ZOMBIE(pp)) ? state_abbrev[sZOMB]
				   : state_abbrev[pp->pr_state],
				   format_time(cputime),
				    /* 100.0 * */ pctcpu,
				   printable(pp->pr_fname));

	/* return the result */
	return (fmt);
}

/*
 * check_nlist(nlst) - checks the nlist to see if any symbols were not
 *		found.	For every symbol that was not found, a one-line
 *		message is printed to stderr.  The routine returns the
 *		number of symbols NOT found.
 */
int
check_nlist(register struct nlist *nlst)
{
	register int i;

	/* check to see if we got ALL the symbols we requested */
	/* this will write one line to stderr for every symbol not found */

	i = 0;
	while (nlst->n_name != NULL)
	{
		if (nlst->n_value == 0)
		{
			/* this one wasn't found */
			(void) fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
			i = 1;
		}
		nlst++;
	}
	return (i);
}


/*
 *	getkval(offset, ptr, size, refstr) - get a value out of the kernel.
 *	"offset" is the byte offset into the kernel for the desired value,
 *		"ptr" points to a buffer into which the value is retrieved,
 *		"size" is the size of the buffer (and the object to retrieve),
 *		"refstr" is a reference string used when printing error meessages,
 *		if "refstr" starts with a '!', then a failure on read will not
 *			be fatal (this may seem like a silly way to do things, but I
 *			really didn't want the overhead of another argument).
 *
 */
int
getkval(
		unsigned long offset,
		int *ptr,
		int size,
		char *refstr)
{
	if (lseek(kmem, (long) offset, 0) == -1)
	{
		if (*refstr == '!')
			refstr++;
		(void) fprintf(stderr, "%s: lseek to %s: %s\n",
					   myname, refstr, sys_errlist[errno]);
		quit(22);
	}
	if (read(kmem, (char *) ptr, size) == -1)
		if (*refstr == '!')
			/* we lost the race with the kernel, process isn't in memory */
			return (0);
		else
		{
			(void) fprintf(stderr, "%s: reading %s: %s\n",
						   myname, refstr, sys_errlist[errno]);
			quit(23);
		}
	return (1);
}

/* ----------------- comparison routines for qsort ---------------- */

/* First, the possible comparison keys.  These are defined in such a way
   that they can be merely listed in the source code to define the actual
   desired ordering.
 */

#define ORDERKEY_PCTCPU  if (dresult = percent_cpu (p2) - percent_cpu (p1),\
				 (result = dresult > 0.0 ? 1 : \
				 dresult < 0.0 ? -1 : 0) == 0)

#define ORDERKEY_CPTICKS if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0)
#define ORDERKEY_STATE	 if ((result = (long) (sorted_state[p2->pr_state] - \
				   sorted_state[p1->pr_state])) == 0)

#define ORDERKEY_PRIO	 if ((result = p2->pr_pri	 - p1->pr_pri)	  == 0)
#define ORDERKEY_RSSIZE  if ((result = p2->pr_rssize - p1->pr_rssize) == 0)
#define ORDERKEY_MEM	 if ((result = (p2->pr_size  - p1->pr_size))  == 0)

#define ORDERKEY_PID	 if ((result = (p2->pr_pid	- p1->pr_pid))	== 0)
#define ORDERKEY_UID	 if ((result = (p2->pr_uid	- p1->pr_uid))	== 0)
#define ORDERKEY_RPID	 if ((result = (p1->pr_pid	- p2->pr_pid))	== 0)
#define ORDERKEY_RUID	 if ((result = (p1->pr_uid	- p2->pr_uid))	== 0)

/* states enum {SONPROC, SRUN, SSLEEP, SSTOP, SIDL}  */
unsigned char sorted_state[] =
{
	7,							/* onproc		*/
	6,							/* run				*/
	5,							/* sleep		*/
	4,							/* stop				*/
	3,							/* idle			*/
	2,							/* zombie		*/
	0,							/* unused				*/
	0							/* unused			*/
};

#if 0
/*
 *	proc_compare - original singleton comparison function for "qsort"
 *	Compares the resource consumption of two processes using five
 *		distinct keys.	The keys (in descending order of importance) are:
 *		percent cpu, cpu ticks, state, resident set size, total virtual
 *		memory usage.  The process states are ordered as follows (from least
 *		to most important):  WAIT, zombie, sleep, stop, start, run.  The
 *		array declaration below maps a process state index into a number
 *		that reflects this ordering.
 */
 /* default comparison rtn */
int
original_proc_compare(
					  struct prpsinfo **pp1,
					  struct prpsinfo **pp2)
{
	register struct prpsinfo *p1;
	register struct prpsinfo *p2;
	register long result;
	double		dresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;

	/* compare percent cpu (pctcpu) */
	dresult = percent_cpu(p2) - percent_cpu(p1);
	result = dresult > 0.0 ? 1 :
		dresult < 0.0 ? -1 : 0;
	if (result)
	{
		/* use cpticks to break the tie */
		if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0)
		{
			/* use process state to break the tie */
			if ((result = (long) (sorted_state[p2->pr_state] -
								  sorted_state[p1->pr_state])) == 0)
			{
				/* use priority to break the tie */
				if ((result = p2->pr_pri - p1->pr_pri) == 0)
				{
					/* use resident set size (rssize) to break the tie */
					if ((result = p2->pr_rssize - p1->pr_rssize) == 0)
					{
						/* use total memory to break the tie */
						result = (p2->pr_size - p1->pr_size);
					}
				}
			}
		}
	}
	return (result);
}
#endif							/* original comparison rtn */

/* compare_state - comparison function for sorting by state,pri,time,size */
int
proc_compare(
			 struct prpsinfo **pp1,
			 struct prpsinfo **pp2)
{
	register struct prpsinfo *p1;
	register struct prpsinfo *p2;
	register long result;
	double		dresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;

	ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_CPTICKS
		ORDERKEY_RSSIZE
		ORDERKEY_MEM
		ORDERKEY_PCTCPU
		;

	return (result);
}


/* compare_cpu - the comparison function for sorting by cpu % (deflt) */
int
compare_cpu(
			struct prpsinfo **pp1,
			struct prpsinfo **pp2)
{
	register struct prpsinfo *p1;
	register struct prpsinfo *p2;
	register long result;
	double		dresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;

	ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_RSSIZE
		ORDERKEY_MEM
		;

	return (result);
}

/* compare_size - the comparison function for sorting by total memory usage */
int
compare_size(
			 struct prpsinfo **pp1,
			 struct prpsinfo **pp2)
{
	register struct prpsinfo *p1;
	register struct prpsinfo *p2;
	register long result;
	double		dresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;

	ORDERKEY_MEM
		ORDERKEY_RSSIZE
		ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_STATE
		ORDERKEY_PRIO
		;

	return (result);
}

/* compare_res - the comparison function for sorting by resident set size */
int
compare_res(
			struct prpsinfo **pp1,
			struct prpsinfo **pp2)
{
	register struct prpsinfo *p1;
	register struct prpsinfo *p2;
	register long result;
	double		dresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;

	ORDERKEY_RSSIZE
		ORDERKEY_MEM
		ORDERKEY_PCTCPU
		ORDERKEY_CPTICKS
		ORDERKEY_STATE
		ORDERKEY_PRIO
		;

	return (result);
}

/* compare_time - the comparison function for sorting by total cpu time */
int
compare_time(
			 struct prpsinfo **pp1,
			 struct prpsinfo **pp2)
{
	register struct prpsinfo *p1;
	register struct prpsinfo *p2;
	register long result;
	double		dresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;

	ORDERKEY_CPTICKS
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return (result);
}

/* compare_pid - the comparison function for sorting by pid */
int
compare_pid(
			struct prpsinfo **pp1,
			struct prpsinfo **pp2)
{
	register struct prpsinfo *p1;
	register struct prpsinfo *p2;
	register long result;
	double		dresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;

	ORDERKEY_PID
		ORDERKEY_CPTICKS
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return (result);
}

/* compare_uid - the comparison function for sorting by user ID */
int
compare_uid(
			struct prpsinfo **pp1,
			struct prpsinfo **pp2)
{
	register struct prpsinfo *p1;
	register struct prpsinfo *p2;
	register long result;
	double		dresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;

	ORDERKEY_UID
		ORDERKEY_CPTICKS
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return (result);
}

/* compare_rpid - the comparison function for sorting by pid ascending */
int
compare_rpid(
			 struct prpsinfo **pp1,
			 struct prpsinfo **pp2)
{
	register struct prpsinfo *p1;
	register struct prpsinfo *p2;
	register long result;
	double		dresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;

	ORDERKEY_RPID
		ORDERKEY_CPTICKS
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return (result);
}

/* compare_uid - the comparison function for sorting by user ID ascending */
int
compare_ruid(
			 struct prpsinfo **pp1,
			 struct prpsinfo **pp2)
{
	register struct prpsinfo *p1;
	register struct prpsinfo *p2;
	register long result;
	double		dresult;

	/* remove one level of indirection */
	p1 = *pp1;
	p2 = *pp2;

	ORDERKEY_RUID
		ORDERKEY_CPTICKS
		ORDERKEY_PCTCPU
		ORDERKEY_STATE
		ORDERKEY_PRIO
		ORDERKEY_MEM
		ORDERKEY_RSSIZE
		;

	return (result);
}


/* ---------------- helper rtns ---------------- */

/*
 * get process table
 */
void
getptable(struct prpsinfo *baseptr)
{
	struct prpsinfo *currproc;	/* pointer to current proc structure	*/
	int			numprocs = 0;
	struct dirent *direntp;

	currproc = baseptr;
	for (rewinddir(procdir); direntp = readdir(procdir);)
	{
		int			fd;
		char		buf[30];

		sprintf(buf, "%s/psinfo", direntp->d_name);

		if ((fd = open(buf, O_RDONLY)) < 0)
			continue;

		if (read(fd, currproc, sizeof(psinfo_t)) != sizeof(psinfo_t))
		{
			(void) close(fd);
			continue;
		}

		numprocs++;
		currproc++;

		(void) close(fd);

		/* Atypical place for growth */
		if (numprocs >= maxprocs)
		{
			reallocproc(2 * numprocs);
			currproc = (struct prpsinfo *)
				((char *) baseptr + sizeof(psinfo_t) * numprocs);
		}

	}

	if (nproc != numprocs)
		nproc = numprocs;
}

/* return the owner of the specified process, for use in commands.c as we're
   running setuid root */
int
proc_owner(int pid)
{
	register struct prpsinfo *p;
	int			i;

	for (i = 0, p = pbase; i < nproc; i++, p++)
		if (p->pr_pid == (pid_t) pid)
			return ((int) (p->pr_uid));

	return (-1);
}

int
setpriority(int dummy, int who, int niceval)
{
	int			scale;
	int			prio;
	pcinfo_t	pcinfo;
	pcparms_t	pcparms;
	tsparms_t  *tsparms;

	strcpy(pcinfo.pc_clname, "TS");
	if (priocntl(0, 0, PC_GETCID, (caddr_t) & pcinfo) == -1)
		return (-1);

	prio = niceval;
	if (prio > PRIO_MAX)
		prio = PRIO_MAX;
	else if (prio < PRIO_MIN)
		prio = PRIO_MIN;

	tsparms = (tsparms_t *) pcparms.pc_clparms;
	scale = ((tsinfo_t *) pcinfo.pc_clinfo)->ts_maxupri;
	tsparms->ts_uprilim = tsparms->ts_upri = -(scale * prio) / 20;
	pcparms.pc_cid = pcinfo.pc_cid;

	if (priocntl(P_PID, who, PC_SETPARMS, (caddr_t) & pcparms) == -1)
		return (-1);

	return (0);
}


get_swapinfo(long *total, long *fr)
{
	register int cnt,
				i;
	register long t,
				f;
	struct swaptable *swt;
	struct swapent *ste;
	static char path[256];

	/* get total number of swap entries */
	cnt = swapctl(SC_GETNSWP, 0);

	/* allocate enough space to hold count + n swapents */
	swt = (struct swaptable *) malloc(sizeof(int) +
									  cnt * sizeof(struct swapent));
	if (swt == NULL)
	{
		*total = 0;
		*fr = 0;
		return;
	}
	swt->swt_n = cnt;

	/*
	 * fill in ste_path pointers: we don't care about the paths, so we point
	 * them all to the same buffer
	 */
	ste = &(swt->swt_ent[0]);
	i = cnt;
	while (--i >= 0)
	{
		ste++->ste_path = path;
	}

	/* grab all swap info */
	swapctl(SC_LIST, swt);

	/* walk thru the structs and sum up the fields */
	t = f = 0;
	ste = &(swt->swt_ent[0]);
	i = cnt;
	while (--i >= 0)
	{
		/* dont count slots being deleted */
		if (!(ste->ste_flags & ST_INDEL))
		{
			t += ste->ste_pages;
			f += ste->ste_free;
		}
		ste++;
	}

	/* fill in the results */
	*total = t;
	*fr = f;
	free(swt);
}


/*
 * When we reach a proc limit, we need to realloc the stuff.
 */
static void
reallocproc(int n)
{
	int			bytes;
	struct oldproc *op,
			   *endbase;

	if (n < maxprocs)
		return;

	maxprocs = n;

	/* allocate space for proc structure array and array of pointers */
	bytes = maxprocs * sizeof(psinfo_t);
	pbase = (struct prpsinfo *) realloc(pbase, bytes);
	pref = (struct prpsinfo **) realloc(pref,
										maxprocs * sizeof(struct prpsinfo *));

	/* Just in case ... */
	if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL)
	{
		fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
		quit(1);
	}
}

/* ---------------------------------------------------------------- */
/* Access kernel Metrics
 * SVR5 uses metreg inteface to Kernel statistics (metrics)
 *	see /usr/include/mas.h, /usr/include/metreg.h
 */

#include <sys/mman.h>
#include <sys/dl.h>
#include <mas.h>
#include <metreg.h>

static int	md;					/* metric descriptor handle */
static uint32 ncpu;				/* number of processors in system */

/* fwd dcls */
static uint32 kmet_get_cpu(int type, char *desc);
static void kmet_verify(
						uint32 md, metid_t id, units_t units, type_t mettype,
						uint32 metsz, uint32 nobj, uint32 nlocs, resource_t res_id,
						uint32 ressz);


static int
get_cpustates(int *new)
{
	new[0] = (int) kmet_get_cpu(MPC_CPU_IDLE, "idle");
	new[1] = (int) kmet_get_cpu(MPC_CPU_USR, "usr");
	new[2] = (int) kmet_get_cpu(MPC_CPU_SYS, "sys");
	new[3] = (int) kmet_get_cpu(MPC_CPU_WIO, "wio");
}


/* initialises kernel metrics access and gets #cpus */
static int
kmet_init()
{
	uint32	   *ncpu_p;

	/* open (and map in) the metric access file and assoc data structures */
	if ((md = mas_open(MAS_FILE, MAS_MMAP_ACCESS)) < 0)
	{
		(void) fprintf(stderr, "mas_open failed\n");
		mas_perror();
		quit(10);
	}

	/* verify the NCPU metric is everything we expect */
	kmet_verify(md, NCPU, CPUS, CONFIGURABLE, sizeof(short),
				1, 1, MAS_SYSTEM, sizeof(uint32));

	/* get the number of cpu's on the system */
	if ((ncpu_p = (uint32 *) mas_get_met(md, NCPU, 0)) == NULL)
	{
		(void) fprintf(stderr, "mas_get_met of ncpu failed\n");
		mas_perror();
		quit(12);
	}
	ncpu = (uint32) (*(short *) ncpu_p);

	/*
	 * check that MPC_CPU_IDLE is of the form we expect ( paranoically we
	 * should check the rest as well but ... )
	 */
	kmet_verify(md, MPC_CPU_IDLE, TIX, PROFILE, sizeof(uint32),
				1, ncpu, NCPU, sizeof(short));

	kmet_verify(md, PROCUSE, PROCESSES, COUNT, sizeof(uint32),
				1, 1, MAS_SYSTEM, sizeof(uint32));
	nproc = kmet_get_nproc();

	return 0;
}

/* done with kernel metrics access */
static int
kmet_done()
{
	if (mas_close(md) < 0)
	{
		(void) fprintf(stderr, "mas_close failed\n");
		mas_perror();
		quit(14);
	}
}


static uint32
kmet_get_cpu(int type, char *desc)
{
	int			i;
	uint32		r = 0,
				rtot = 0;

	for (i = 0; i < ncpu; i++)
	{
		r = *(uint32 *) mas_get_met(md, (metid_t) type, 0);
		if (!r)
		{
			(void) fprintf(stderr, "mas_get_met of %s failed\n", desc);
			mas_perror();
			quit(12);
		}
		rtot += r;				/* sum them for multi cpus */
	}
	return rtot /* /ncpu */ ;
}

static int
kmet_get_freemem()
{
	dl_t	   *fm_p,
				fm,
				fmc,
				denom;
	time_t		td1;
	static time_t td0;
	static dl_t fm_old;


	td1 = time(NULL);
	if ((fm_p = (dl_t *) mas_get_met(md, FREEMEM, 0)) == NULL)
	{
		(void) fprintf(stderr, "mas_get_met of freemem failed\n");
		mas_perror();
		quit(12);
	}
	fm = *fm_p;

	denom.dl_hop = 0;
	denom.dl_lop = (long) (td1 - td0);
	td0 = td1;

	/*
	 * calculate the freemem difference divided by the time diff giving the
	 * freemem in that time sample (new - old) / (time_between_samples)
	 */
	fmc = lsub(fm, fm_old);
	fm_old = fm;

	fmc = ldivide(fmc, denom);
	return fmc.dl_lop;
}

/*
 * return # of processes currently executing on system
 */
static int
kmet_get_nproc()
{
	uint32	   *p;

	if ((p = (uint32 *) mas_get_met(md, PROCUSE, 0)) == NULL)
	{
		(void) fprintf(stderr, "mas_get_met of procuse failed\n");
		mas_perror();
		quit(11);
	}
	nproc = (int) *p;
}


/*
 * Function:	kmet_verify
 * renamed from mas_usrtime example verify_met() fm Doug Souders
 *
 * Description: Verify the registration data associated with this metric
 *		match what are expected.  Cautious consumer applications
 *		should do this sort of verification before using metrics.
 */
static void
kmet_verify(
			uint32 md,			/* metric descriptor				*/
			metid_t id,			/* metric id number					*/
			units_t units,		/* expected units of metric			*/
			type_t mettype,		/* expected type of metric			*/
			uint32 metsz,		/* expected object size of metric	*/
			uint32 nobj,		/* expected number of array elements */
			uint32 nlocs,		/* expected number of instances		*/
			resource_t res_id,	/* expected resource id number		*/
			uint32 ressz		/* expected resource object size	*/
)
{

	char	   *name;			/* the name of the metric	*/
	units_t    *units_p;		/* the units of the metric	*/
	type_t	   *mettype_p;		/* type field of the metric */
	uint32	   *objsz_p;		/* size of each element in met	*/
	uint32	   *nobj_p;			/* num of elements >1 then array */
	uint32	   *nlocs_p;		/* total number of instances	*/
	uint32	   *status_p;		/* status word (update|avail)	*/
	resource_t *resource_p;		/* the resource list of the met */
	uint32	   *resval_p;		/* pointer to resource		*/
	uint32	   *ressz_p;		/* size of the resource met */

	if (!(name = mas_get_met_name(md, id)))
	{
		(void) fprintf(stderr, "mas_get_met_name failed\n");
		mas_perror();
		quit(11);
	}

	if (!(status_p = mas_get_met_status(md, id)))
	{
		(void) fprintf(stderr, "mas_get_met_status of %s failed\n",
					   name);
		mas_perror();
		quit(11);
	}
	if (*status_p != MAS_AVAILABLE)
	{
		(void) fprintf(stderr, "unexpected status word for %s\n"
					   "- expected %u got %u\n",
					   name, MAS_AVAILABLE, *status_p);
		quit(11);
	}
	if (!(units_p = mas_get_met_units(md, id)))
	{
		(void) fprintf(stderr, "mas_get_met_units of %s failed\n",
					   name);
		mas_perror();
		quit(11);
	}
	if (units != *units_p)
	{
		(void) fprintf(stderr, "unexpected units for %s\n"
					   "- expected %u got %u\n",
					   name, units, *units_p);
		quit(11);
	}

	if (!(mettype_p = mas_get_met_type(md, id)))
	{
		(void) fprintf(stderr, "mas_get_met_type of %s failed\n",
					   name);
		mas_perror();
		quit(11);
	}
	if (mettype != *mettype_p)
	{
		(void) fprintf(stderr, "unexpected metric type for %s\n"
					   "- expected %u got %u\n",
					   name, mettype, *mettype_p);
		quit(11);
	}

	if (!(objsz_p = mas_get_met_objsz(md, id)))
	{
		(void) fprintf(stderr, "mas_get_met_objsz of %s failed\n", name);
		mas_perror();
		quit(11);
	}
	if (*objsz_p != metsz)
	{
		(void) fprintf(stderr, "unexpected object size for %s\n"
					   "- expected %u got %u\n",
					   name, metsz, *objsz_p);
		quit(11);
	}

	if (!(nobj_p = mas_get_met_nobj(md, id)))
	{
		(void) fprintf(stderr, "mas_get_met_nobj of %s failed\n", name);
		mas_perror();
		quit(11);
	}
	if (nobj != *nobj_p)
	{
		(void) fprintf(stderr, "unexpected number of objects for %s\n"
					   "- expected %u got %u\n",
					   name, nobj, *nobj_p);
		quit(11);
	}

	/* get the number of instances that libmas thinks it knows about  */
	if (!(nlocs_p = mas_get_met_nlocs(md, id)))
	{
		(void) fprintf(stderr, "mas_get_met_nlocs of %s failed\n", name);
		mas_perror();
		quit(11);
	}
	if (nlocs != *nlocs_p)
	{
		(void) fprintf(stderr, "unexpected number of instances for %s"
					   " - expected %u got %u\n",
					   name, nlocs, *nlocs_p);
		quit(11);

	}
	/* get the resource list for the metric */
	if (!(resource_p = mas_get_met_resources(md, id)))
	{
		(void) fprintf(stderr, "mas_get_met_resources of %s failed\n", name);
		mas_perror();
		quit(11);
	}
	if (*resource_p != res_id)
	{
		(void) fprintf(stderr, "unexpected resource id for %s\n"
					   "- expected %u got %u\n",
					   name, res_id, *resource_p);
		quit(11);
	}
	/* get the size of the resource  */
	if (!(ressz_p = mas_get_met_objsz(md, (metid_t) (*resource_p))))
	{
		(void) fprintf(stderr, "mas_get_met_objsz of resource failed\n");
		mas_perror();
		quit(11);
	}
	if (*ressz_p != ressz)
	{
		(void) fprintf(stderr, "unexpected resource size for %s\n"
					   "- expected %u got %u\n",
					   name, ressz, *ressz_p);
		quit(11);
	}
/*
 *	get the address of the resource
 */
	if (!(resval_p = (uint32 *) mas_get_met(md, *resource_p, 0)))
	{
		(void) fprintf(stderr, "mas_get_met of resource failed\n");
		mas_perror();
		quit(11);
	}
	if (ressz == sizeof(short))
	{
		if ((uint32) (*(short *) resval_p) != nlocs)
		{
			(void) fprintf(stderr, "unexpected resource value for %s\n"
						   "- expected %u got %u\n",
						   name, nlocs, (uint32) (*(short *) resval_p));
			quit(11);
		}
	}
	else
	{							/* assume size of uint32 */
		if (*resval_p != nlocs)
		{
			(void) fprintf(stderr, "unexpected resource value for %s\n"
						   "- expected %u got %u\n",
						   name, nlocs, *resval_p);
			quit(11);
		}
	}
	return;
}

void
get_io_info(struct io_info *io_info)
{
	/* Not supported yet */
	memset(io_info, 0, sizeof(*io_info));
}
07070100000036000081A400000000000000000000000166622DE8000000A4000000000000000000000000000000000000002400000000pg_top-4.1.1/machine/m_svr5.man.rstSVR5 CREDITS
============

The SVR5 port was generated by Mike Hopkirk from the SVR42 port by David Cutter
with lots of help from Kurt Gollhardt and Doug Souders 

07070100000037000081A400000000000000000000000166622DE80000016F000000000000000000000000000000000000001700000000pg_top-4.1.1/message.h/* interface declaration for display messages */
/* This is a small subset of the interface from display.c that
   just contains the calls for displaying messages.  Do not include
   this and display.h at the same time. */

#ifndef _MESSAGE_H_
#define _MESSAGE_H_

void		new_message(int type, char *msgfmt,...);
void		clear_message();

#endif							/* _MESSAGE_H_ */
07070100000038000081A400000000000000000000000166622DE8000005E7000000000000000000000000000000000000001200000000pg_top-4.1.1/os.h#ifndef _OS_H_
#define _OS_H_

#include "config.h"

#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>

#if TIME_WITH_SYS_TIME
#include <sys/time.h>
#include <time.h>
#else
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <time.h>
#endif
#endif

#if STDC_HEADERS
#include <string.h>
#include <stdlib.h>
#define setbuffer(f, b, s)	setvbuf((f), (b), (b) ? _IOFBF : _IONBF, (s))
#define memzero(a, b)		memset((a), 0, (b))
#else							/* !STDC_HEADERS */
#ifndef HAVE_STRCHR
#define strchr(a, b)		index((a), (b))
#define strrchr(a, b)		rindex((a), (b))
#endif							/* HAVE_STRCHR */
#ifdef HAVE_MEMCPY
#define memzero(a, b)		memset((a), 0, (b))
#else
#define memcpy(a, b, c)		bcopy((b), (a), (c))
#define memzero(a, b)		bzero((a), (b))
#define memcmp(a, b, c)		bcmp((a), (b), (c))
#endif							/* HAVE_MEMCPY */
#ifdef HAVE_STRINGS_H
#include <strings.h>
#else
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#endif
char	   *getenv();
#if !defined (__macosx)
caddr_t		malloc();

*/
#endif
#endif							/* STDC_HEADERS */

/* we must have both sighold and sigrelse to use them */
#if defined(HAVE_SIGHOLD) && !defined(HAVE_SIGRELSE)
#undef HAVE_SIGHOLD
#endif

/* include unistd.h on Solaris, to get fewer
 * warrnigs if compiling with Sun's Studio compilers.
 * This should be included on anything that has it, but
 * I don't have time to figure out if that will cause
 * other problems.	--wnl
 */
#if defined (__sun) && defined (__SVR4)
#include <unistd.h>
#endif

#endif							/* _OS_H_ */
07070100000039000081A400000000000000000000000166622DE800001832000000000000000000000000000000000000001200000000pg_top-4.1.1/pg.c/*	Copyright (c) 2007-2019, Mark Wong */

#include <stdlib.h>
#include <string.h>
#include <strings.h>

#include "display.h"
#include "pg.h"
#include "pg_top.h"

#define QUERY_PROCESSES \
		"WITH lock_activity AS\n" \
		"(\n" \
		"     SELECT pid, count(*) AS lock_count\n" \
		"     FROM pg_locks\n" \
		"     WHERE relation IS NOT NULL\n" \
		"     GROUP BY pid\n" \
		")\n" \
		"SELECT a.pid, query, state, usename,\n" \
		"       extract(EPOCH FROM age(clock_timestamp(),\n" \
		"                              xact_start))::BIGINT,\n" \
		"       extract(EPOCH FROM age(clock_timestamp(),\n" \
		"                              query_start))::BIGINT,\n" \
		"       coalesce(lock_count, 0) AS lock_count\n" \
		"FROM pg_stat_activity a LEFT OUTER JOIN lock_activity b\n" \
		"  ON a.pid = b.pid;"

#define QUERY_PROCESSES_9_1 \
		"SELECT procpid, current_query\n" \
		"FROM pg_stat_activity;"

#define CURRENT_QUERY \
		"SELECT query\n" \
		"FROM pg_stat_activity\n" \
		"WHERE pid = %d;"

#define CURRENT_QUERY_9_1 \
		"SELECT current_query\n" \
		"FROM pg_stat_activity\n" \
		"WHERE procpid = %d;"

#define REPLICATION \
		"SELECT pid, usename, application_name, client_addr, state,\n" \
		"       pg_current_wal_insert_lsn() AS primary,\n" \
		"       sent_lsn, write_lsn, flush_lsn,\n" \
		"       replay_lsn, \n" \
		"       pg_wal_lsn_diff(pg_current_wal_insert_lsn(),\n" \
		"                       sent_lsn) as sent_lag,\n" \
		"       pg_wal_lsn_diff(pg_current_wal_insert_lsn(),\n" \
		"                       write_lsn) as write_lag,\n" \
		"       pg_wal_lsn_diff(pg_current_wal_insert_lsn(),\n" \
		"                       flush_lsn) as flush_lag,\n" \
		"       pg_wal_lsn_diff(pg_current_wal_insert_lsn(),\n" \
		"                       replay_lsn) as replay_lag\n" \
		"       FROM pg_stat_replication;"

#define REPLICATION_9_6 \
		"SELECT pid, usename, application_name, client_addr, state,\n" \
		"       pg_current_xlog_insert_location() AS primary,\n" \
		"       sent_location, write_location, flush_location,\n" \
		"       replay_location, \n" \
		"       pg_xlog_location_diff(pg_current_xlog_insert_location(),\n" \
		"                             sent_location) as sent_lag,\n" \
		"       pg_xlog_location_diff(pg_current_xlog_insert_location(),\n" \
		"                             write_location) as write_lag,\n" \
		"       pg_xlog_location_diff(pg_current_xlog_insert_location(),\n" \
		"                             flush_location) as flush_lag,\n" \
		"       pg_xlog_location_diff(pg_current_xlog_insert_location(),\n" \
		"                             replay_location) as replay_lag\n" \
		"       FROM pg_stat_replication;"

#define GET_LOCKS \
		"SELECT datname, nspname, r.relname, i.relname, mode, granted\n" \
		"FROM pg_stat_activity, pg_locks\n" \
		"LEFT OUTER JOIN pg_class r \n" \
		"  ON relation = r.oid\n"\
		" AND r.relkind = 'r'\n" \
		"LEFT OUTER JOIN pg_class i \n" \
		"  ON relation = i.oid\n"\
		" AND i.relkind = 'i'\n" \
		"LEFT OUTER JOIN pg_namespace nsp\n" \
		"  ON coalesce(r.relnamespace, i.relnamespace) = nsp.oid\n" \
		"WHERE pg_stat_activity.pid = %d\n" \
		"  AND pg_stat_activity.pid = pg_locks.pid\n" \
		"  AND relation IS NOT NULL;"

#define GET_LOCKS_9_1 \
		"SELECT datname, nspname, r.relname, i.relname, mode, granted\n" \
		"FROM pg_stat_activity, pg_locks\n" \
		"LEFT OUTER JOIN pg_class r \n" \
		"  ON relation = r.oid\n"\
		" AND r.relkind = 'r'\n" \
		"LEFT OUTER JOIN pg_class i \n" \
		"  ON relation = i.oid\n"\
		" AND i.relkind = 'i'\n" \
		"LEFT OUTER JOIN pg_namespace nsp\n" \
		"  ON coalesce(r.relnamespace, i.relnamespace) = nsp.oid\n" \
		"WHERE procpid = %d\n" \
		"  AND procpid = pid\n" \
		"  AND relation IS NOT NULL;"

int			pg_version(PGconn *);

void
connect_to_db(struct pg_conninfo_ctx *conninfo)
{
	int			i;
	const char *keywords[6] = {"host", "port", "user", "password", "dbname",
	NULL};

	if (conninfo->persistent && PQsocket(conninfo->connection) >= 0)
		return;

	conninfo->connection = PQconnectdbParams(keywords, conninfo->values, 1);
	if (PQstatus(conninfo->connection) != CONNECTION_OK)
	{
		new_message(MT_standout | MT_delayed, " %s",
					PQerrorMessage(conninfo->connection));

		PQfinish(conninfo->connection);
		conninfo->connection = NULL;
		return;
	}

	if (conninfo->persistent)
		for (i = 0; i < 5; i++)
			if (conninfo->values[i] != NULL)
				free((void *) conninfo->values[i]);

	PQexec(conninfo->connection,
			"SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL " \
			"READ UNCOMMITTED;");
}

void
disconnect_from_db(struct pg_conninfo_ctx *conninfo)
{
	if (conninfo->persistent)
		return;
	PQfinish(conninfo->connection);
}

PGresult *
pg_locks(PGconn *pgconn, int procpid)
{
	char	   *sql;
	PGresult   *pgresult;

	if (pg_version(pgconn) >= 902)
	{
		sql = (char *) malloc(strlen(GET_LOCKS) + 7);
		sprintf(sql, GET_LOCKS, procpid);
	}
	else
	{
		sql = (char *) malloc(strlen(GET_LOCKS) + 7);
		sprintf(sql, GET_LOCKS_9_1, procpid);
	}
	pgresult = PQexec(pgconn, sql);
	free(sql);
	return pgresult;
}

PGresult *
pg_processes(PGconn *pgconn)
{
	PGresult   *pgresult;

	PQexec(pgconn, "BEGIN;");
	PQexec(pgconn, "SET statement_timeout = '2s';");
	if (pg_version(pgconn) >= 902)
	{
		pgresult = PQexec(pgconn, QUERY_PROCESSES);
	}
	else
	{
		pgresult = PQexec(pgconn, QUERY_PROCESSES_9_1);
	}
	PQexec(pgconn, "ROLLBACK;;");
	return pgresult;
}

PGresult *
pg_replication(PGconn *pgconn)
{
	PGresult   *pgresult;

	PQexec(pgconn, "BEGIN;");
	PQexec(pgconn, "SET statement_timeout = '2s';");
	if (pg_version(pgconn) >= 1000) {
		pgresult = PQexec(pgconn, REPLICATION);
	} else {
		pgresult = PQexec(pgconn, REPLICATION_9_6);
	}
	PQexec(pgconn, "ROLLBACK;");
	return pgresult;
}

PGresult *
pg_query(PGconn *pgconn, int procpid)
{
	char	   *sql;
	PGresult   *pgresult;

	if (pg_version(pgconn) >= 902)
	{
		sql = (char *) malloc(strlen(CURRENT_QUERY) + 7);
		sprintf(sql, CURRENT_QUERY, procpid);
	}
	else
	{
		sql = (char *) malloc(strlen(CURRENT_QUERY_9_1) + 7);
		sprintf(sql, CURRENT_QUERY_9_1, procpid);
	}
	pgresult = PQexec(pgconn, sql);
	free(sql);

	return pgresult;
}

int
pg_version(PGconn *pgconn)
{
	return PQserverVersion(pgconn) / 100;
}
0707010000003A000081A400000000000000000000000166622DE8000003E3000000000000000000000000000000000000001200000000pg_top-4.1.1/pg.h/*	Copyright (c) 2007-2019, Mark Wong */

#ifndef _PG_H_
#define _PG_H_

#include <libpq-fe.h>

struct pg_conninfo_ctx
{
	PGconn	   *connection;
	int			persistent;
	const char *values[6];
};

void		connect_to_db(struct pg_conninfo_ctx *);
void		disconnect_from_db(struct pg_conninfo_ctx *);

PGresult   *pg_locks(PGconn *, int);
PGresult   *pg_processes(PGconn *);
PGresult   *pg_replication(PGconn *);
PGresult   *pg_query(PGconn *, int);

enum BackendState
{
	STATE_UNDEFINED,
	STATE_IDLE,
	STATE_RUNNING,
	STATE_IDLEINTRANSACTION,
	STATE_FASTPATH,
	STATE_IDLEINTRANSACTION_ABORTED,
	STATE_DISABLED
};

enum pg_stat_activity
{
	PROC_PID = 0,
	PROC_QUERY,
	PROC_STATE,
	PROC_USENAME,
	PROC_XSTART,
	PROC_QSTART,
	PROC_LOCKS
};

enum pg_stat_replication
{
	REP_PID = 0,
	REP_USENAME,
	REP_APPLICATION_NAME,
	REP_CLIENT_ADDR,
	REP_STATE,
	REP_WAL_INSERT,
	REP_SENT,
	REP_WRITE,
	REP_FLUSH,
	REP_REPLAY,
	REP_SENT_LAG,
	REP_WRITE_LAG,
	REP_FLUSH_LAG,
	REP_REPLAY_LAG
};

#endif							/* _PG_H_ */
0707010000003B000081A400000000000000000000000166622DE800003B1A000000000000000000000000000000000000001D00000000pg_top-4.1.1/pg_top.1.rst.in========
 pg_top
========

---------------------------------------------------------------------
display and update information about the top cpu PostgreSQL processes
---------------------------------------------------------------------

:Author: William LeFebvre, Mark Wong
:Manual section: 1
:Version: 4.0.0

SYNOPSIS
========

**pg_top** [option...] [number]

DESCRIPTION
===========

*pg_top* displays the top processes on the system and periodically updates this
information.  Raw cpu percentage is used to rank the processes.  If *number* is
given, then the top *number* processes will be displayed instead of the
default.

*pg_top* makes a distinction between terminals that support advanced
capabilities and those that do not.  This distinction affects the choice of
defaults for certain options.  In the remainder of this document, an
"intelligent" terminal is one that supports cursor addressing, clear screen,
and clear to end of line.  Conversely, a \*(lqdumb\*(rq terminal is one that
does not support such features.  If the output of *pg_top* is redirected to a
file, it acts as if it were being run on a dumb terminal.

OPTIONS
=======

-b, --batch   Use "batch" mode.  In this mode, all input from the terminal is
              ignored.  Interrupt characters (such as ^C and ^\e) still have an
              effect.  This is the default on a dumb terminal, or when the
              output is not a terminal.
-C, --color-mode   Turn off the use of color in the display.
-c, --show-command   Show the command name for each process. Default is to show
                     the full command line.  This option is not supported on
                     all platforms.
-h HOST, --host=HOST   Specifies the host name of the machine on which the server is
                  running. If the value begins with a slash, it is used as the
                  directory for the Unix domain socket. The default is taken
                  from the PGHOST environment variable, if set.

-I, --hide-idle   Do not display idle processes.  By default, pg_top displays
                  both active and idle processes.
-i, --interactive   Use "lqinteractive" mode.  In this mode, any input is
                    immediately read for processing.  See the section on
                    "Interactive Mode" for an explanation of which keys perform
                    what functions.  After the command is processed, the screen
                    will immediately be updated, even if the command was not
                    understood.  This mode is the default when standard output
                    is an intelligent terminal.
-n, --non-interactive   Use "non-interactive" mode.  This is identical to
                        "batch" mode.
-o FIELD, --order-field=FIELD   Sort the process display area on the specified
                                field.  The field name is the name of the
                                column as seen in the output, but in lower
                                case.  Likely values are "cpu", "size", "res",
                                "xtime" and "qtime", but may vary on different
                                operating systems.  Note that not all operating
                                systems support this option.
-p PORT, --port=PORT   Specifies the TCP port or local Unix domain socket file
                       extension on which the server is listening for
                       connections. Defaults to the PGPORT environment
                       variable, if set.
-R   Display WAL sender processes' replication activity to connected standby
     servers.  Only directly connected standbys are listed; no information is
     available about downstream standby servers.
-r, --remote-mode   Monitor a remote database where the database is on a system
                    other than where pg_top is running from.  *pg_top* will
                    monitor a remote database if it has the pg_proctab
                    extension installed.
-s TIME, --set-delay=TIME   Set the delay between screen updates to *TIME*
                            seconds.  The default delay between updates is 5
                            seconds.
-T, --show-tags   List all available color tags and the current set of tests
                  used for color highlighting, then exit.
-U USERNAME, --username=USERNAME   PostgreSQL database user name to connect as.
-V, --version   Write version number information to stderr then exit
                immediately.  No other processing takes place when this option
                is used.  To see current revision information while *pg_top* is
                running, use the help command "?".
-W, --password   Forces *pg_top* to prompt for a password before connecting to
                 a database.  *pg_top* will also keep the database connection
                 open while running, and will clear the database connection
                 from memory for security.
-X   Display I/O activity per process.  This depends on whether the platform
     *pg_top* is run on supports getting I/O statistics per process, or whether
     the database system that pg_proctab is installed on supports getting I/O
     statistics when *pg_top* attempts to get operating system statistics
     remotely.
-x COUNT, --set-display=COUNT   Show only *COUNT* displays, then exit.  A
                                display is considered to be one update of the
                                screen.  This option allows the user to select
                                the number of displays he wants to see before
                                *pg_top* automatically exits.  For intelligent
                                terminals, no upper limit is set.  The default
                                is 1 for dumb terminals.
-z USERNAME, --show-username=USERNAME   Show only those processes owned by
                                        *USERNAME*.  This option currently only
                                        accepts PostgreSQL database user names.

Both *COUNT* and *NUMBER* fields can be specified as "infinite", indicating
that they can stretch as far as possible.  This is accomplished by using any
proper prefix of the keywords "infinity", "maximum, or "all".  The default for
*COUNT* on an intelligent terminal is, in fact, *infinity*.

The environment variable **PG_TOP** is examined for options before the command
line is scanned.  This enables a user to set his or her own defaults.  The
number of processes to display can also be specified in the environment
variable **PG_TOP**.  The options **-C**, **-I**, and **\-u** are actually
toggles.  A second specification of any of these options will negate the first.
Thus a user who has the environment variable **PG_TOP** set to "-I" may use the
command "top -I" to see idle processes.

INTERACTIVE MODE
================

When *pg_top* is running in "interactive mode", it reads commands from the
terminal and acts upon them accordingly.  In this mode, the terminal is put in
"BREAK", so that a character will be processed as soon as it is typed.  Almost
always, a key will be pressed when *pg_top* is between displays; that is, while
it is waiting for *TIME* seconds to elapse.  If this is the case, the command
will be processed and the display will be updated immediately thereafter
(reflecting any changes that the command may have specified).  This happens
even if the command was incorrect.  If a key is pressed while *pg_top* is in
the middle of updating the display, it will finish the update and then process
the command.  Some commands require additional information, and the user will
be prompted accordingly.  While typing this information in, the user's erase
and kill keys (as set up by the command *stty*) are recognized, and a newline
terminates the input.

These commands are currently recognized (^L refers to control-L):

:^L: Redraw the screen.
:A: Display the actual query plan (EXPLAIN ANALYZE) of the currently running
    SQL statement by re-running the SQL statement (prompt for process id.)
:a: Display the top PostgreSQL processor activity. (default)
:C: Toggle the use of color in the display.
:c: Toggle the display of the full command line.
:d: Change the number of displays to show (prompt for new number).  Remember
    that the next display counts as one, so typing **d1** will make *pg_top*
    show one final display and then immediately exit.
:h or ?: Display a summary of the commands (help screen).  Version information
         is included in this display.
:E: Display re-determined execution plan (EXPLAIN) of the SQL statement by a
    backend process (prompt for process id.)
:i: Toggle the display of idle processes.
:L: Display the currently held locks by a backend process (prompt for process
    id.)
:n or #: Change the number of processes to display (prompt for new number).
:o: Change the order in which the display is sorted.  This command is not
    available on all systems.  The sort key names when viewing processes vary
    from system to system but usually include:  "cpu", "res", "size", "xtime"
    and "qtime".  The default is unsorted.  See the interactive help for
    available sort key names.
:Q: Display the currently running query of a backend process (prompt for
    process id.)
:q: Quit *pg_top*.
:s: Change the number of seconds to delay between displays (prompt for new
    number).
:u: Display only processes owned by a specific username (prompt for username).
    If the username specified is simply \*(lq+\*(rq, then processes belonging
    to all users will be displayed.

THE DISPLAY
===========

The actual display varies depending on the specific variant of Unix that the
machine is running.  This description may not exactly match what is seen by
pg_top running on this particular machine.  Differences are listed at the end
of this manual entry.

The top few lines of the display show general information about the state of
the system, including the last process id assigned to a process (on most
systems), the three load averages, the current time, the number of existing
processes, the number of processes in each state (sleeping, running, starting,
zombies, and stopped), and a percentage of time spent in each of the processor
states (user, nice, system, and idle).  It also includes information about
physical and virtual memory allocation.

The remainder of the screen displays information about individual processes.
This display is similar in spirit to *ps(1)* but it is not exactly the same.
The columns displayed by *pg_top* will differ slightly between operating
systems.  Generally, the following display are available:

ACTIVITY DISPLAY
================
:PID: The process id.
:USERNAME: Username of the process's owner (if **-u** is specified, a UID
           column will be substituted for USERNAME).
:SIZE: Total size of the process (text, data, and stack) given in kilobytes.
:RES: Resident memory: current amount of process memory that resides in
      physical memory, given in kilobytes.
:STATE: Current backend state (typically one of "idle", "active", "idltxn",
        "fast", "disable", or "stop".
:XTIME: Elapsed time since the current transactions started.
:QTIME: Elapsed time since the current query started.
:%CPU: Percentage of available cpu time used by this process.
:LOCKS: Number of locks granted to this process.
:COMMAND: Name of the command that the process is currently running.

I/O DISPLAY (Linux only)
========================

:PID: The process id.
:IOPS: Count the number of read and write I/O operations per second.
:IORPS: Count the number of read I/O operations per second.
:IOWPS: Count the number of write I/O operations per second.
:READS: Number of bytes read from storage.
:WRITES: Number of bytes written to storage.
:COMMAND: Name of the command that the process is currently running.

REPLICATION DISPLAY
===================
:PID: The process id.
:USERNAME: Name of the user logged into this WAL sender process
:APPLICATION: Name of the application that is connected to this WAL sender
:CLIENT: IP address of the client connected to this WAL sender
:STATE: Current WAL sender state
:PRIMARY: Current transaction log insert location on primary node
:SENT: Last write-ahead log location sent on this connection
:WRITE: Last write-ahead log location written to disk
:FLUSH: Last write-ahead log location flushed to disk
:REPLAY: Last write-ahead log location replayed into the database
:SLAG: Size of write-ahead log location remaining to be sent
:WLAG: Size of write-ahead log location remaining to be written to disk
:FLAG: Size of write-ahead log location remaining to be flushed to disk
:RLAG: Size of write-ahead log location remaining to be replayed into the
       database

COLOR
=====

*pg_top* supports the use of ANSI color in its output. By default, color is
available but not used.  The environment variable **PG_TOPCOLORS** specifies
colors to use and conditions for which they should be used.  At the present
time, only numbers in the summary display area can be colored. In a future
version it will be possible to highlight numbers in the process display area as
well.  The environment variable is the only way to specify color: there is no
equivalent command line option.  Note that the environment variable
**PG_TOPCOLOURS** is also understood. The British spelling takes precedence.
The use of color only works on terminals that understand and process ANSI color
escape sequences.

The environment variable is a sequence of color specifications, separated by
colons. Each specification takes the form tag=min,max#code where *tag* is the
name of the value to check, *min* and *max* specify a range for the value, and
*code* is an ANSI color code.  Multiple color codes can be listed and separated
with semi-colons.  A missing *min* implies the lowest possible value (usually
0) and a missing *max* implies infinity. The comma must always be present. When
specifying numbers for load averages, they should be multiplied by 100.  For
example, the specification **1min=500,1000#31** indicates that a 1 minute load
average between 5 and 10 should be displayed in red. Color attributes can be
combined.  For example, the specification **5min=1000,#37;41** indicates that a
5 minute load average higher than 10 should be displayed with white characters
on a red background. A special tag named *header* is used to control the color
of the header for process display.  It should be specified with no lower and
upper limits, specifically **header=,#** followed by the ANSI color code.

You can see a list of color codes recognized by this installation of pg_top
with the **-T** option.  This will also show the current set of tests used for
color highligting, as specified in the environment.

ENVIRONMENT
===========

**PG_TOP**		user-configurable defaults for options.  **PG_TOPCOLORS**
color specification

BUGS
====

As with *ps(1)*, things can change while *pg_top* is collecting information for
an update.  The picture it gives is only a close approximation to reality.

SEE ALSO
========

*ps(1),* *stty(1),* *mem(4)*

@MAN_SUPPLEMENT@
0707010000003C000081A400000000000000000000000166622DE800006141000000000000000000000000000000000000001600000000pg_top-4.1.1/pg_top.cchar	   *copyright =
"Copyright (c) 1984 through 2007, William LeFebvre";

/*
 *	Top users/processes display for Unix
 *
 *	This program may be freely redistributed,
 *	but this entire comment MUST remain intact.
 *
 *	Copyright (c) 1984, 1989, William LeFebvre, Rice University
 *	Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University
 *	Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory
 *	Copyright (c) 1996, William LeFebvre, Group sys Consulting
 *	Copyright (c) 2007-2019, Mark Wong
 */

/*
 *	See the file "HISTORY" for information on version-to-version changes.
 */

/*
 *	This file contains "main" and other high-level routines.
 */

/*
 * The following preprocessor variables, when defined, are used to
 * distinguish between different Unix implementations:
 *
 *	FD_SET	 - macros FD_SET and FD_ZERO are used when defined
 */

#include "os.h"
#include <signal.h>
#include <setjmp.h>
#include <ctype.h>
#include <unistd.h>
#include <getopt.h>

/* determine which type of signal functions to use */
#ifdef HAVE_SIGACTION
#undef HAVE_SIGHOLD
#else
#if !defined(HAVE_SIGHOLD) || !defined(HAVE_SIGRELSE)
#define BSD_SIGNALS
#endif
#endif

/* includes specific to top */

#include "pg_top.h"
#include "remote.h"
#include "commands.h"
#include "display.h"			/* interface to display package */
#include "screen.h"				/* interface to screen package */
#include "boolean.h"
#include "utils.h"
#include "version.h"
#ifdef ENABLE_COLOR
#include "color.h"
#endif
#include "port.h"

/* Size of the stdio buffer given to stdout */
#define Buffersize	2048

/* The buffer that stdio will use */
char		stdoutbuf[Buffersize];

/* build signal masks */
#ifndef sigmask
#define sigmask(s)	(1 << ((s) - 1))
#endif

/* for getopt: */
extern int	optind;
extern char *optarg;

/* imported from screen.c */
extern int	overstrike;

/* values which need to be accessed by signal handlers */
int			max_topn;			/* maximum displayable processes */

/* miscellaneous things */
char	   *myname = "pg_top";
jmp_buf		jmp_int;

/* internal variables */
static const char *progname = "pg_top";

void		process_commands(struct pg_top_context *);
static void usage(const char *progname);

/* List of all the options available */
static struct option long_options[] = {
	{"batch", no_argument, NULL, 'b'},
	{"show-command", no_argument, NULL, 'c'},
	{"color-mode", no_argument, NULL, 'C'},
	{"interactive", no_argument, NULL, 'i'},
	{"hide-idle", no_argument, NULL, 'I'},
	{"non-interactive", no_argument, NULL, 'n'},
	{"order-field", required_argument, NULL, 'o'},
	{"remote-mode", no_argument, NULL, 'r'},
	{"set-delay", required_argument, NULL, 's'},
	{"show-tags", no_argument, NULL, 'T'},
	{"version", no_argument, NULL, 'V'},
	{"set-display", required_argument, NULL, 'x'},
	{"show-username", required_argument, NULL, 'z'},
	{"help", no_argument, NULL, '?'},
	{"dbname", required_argument, NULL, 'd'},
	{"host", required_argument, NULL, 'h'},
	{"port", required_argument, NULL, 'p'},
	{"username", required_argument, NULL, 'U'},
	{"password", no_argument, NULL, 'W'},
	{NULL, 0, NULL, 0}
};

/* pointers to display routines */
void		(*d_loadave) (int, double *) = i_loadave;
void		(*d_minibar) (
						  int (*) (char *, int)) = i_minibar;
void		(*d_uptime) (time_t *, time_t *) = i_uptime;
void		(*d_procstates) (int, int *) = i_procstates;
void		(*d_cpustates) (int64_t *) = i_cpustates;
void		(*d_memory) (long *) = i_memory;
void		(*d_swap) (long *) = i_swap;
void		(*d_db) (struct db_info *) = i_db;
void		(*d_io) (struct io_info *) = i_io;
void		(*d_message) () = i_message;
void		(*d_process) (int, char *) = i_process;

/*
 *	usage - print help message with details about commands
 */
static void
usage(const char *progname)
{
	printf("%s monitors a PostgreSQL database cluster.\n\n", progname);
	printf("Usage:\n");
	printf("  %s [OPTION]... [COUNT]\n", progname);
	printf("\nGeneral options:\n");
	printf("  -b, --batch               use batch mode\n");
	printf("  -c, --show-command        display command name of each process\n");
	printf("  -C, --color-mode          turn off color mode\n");
	printf("  -i, --interactive         use interactive mode\n");
	printf("  -I, --hide-idle           hide idle processes\n");
	printf("  -n, --non-interactive     use non-interactive mode\n");
	printf("  -o, --order-field=FIELD   select sort order\n");
	printf("  -r, --remote-mode         activate remote mode\n");
	printf("  -R                        display replication stats\n");
	printf("  -s, --set-delay=SECOND    set delay between screen updates\n");
	printf("  -T, --show-tags           show color tags\n");
	printf("  -V, --version             output version information, then exit\n");
	printf("  -x, --set-display=COUNT   set maximum number of displays\n");
	printf("                            exit once this number is reached\n");
	printf("  -X                        display i/o stats\n");
	printf("  -z, --show-username=NAME  display only processes owned by given\n");
	printf("                            username\n");
	printf("  -?, --help                show this help, then exit\n");
	printf("\nConnection options:\n");
	printf("  -d, --dbname=DBNAME       database to connect to\n");
	printf("  -h, --host=HOSTNAME       database server host or socket directory\n");
	printf("  -p, --port=PORT           database server port\n");
	printf("  -U, --username=USERNAME   user name to connect as\n");
	printf("  -W, --password            force password prompt, and persistent connection\n");
}

RETSIGTYPE
onalrm(int i)					/* SIGALRM handler */

{
	/* this is only used in batch mode to break out of the pause() */
	/* return; */
}

void
do_display(struct pg_top_context *pgtctx)
{
	register int i = 0;
	register int active_procs;

	caddr_t		processes;
	time_t		curr_time;
	static struct ext_decl exts = {NULL, NULL};

	/* get the current stats and processes */
	if (pgtctx->mode_remote == 0)
	{
		get_system_info(&pgtctx->system_info);
		processes = get_process_info(&pgtctx->system_info, &pgtctx->ps,
									 pgtctx->order_index, &pgtctx->conninfo,
									pgtctx->mode);
	}
	else
	{
		get_system_info_r(&pgtctx->system_info, &pgtctx->conninfo);
		processes = get_process_info_r(&pgtctx->system_info, &pgtctx->ps,
									   pgtctx->order_index, &pgtctx->conninfo, pgtctx->mode);
	}

	/* Get database activity information */
	get_database_info(&pgtctx->db_info, &pgtctx->conninfo);

	/* Get database I/O information */
	get_io_info(&pgtctx->io_info);

	/* display the load averages */
	(*d_loadave) (pgtctx->system_info.last_pid, pgtctx->system_info.load_avg);

	/* this method of getting the time SHOULD be fairly portable */
	time(&curr_time);

	/* if we have a minibar extension, use it, otherwise show uptime */
	if (exts.f_minibar != NULL)
	{
		(*d_minibar) (exts.f_minibar);
	}
	else
	{
		(*d_uptime) (&pgtctx->statics.boottime, &curr_time);
	}

	/* display the current time */
	i_timeofday(&curr_time);

	/* display process state breakdown */
	(*d_procstates) (pgtctx->system_info.p_total, pgtctx->system_info.procstates);

	/* display the cpu state percentage breakdown */
	if (pgtctx->dostates)		/* but not the first time */
	{
		(*d_cpustates) (pgtctx->system_info.cpustates);
	}
	else
	{
		/* we'll do it next time */
		if (smart_terminal)
		{
			z_cpustates();
		}
		pgtctx->dostates = Yes;
	}

	/* display memory stats */
	(*d_memory) (pgtctx->system_info.memory);

	/* display database activity */
	(*d_db) (&pgtctx->db_info);

	/* display database I/O */
	(*d_io) (&pgtctx->io_info);

	/* display swap stats */
	(*d_swap) (pgtctx->system_info.swap);

	/* handle message area */
	(*d_message) ();

	/* update the header area */
	pgtctx->d_header(pgtctx->header_text);

	if (pgtctx->topn > 0)
	{
		/* determine number of processes to actually display */

		/*
		 * this number will be the smallest of:  active processes, number user
		 * requested, number current screen accomodates
		 */
		active_procs = pgtctx->system_info.P_ACTIVE;
		if (active_procs > pgtctx->topn)
		{
			active_procs = pgtctx->topn;
		}
		if (active_procs > max_topn)
		{
			active_procs = max_topn;
		}

		/* Now show the top "n" processes or other statistics. */
		switch (pgtctx->mode)
		{
#if defined(__linux__) || defined(__FreeBSD__)
			case MODE_IO_STATS:
				for (i = 0; i < active_procs; i++)
				{
					if (pgtctx->mode_remote == 0)
						(*d_process) (i, format_next_io(processes));
					else
						(*d_process) (i, format_next_io_r(processes));
				}
				break;
#endif /* defined(__linux__) || defined(__FreeBSD__) */
			case MODE_REPLICATION:
				for (i = 0; i < active_procs; i++)
				{
					if (pgtctx->mode_remote == 0)
						(*d_process) (i, format_next_replication(processes));
					else
						(*d_process) (i, format_next_replication_r(processes));
				}
				break;
			case MODE_PROCESSES:
			default:
				for (i = 0; i < active_procs; i++)
				{
					if (pgtctx->mode_remote == 0)
						(*d_process) (i, format_next_process(processes));
					else
						(*d_process) (i, format_next_process_r(processes));
				}
		}
	}
	else
	{
		i = 0;
	}

	/* do end-screen processing */
	u_endscreen(i);

	/* now, flush the output buffer */
	if (fflush(stdout) != 0)
	{
		new_message(MT_standout, " Write error on stdout");
		putchar('\r');
		quit(1);
		/* NOTREACHED */
	}

	/* only do the rest if we have more displays to show */
	if (pgtctx->displays)
	{
		/* switch out for new display on smart terminals */
		if (smart_terminal)
		{
			if (overstrike)
			{
				reset_display(pgtctx);
			}
			else
			{
				d_loadave = u_loadave;
				d_minibar = u_minibar;
				d_uptime = u_uptime;
				d_procstates = u_procstates;
				d_cpustates = u_cpustates;
				d_memory = u_memory;
				d_db = u_db;
				d_io = u_io;
				d_swap = u_swap;
				d_message = u_message;
				pgtctx->d_header = u_header;
				d_process = u_process;
			}
		}

		if (!pgtctx->interactive)
		{
			/* set up alarm */
			(void) signal(SIGALRM, onalrm);
			(void) alarm((unsigned) pgtctx->delay);

			/* wait for the rest of it .... */
			pause();
		}
		else
			process_commands(pgtctx);
	}
}

void
process_arguments(struct pg_top_context *pgtctx, int ac, char **av)
{
	int			i;
	int			option_index;

	while ((i = getopt_long(ac, av, "CDITbcinRrVh:s:d:U:o:Wp:Xx:z:",
							long_options, &option_index)) != EOF)
	{
		switch (i)
		{
#ifdef ENABLE_COLOR
			case 'C':
				pgtctx->color_on = !pgtctx->color_on;
				break;
#endif

			case 'D':
				debug_set(1);
				break;

			case 'V':			/* show version number */
				printf("pg_top %s\n", version_string());
				exit(0);
				break;

			case 'z':			/* display only username's processes */
				strncpy(pgtctx->ps.usename, optarg, NAMEDATALEN);
				break;

			case 'I':			/* show idle processes */
				pgtctx->ps.idle = !pgtctx->ps.idle;
				break;

			case 'T':			/* show color tags */
				pgtctx->show_tags = 1;
				break;

			case 'i':			/* go interactive regardless */
				pgtctx->interactive = Yes;
				break;

			case 'c':
				pgtctx->ps.fullcmd = No;
				break;

			case 'n':			/* batch, or non-interactive */
			case 'b':
				pgtctx->interactive = No;
				break;

			case 'x':			/* number of displays to show */
				if ((i = atoiwi(optarg)) == Invalid || i == 0)
				{
					new_message(MT_standout | MT_delayed,
								" Bad display count (ignored)");
				}
				else
				{
					pgtctx->displays = i;
				}
				break;

			case 's':
				if ((pgtctx->delay = atoi(optarg)) < 0 ||
					(pgtctx->delay == 0 && getuid() != 0))
				{
					new_message(MT_standout | MT_delayed,
								" Bad seconds delay (ignored)");
					pgtctx->delay = Default_DELAY;
				}
				break;

			case 'o':			/* select sort order */
				pgtctx->order_name = optarg;
				break;

			case 'p':			/* database port */
				if ((i = atoiwi(optarg)) == Invalid || i == 0)
				{
					new_message(MT_standout | MT_delayed,
								" Bad port number (ignored)");
				}
				else
				{
					pgtctx->conninfo.values[PG_PORT] = strdup(optarg);
				}
				break;

			case 'W':			/* prompt for database password */
				pgtctx->conninfo.persistent = 1;
				pgtctx->conninfo.values[PG_PASSWORD] =
					simple_prompt("Password: ", 1000, 0);
				break;

			case 'U':			/* database user name */
				pgtctx->conninfo.values[PG_USER] = strdup(optarg);
				break;

			case 'd':			/* database name */
				pgtctx->conninfo.values[PG_DBNAME] = strdup(optarg);
				break;

			case 'h':			/* socket location */
				pgtctx->conninfo.values[PG_HOST] = strdup(optarg);
				break;

			case 'R':			/* replication mode */
				pgtctx->mode = MODE_REPLICATION;
				break;

			case 'r':			/* remote mode */
				pgtctx->mode_remote = 1;
				break;

			case 'X':			/* i/O mode */
				pgtctx->mode = MODE_IO_STATS;
				break;

			default:
				fprintf(stderr, "Try \"%s --help\" for more information.\n",
						progname);
				exit(1);
		}
	}
}

void
process_commands(struct pg_top_context *pgtctx)
{
	int			no_command;
	fd_set		readfds;
	char		ch;

	do
	{
		no_command = No;

		/* set up arguments for select with timeout */
		FD_ZERO(&readfds);
		FD_SET(0, &readfds);	/* for standard input */
		pgtctx->timeout.tv_sec = pgtctx->delay;
		pgtctx->timeout.tv_usec = 0;

		/* wait for either input or the end of the delay period */
		if (select(32, &readfds, (fd_set *) NULL, (fd_set *) NULL,
				   &pgtctx->timeout) > 0)
		{
			/* something to read -- clear the message area first */
			clear_message();

			/* now read it and convert to command strchr */
			/* (use "change" as a temporary to hold strchr) */
			if (read(0, &ch, 1) != 1)
			{
				/* read error: either 0 or -1 */
				new_message(MT_standout, " Read error on stdin");
				putchar('\r');
				quit(1);
				/* NOTREACHED */
			}

			no_command = execute_command(pgtctx, ch);

			/* flush out stuff that may have been written */
			fflush(stdout);
		}
	} while (no_command);
}

/*
 *	reset_display() - reset all the display routine pointers so that entire
 *	screen will get redrawn.
 */

void
reset_display(struct pg_top_context *pgtctx)
{
	d_loadave = i_loadave;
	d_minibar = i_minibar;
	d_uptime = i_uptime;
	d_procstates = i_procstates;
	d_cpustates = i_cpustates;
	d_memory = i_memory;
	d_swap = i_swap;
	d_db = i_db;
	d_io = i_io;
	d_message = i_message;
	pgtctx->d_header = i_header;
	d_process = i_process;
}

/*
 *	signal handlers
 */

void
set_signal(int sig, RETSIGTYPE(*handler) (int))

{
#ifdef HAVE_SIGACTION
	struct sigaction action;

	action.sa_handler = handler;
	action.sa_flags = 0;
	(void) sigaction(sig, &action, NULL);
#else
	(void) signal(sig, handler);
#endif
}

RETSIGTYPE
leave(int i)					/* exit under normal conditions -- INT handler */
{
	end_screen();
	exit(0);
}

RETSIGTYPE
tstop(int i)					/* SIGTSTP handler */
{
#ifdef HAVE_SIGACTION
	sigset_t	set;
#endif

	/* move to the lower left */
	end_screen();
	fflush(stdout);

	/* default the signal handler action */
	set_signal(SIGTSTP, SIG_DFL);

	/* unblock the TSTP signal */
#ifdef HAVE_SIGACTION
	sigemptyset(&set);
	sigaddset(&set, SIGTSTP);
	sigprocmask(SIG_UNBLOCK, &set, NULL);
#endif

#ifdef HAVE_SIGHOLD
	sigrelse(SIGTSTP);
#endif

#ifdef BSD_SIGNALS
	(void) sigsetmask(sigblock(0) & ~(sigmask(SIGTSTP)));
#endif

	/* send ourselves a TSTP to stop the process */
	(void) kill(0, SIGTSTP);

	/* reset the signal handler */
	set_signal(SIGTSTP, tstop);

	/* reinit screen */
	reinit_screen();

	/* jump to appropriate place */
	longjmp(jmp_int, 1);

	/* NOTREACHED */
}

#ifdef SIGWINCH
RETSIGTYPE
winch(int i)					/* SIGWINCH handler */
{
	/* reascertain the screen dimensions */
	get_screensize();

	/* tell display to resize */
	max_topn = display_resize();

#ifndef HAVE_SIGACTION
	/* reset the signal handler */
	set_signal(SIGWINCH, winch);
#endif

	/* jump to appropriate place */
	longjmp(jmp_int, 1);
}
#endif

void
quit(int status)				/* exit under duress */
{
	end_screen();
	exit(status);
	/* NOTREACHED */
}

int
main(int argc, char *argv[])
{
	register int i;
	struct pg_top_context pgtctx;

#ifdef BSD_SIGNALS
	int			old_sigmask;	/* only used for BSD-style signals */
#endif							/* BSD_SIGNALS */
	char	   *uname_field = "USERNAME";
	char	   *env_top;
	char	  **preset_argv;
	int			preset_argc = 0;
	char	  **av;
	int			ac;

#ifndef FD_SET
	/* FD_SET and friends are not present:	fake it */
	typedef int fd_set;

#define FD_ZERO(x)	   (*(x) = 0)
#define FD_SET(f, x)   (*(x) = 1<<f)
#endif

#ifdef HAVE_SIGPROCMASK
	sigset_t	signalset;
#endif

	/* initialize some selection options */
	memset(&pgtctx, 0, sizeof(struct pg_top_context));
#ifdef ENABLE_COLOR
	pgtctx.color_on = 1;
#endif
	pgtctx.d_header = i_header;
	pgtctx.delay = Default_DELAY;
	pgtctx.displays = 0;		/* indicates unspecified */
	pgtctx.dostates = No;
	pgtctx.do_unames = Yes;
	pgtctx.interactive = Maybe;
	pgtctx.mode = MODE_PROCESSES;
	pgtctx.mode_remote = No;
	pgtctx.order_index = -1;
	pgtctx.ps.idle = Yes;
	pgtctx.ps.fullcmd = Yes;
	pgtctx.ps.command = NULL;
	pgtctx.ps.usename[0] = '\0';
	pgtctx.show_tags = No;
	pgtctx.topn = 0;
	pgtctx.conninfo.connection = NULL;
	pgtctx.conninfo.persistent = 0;

	/* Show help or version number if necessary */
	if (argc > 1)
	{
		if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
		{
			usage(progname);
			exit(0);
		}
		if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
		{
			printf("pg_top %s\n", version_string());
			exit(0);
		}
	}

	/* set the buffer for stdout */
#ifdef HAVE_SETVBUF
	setvbuf(stdout, stdoutbuf, _IOFBF, BUFFERSIZE);
#else
#ifdef HAVE_SETBUFFER
	setbuffer(stdout, stdoutbuf, Buffersize);
#endif							/* HAVE_SETBUFFER */
#endif							/* HAVE_SETVBUF */

	/* get our name */
	if (argc > 0)
	{
		if ((myname = strrchr(argv[0], '/')) == 0)
		{
			myname = argv[0];
		}
		else
		{
			myname++;
		}
	}

	/* get preset options from the environment */
	if ((env_top = getenv("PG_TOP")) != NULL)
	{
		av = preset_argv = argparse(env_top, &preset_argc);
		ac = preset_argc;

		/*
		 * set the dummy argument to an explanatory message, in case getopt
		 * encounters a bad argument
		 */
		preset_argv[0] = "while processing environment";
	}

	/* process options */
	do
	{
		/* if we're done doing the presets, then process the real arguments */
		if (preset_argc == 0)
		{
			ac = argc;
			av = argv;

			/* this should keep getopt happy... */
			optind = 1;
		}
		process_arguments(&pgtctx, ac, av);

		/* get count of top processes to display (if any) */
		if (optind < ac && *av[optind])
		{
			if ((i = atoiwi(av[optind])) == Invalid)
			{
				new_message(MT_standout | MT_delayed,
							" Process count not a number (ignored)");
			}
			else
			{
				pgtctx.displays = i;
			}
		}

		/* tricky:	remember old value of preset_argc & set preset_argc = 0 */
		i = preset_argc;
		preset_argc = 0;

		/* repeat only if we really did the preset arguments */
	} while (i != 0);

	/* set constants for username/uid display correctly */
	if (!pgtctx.do_unames)
	{
		uname_field = "   UID  ";
	}

	/*
	 * in order to support forward compatability, we have to ensure that the
	 * entire statics structure is set to a known value before we call
	 * machine_init.  This way fields that a module does not know about will
	 * retain their default values
	 */
	memzero((void *) &pgtctx.statics, sizeof(pgtctx.statics));
	pgtctx.statics.boottime = -1;

#ifdef ENABLE_COLOR
	/* If colour has been turned on read in the settings. */
	env_top = getenv("PG_TOPCOLOURS");
	if (!env_top)
	{
		env_top = getenv("PG_TOPCOLORS");
	}
	/* must do something about error messages */
	color_env_parse(env_top);
#endif

	/* call the platform-specific init */
	if (pgtctx.mode_remote == 0)
		i = machine_init(&pgtctx.statics);
	else
		i = machine_init_r(&pgtctx.statics, &pgtctx.conninfo);

	if (i == -1)
		exit(1);

	/* determine sorting order index, if necessary */
	if (pgtctx.order_name != NULL)
	{
		if (pgtctx.statics.order_names == NULL)
		{
			new_message(MT_standout | MT_delayed,
						" This platform does not support arbitrary ordering");
		}
		else if ((pgtctx.order_index = string_index(pgtctx.order_name,
													pgtctx.statics.order_names)) == -1)
		{
			char	  **pp;

			fprintf(stderr, "%s: '%s' is not a recognized sorting order.\n",
					myname, pgtctx.order_name);
			fprintf(stderr, "\tTry one of these:");
			pp = pgtctx.statics.order_names;
			while (*pp != NULL)
			{
				fprintf(stderr, " %s", *pp++);
			}
			fputc('\n', stderr);
			exit(1);
		}
	}

#ifdef WITH_EXT
	/* initialize extensions */
	init_ext(&exts);
#endif

	/* initialize termcap */
	init_termcap(pgtctx.interactive);

	/* 0 corresponds to machine headers definitions */
	pgtctx.header_options[0][MODE_PROCESSES] = format_header(uname_field);
#if defined(__linux__) || defined(__FreeBSD__)
	pgtctx.header_options[0][MODE_IO_STATS] = fmt_header_io;
#endif /* defined(__linux__) || defined(__FreeBSD__) */
	pgtctx.header_options[0][MODE_REPLICATION] = fmt_header_replication;

	/* 1 corresponds to headers definitions when remotely connecting to pg */
	pgtctx.header_options[1][MODE_PROCESSES] = format_header_r(uname_field);
	pgtctx.header_options[1][MODE_IO_STATS] = fmt_header_io_r;
	pgtctx.header_options[1][MODE_REPLICATION] = fmt_header_replication_r;

	/* get the string to use for the process area header */

	pgtctx.header_text =
		pgtctx.header_options[pgtctx.mode_remote][pgtctx.mode];

#ifdef ENABLE_COLOR
	/* Disable colours on non-smart terminals */
	if (!smart_terminal)
	{
		pgtctx.color_on = 0;
	}
#endif

	/* initialize display interface */
	if ((max_topn = display_init(&pgtctx.statics)) == -1)
	{
		fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
		exit(4);
	}

	/* handle request for color tags */
	if (pgtctx.show_tags)
	{
		color_dump(stdout);
		exit(0);
	}

	/*
	 * Set topn based on the current screensize when starting up if it was not
	 * specified on the command line.
	 */
	if (pgtctx.topn == 0)
	{
		get_screensize();
		pgtctx.topn = display_resize();
	}

	/* print warning if user requested more processes than we can display */
	if (pgtctx.topn > max_topn)
	{
		new_message(MT_standout | MT_delayed,
					" This terminal can only display %d processes.",
					max_topn);
	}

	/* set header display accordingly */
	display_header(pgtctx.topn > 0);

	/* determine interactive state */
	if (pgtctx.interactive == Maybe)
	{
		pgtctx.interactive = smart_terminal;
	}

	/* if # of displays not specified, fill it in */
	if (pgtctx.displays == 0)
	{
		pgtctx.displays = smart_terminal ? Infinity : 1;
	}

	/* hold interrupt signals while setting up the screen and the handlers */
#ifdef HAVE_SIGPROCMASK
	sigemptyset(&signalset);
	sigaddset(&signalset, SIGINT);
	sigaddset(&signalset, SIGQUIT);
	sigaddset(&signalset, SIGTSTP);
#ifdef SIGWINCH
	sigaddset(&signalset, SIGWINCH);
#endif
	sigprocmask(SIG_BLOCK, &signalset, NULL);
#endif

#ifdef HAVE_SIGHOLD
	sighold(SIGINT);
	sighold(SIGQUIT);
	sighold(SIGTSTP);
#ifdef SIGWINCH
	sighold(SIGWINCH);
#endif
#endif

#ifdef BSD_SIGNALS
#ifdef SIGWINCH
	old_sigmask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) |
						   sigmask(SIGTSTP) | sigmask(SIGWINCH));
#else
	old_sigmask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) |
						   sigmask(SIGTSTP));
#endif
#endif

	init_screen();
	(void) set_signal(SIGINT, leave);
	(void) set_signal(SIGQUIT, leave);
	(void) set_signal(SIGTSTP, tstop);
#ifdef SIGWINCH
	(void) set_signal(SIGWINCH, winch);
#endif

	/* setup the jump buffer for stops */
	if (setjmp(jmp_int) != 0)
	{
		/* control ends up here after an interrupt */
		reset_display(&pgtctx);
	}

	/*
	 * Ready to release the signals.  This will also happen (needlessly) after
	 * a longjmp, but that's okay.
	 */
#ifdef HAVE_SIGPROCMASK
	sigprocmask(SIG_UNBLOCK, &signalset, NULL);
#endif

#ifdef HAVE_SIGHOLD
	sigrelse(SIGINT);
	sigrelse(SIGQUIT);
	sigrelse(SIGTSTP);
#ifdef SIGWINCH
	sigrelse(SIGWINCH);
#endif
#endif

#ifdef BSD_SIGNALS
	(void) sigsetmask(old_sigmask);
#endif

	/* some systems require a warmup */
	if (pgtctx.statics.flags.warmup)
	{
		if (pgtctx.mode_remote == 0)
		{
			get_system_info(&pgtctx.system_info);
			(void) get_process_info(&pgtctx.system_info, &pgtctx.ps, -1,
									&pgtctx.conninfo, pgtctx.mode);
		}
		else
		{
			get_system_info_r(&pgtctx.system_info, &pgtctx.conninfo);
			(void) get_process_info_r(&pgtctx.system_info, &pgtctx.ps, -1,
									  &pgtctx.conninfo, -1);
		}

		/* Get database activity information */
		get_database_info(&pgtctx.db_info, &pgtctx.conninfo);

		/* Get database I/O information */
		get_io_info(&pgtctx.io_info);

		pgtctx.timeout.tv_sec = 1;
		pgtctx.timeout.tv_usec = 0;
		select(0, NULL, NULL, NULL, &pgtctx.timeout);

		/* if we've warmed up, then we can show good states too */
		pgtctx.dostates = Yes;
	}

	/*
	 * main loop -- repeat while display count is positive or while it
	 * indicates infinity (by being -1)
	 */

	while ((pgtctx.displays == -1) || (pgtctx.displays-- > 0))
	{
		do_display(&pgtctx);
	}

	quit(0);
	/* NOTREACHED */
	return 0;
}
0707010000003D000081A400000000000000000000000166622DE800000AEA000000000000000000000000000000000000001600000000pg_top-4.1.1/pg_top.h/*
 *	Top - a top users display for Berkeley Unix
 *
 *	General (global) definitions
 *
 *	Copyright (c) 2007-2019, Mark Wong
 */

#ifndef _PG_TOP_H_
#define _PG_TOP_H_

#include "machine.h"
#include "os.h"

/* Log base 2 of 1024 is 10 (2^10 == 1024) */
#define LOG1024		10

/* Special atoi routine returns either a non-negative number or one of: */
#define Infinity	-1
#define Invalid		-2

/* maximum number we can have */
#define Largest		0x7fffffff

struct ext_decl
{
	int			(*f_minibar) (char *, int);
	int			(*f_display) (char *, int);
};

/*
 *	Definitions for things that might vary between installations.
 */

/*
 *	"Table_size" defines the size of the hash tables used to map uid to
 *	username.  Things will work best if the number is a prime number.
 *	We use a number that should be suitable for most installations.
 */
#ifndef Table_size
#define Table_size	8191
#endif

/*
 *	"Nominal_TOPN" is used as the default TOPN when Default_TOPN is Infinity
 *	and the output is a dumb terminal.	If we didn't do this, then
 *	installations who use a default TOPN of Infinity will get every
 *	process in the system when running top on a dumb terminal (or redirected
 *	to a file).  Note that Nominal_TOPN is a default:  it can still be
 *	overridden on the command line, even with the value "infinity".
 */
#ifndef Nominal_TOPN
#define Nominal_TOPN	40
#endif

#ifndef Default_DELAY
#define Default_DELAY	5
#endif

/*
 *	If the local system's getpwnam interface uses random access to retrieve
 *	a record (i.e.: 4.3 systems, Sun "yellow pages"), then defining
 *	RANDOM_PW will take advantage of that fact.  If RANDOM_PW is defined,
 *	then getpwnam is used and the result is cached.  If not, then getpwent
 *	is used to read and cache the password entries sequentially until the
 *	desired one is found.
 *
 *	We initially set RANDOM_PW to something which is controllable by the
 *	Configure script.  Then if its value is 0, we undef it.
 */

#define RANDOM_PW	1
#if RANDOM_PW == 0
#undef RANDOM_PW
#endif

enum pgparams
{
	PG_HOST,
	PG_PORT,
	PG_USER,
	PG_PASSWORD,
	PG_DBNAME
};

struct pg_top_context
{
#ifdef ENABLE_COLOR
	int			color_on;
#endif
	int			delay;
	int			displays;
	void		(*d_header) (char *);
	char		do_unames;
	char		dostates;
	char	   *header_options[2][MODE_TYPES];
	char	   *header_text;
	char		interactive;
	int			mode;
	int			mode_remote;	/* Mode for monitoring a remote database
								 * system. */
	int			order_index;
	char	   *order_name;
	struct db_info db_info;
	struct io_info io_info;
	struct process_select ps;
	char		show_tags;
	struct statics statics;
	struct system_info system_info;
	struct timeval timeout;
	int			topn;
	struct pg_conninfo_ctx conninfo;
};

void		quit(int);
void		reset_display(struct pg_top_context *);

#endif							/* _PG_TOP_H_ */
0707010000003E000081A400000000000000000000000166622DE80000036B000000000000000000000000000000000000001400000000pg_top-4.1.1/port.h/*-------------------------------------------------------------------------
 *
 * port.h
 *	  Header for src/port/ compatibility functions.
 *
 * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * $PostgreSQL: pgsql/src/include/port.h,v 1.106.2.1 2007/01/11 02:40:12 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
#ifndef _PORT_H_
#define _PORT_H_

#include <stdbool.h>

#if defined(WIN32) && !defined(__CYGWIN__)
#define DEVNULL "nul"
/* "con" does not work from the Msys 1.0.10 console (part of MinGW). */
#define DEVTTY	"con"
#else
#define DEVNULL "/dev/null"
#define DEVTTY "/dev/tty"
#endif

/* Portable prompt handling */
extern char *simple_prompt(const char *prompt, int maxlen, bool echo);

#endif							/* _PORT_H_ */
0707010000003F000081A400000000000000000000000166622DE800000273000000000000000000000000000000000000001600000000pg_top-4.1.1/remote.h/*
 * Copyright (c) 2008-2019, Mark Wong
 */

#ifndef _REMOTE_H_
#define _REMOTE_H_

#include "machine.h"

int			machine_init_r(struct statics *, struct pg_conninfo_ctx *);
void		get_system_info_r(struct system_info *, struct pg_conninfo_ctx *);
caddr_t		get_process_info_r(struct system_info *, struct process_select *, int,
							   struct pg_conninfo_ctx *, int);
char	   *format_header_r(char *);
char	   *format_next_io_r(caddr_t);
char	   *format_next_process_r(caddr_t);
char	   *format_next_replication_r(caddr_t);

extern char fmt_header_io_r[];
extern char fmt_header_replication_r[];

#endif							/* _REMOTE_H_ */
07070100000040000081A400000000000000000000000166622DE800002215000000000000000000000000000000000000001600000000pg_top-4.1.1/screen.c/*
 *	Top users/processes display for Unix
 *	Version 3
 *
 *	This program may be freely redistributed,
 *	but this entire comment MUST remain intact.
 *
 *	Copyright (c) 1984, 1989, William LeFebvre, Rice University
 *	Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
 */

/*	This file contains the routines that interface to termcap and stty/gtty.
 *
 *	Paul Vixie, February 1987: converted to use ioctl() instead of stty/gtty.
 *
 *	I put in code to turn on the TOSTOP bit while top was running, but I
 *	didn't really like the results.  If you desire it, turn on the
 *	preprocessor variable "TOStop".   --wnl
 */

#include "os.h"
#include "pg_top.h"

#include <sys/ioctl.h>
#ifdef CBREAK
#include <sgtty.h>
#define SGTTY
#else
#include <termios.h>
#endif /* CBREAK */
#ifndef TAB3
#ifdef OXTABS
#define TAB3 OXTABS
#else
#define TAB3 0
#endif /* OXTABS */
#endif
#include "screen.h"
#include "boolean.h"

extern char *myname;

int			overstrike;
int			screen_length;
int			screen_width;
char		ch_erase;
char		ch_kill;
char		smart_terminal;
char		PC;
char		termcap_buf[1024];
char		string_buffer[1024];
char		home[16];
char		lower_left[15];
char	   *clear_line;
char	   *clear_screen;
char	   *clear_to_end;
char	   *cursor_motion;
char	   *start_standout;
char	   *end_standout;
char	   *terminal_init;
char	   *terminal_end;

#ifdef SGTTY
static struct sgttyb old_settings;
static struct sgttyb new_settings;
#endif
static struct termios old_settings;
static struct termios new_settings;
static char is_a_terminal = No;

#ifdef TOStop
static int	old_lword;
static int	new_lword;
#endif

#define STDIN	0
#define STDOUT	1
#define STDERR	2

/* This has to be defined as a subroutine for tputs (instead of a macro) */

int
putstdout(int ch)
{
	return putchar((unsigned int) ch);
}

void
get_screensize()
{

#ifdef TIOCGWINSZ

	struct winsize ws;

	if (ioctl(1, TIOCGWINSZ, &ws) != -1)
	{
		if (ws.ws_row != 0)
		{
			screen_length = ws.ws_row;
		}
		if (ws.ws_col != 0)
		{
			screen_width = ws.ws_col - 1;
		}
	}
#else
#ifdef TIOCGSIZE

	struct ttysize ts;

	if (ioctl(1, TIOCGSIZE, &ts) != -1)
	{
		if (ts.ts_lines != 0)
		{
			screen_length = ts.ts_lines;
		}
		if (ts.ts_cols != 0)
		{
			screen_width = ts.ts_cols - 1;
		}
	}
#endif							/* TIOCGSIZE */
#endif							/* TIOCGWINSZ */

	char	   *lower_left_motion = "";

	/* get_screensize() can be called from main() without cursor_motion */
	/* having been set, so we protect against that possibility. */
	if (smart_terminal == Yes)
	{
		/* We need to account for the fact that tgoto() might return NULL. */
		lower_left_motion = tgoto(cursor_motion, 0, screen_length - 1);
		if (lower_left_motion == NULL)
		{
			lower_left_motion = "";
		}
	}
	(void) strncpy(lower_left, lower_left_motion, 15);
	lower_left[14] = '\0';
}

void
init_termcap(int interactive)
{
	char	   *bufptr;
	char	   *PCptr;
	char	   *term_name;
	int			status;

	/* set defaults in case we aren't smart */
	screen_width = MAX_COLS;
	screen_length = 0;

	if (!interactive)
	{
		/* pretend we have a dumb terminal */
		smart_terminal = No;
		return;
	}

	/* assume we have a smart terminal until proven otherwise */
	smart_terminal = Yes;

	/* get the terminal name */
	term_name = getenv("TERM");

	/* if there is no TERM, assume it's a dumb terminal */
	/* patch courtesy of Sam Horrocks at telegraph.ics.uci.edu */
	if (term_name == NULL)
	{
		smart_terminal = No;
		return;
	}

	/* now get the termcap entry */
	if ((status = tgetent(termcap_buf, term_name)) != 1)
	{
		if (status == -1)
		{
			fprintf(stderr, "%s: can't open termcap file\n", myname);
		}
		else
		{
			fprintf(stderr, "%s: no termcap entry for a `%s' terminal\n",
					myname, term_name);
		}

		/* pretend it's dumb and proceed */
		smart_terminal = No;
		return;
	}

	/* "hardcopy" immediately indicates a very stupid terminal */
	if (tgetflag("hc"))
	{
		smart_terminal = No;
		return;
	}

	/* set up common terminal capabilities */
	if ((screen_length = tgetnum("li")) <= 0)
	{
		screen_length = smart_terminal = 0;
		return;
	}

	/* screen_width is a little different */
	if ((screen_width = tgetnum("co")) == -1)
	{
		screen_width = 79;
	}
	else
	{
		screen_width -= 1;
	}

	/* terminals that overstrike need special attention */
	overstrike = tgetflag("os");

	/* initialize the pointer into the termcap string buffer */
	bufptr = string_buffer;

	/* get "ce", clear to end */
	if (!overstrike)
	{
		clear_line = tgetstr("ce", &bufptr);
	}

	/* get necessary capabilities */
	if ((clear_screen = tgetstr("cl", &bufptr)) == NULL ||
		(cursor_motion = tgetstr("cm", &bufptr)) == NULL)
	{
		smart_terminal = No;
		return;
	}

	/* get some more sophisticated stuff -- these are optional */
	clear_to_end = tgetstr("cd", &bufptr);
	terminal_init = tgetstr("ti", &bufptr);
	terminal_end = tgetstr("te", &bufptr);
	start_standout = tgetstr("so", &bufptr);
	end_standout = tgetstr("se", &bufptr);

	/* pad character */
	PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0;

	/* set convenience strings */
	/* We need to account for the fact that tgoto() might return NULL. */
	char	   *home_motion = tgoto(cursor_motion, 0, 0);

	if (home_motion == NULL)
	{
		home_motion = "";
	}
	(void) strncpy(home, home_motion, 15);
	home[15] = '\0';
	/* (lower_left is set in get_screensize) */

	/* get the actual screen size with an ioctl, if needed */

	/*
	 * This may change screen_width and screen_length, and it always sets
	 * lower_left.
	 */
	get_screensize();

	/* if stdout is not a terminal, pretend we are a dumb terminal */
#ifdef SGTTY
	if (ioctl(STDOUT, TIOCGETP, &old_settings) == -1)
	{
		smart_terminal = No;
	}
#endif
	if (tcgetattr(STDOUT, &old_settings) == -1)
	{
		smart_terminal = No;
	}
}

void
init_screen()
{
	/* get the old settings for safe keeping */
#ifdef SGTTY
	if (ioctl(STDOUT, TIOCGETP, &old_settings) != -1)
	{
		/* copy the settings so we can modify them */
		new_settings = old_settings;

		/* turn on CBREAK and turn off character echo and tab expansion */
		new_settings.sg_flags |= CBREAK;
		new_settings.sg_flags &= ~(ECHO | XTABS);
		(void) ioctl(STDOUT, TIOCSETP, &new_settings);

		/* remember the erase and kill characters */
		ch_erase = old_settings.sg_erase;
		ch_kill = old_settings.sg_kill;

#ifdef TOStop
		/* get the local mode word */
		(void) ioctl(STDOUT, TIOCLGET, &old_lword);

		/* modify it */
		new_lword = old_lword | LTOSTOP;
		(void) ioctl(STDOUT, TIOCLSET, &new_lword);
#endif
		/* remember that it really is a terminal */
		is_a_terminal = Yes;

		/* send the termcap initialization string */
		putcap(terminal_init);
	}
#endif
	if (tcgetattr(STDOUT, &old_settings) != -1)
	{
		/* copy the settings so we can modify them */
		new_settings = old_settings;

		/* turn off ICANON, character echo and tab expansion */
		new_settings.c_lflag &= ~(ICANON | ECHO);
		new_settings.c_oflag &= ~(TAB3);
		new_settings.c_cc[VMIN] = 1;
		new_settings.c_cc[VTIME] = 0;
		(void) tcsetattr(STDOUT, TCSADRAIN, &new_settings);

		/* remember the erase and kill characters */
		ch_erase = old_settings.c_cc[VERASE];
		ch_kill = old_settings.c_cc[VKILL];

		/* remember that it really is a terminal */
		is_a_terminal = Yes;

		/* send the termcap initialization string */
		putcap(terminal_init);
	}

	if (!is_a_terminal)
	{
		/* not a terminal at all---consider it dumb */
		smart_terminal = No;
	}
}

void
end_screen()
{
	/* move to the lower left, clear the line and send "te" */
	if (smart_terminal)
	{
		putcap(lower_left);
		putcap(clear_line);
		fflush(stdout);
		putcap(terminal_end);
	}

	/* if we have settings to reset, then do so */
	if (is_a_terminal)
	{
#ifdef SGTTY
		(void) ioctl(STDOUT, TIOCSETP, &old_settings);
#ifdef TOStop
		(void) ioctl(STDOUT, TIOCLSET, &old_lword);
#endif
#endif
		(void) tcsetattr(STDOUT, TCSADRAIN, &old_settings);
	}
}

void
reinit_screen()
{
	/* install our settings if it is a terminal */
	if (is_a_terminal)
	{
#ifdef SGTTY
		(void) ioctl(STDOUT, TIOCSETP, &new_settings);
#ifdef TOStop
		(void) ioctl(STDOUT, TIOCLSET, &new_lword);
#endif
#endif
		(void) tcsetattr(STDOUT, TCSADRAIN, &new_settings);
	}

	/* send init string */
	if (smart_terminal)
	{
		putcap(terminal_init);
	}
}

void
standout(char *msg)
{
	if (smart_terminal)
	{
		putcap(start_standout);
		fputs(msg, stdout);
		putcap(end_standout);
	}
	else
	{
		fputs(msg, stdout);
	}
}

void
clear()
{
	if (smart_terminal)
	{
		putcap(clear_screen);
	}
}

int
clear_eol(int len)
{
	if (smart_terminal && !overstrike && len > 0)
	{
		if (clear_line)
		{
			putcap(clear_line);
			return (0);
		}
		else
		{
			while (len-- > 0)
			{
				putchar(' ');
			}
			return (1);
		}
	}
	return (-1);
}

void
go_home()
{
	if (smart_terminal)
	{
		putcap(home);
	}
}
07070100000041000081A400000000000000000000000166622DE8000005A3000000000000000000000000000000000000001600000000pg_top-4.1.1/screen.h/*
 *	top - a top users display for Unix 4.2
 *
 *	This file contains all the definitions necessary to use the hand-written
 *	screen package in "screen.c"
 */

#ifndef _SCREEN_H_
#define _SCREEN_H_

/* includes for termcap */
#ifdef HAVE_TERMCAP_H
#include <termcap.h>
#else
int			tputs(const char *, int, int (*) (int));
char	   *tgoto(const char *, int, int);
int			tgetent(const char *, char *);
int			tgetflag(const char *);
int			tgetnum(const char *);
char	   *tgetstr(const char *, char **);
#endif

#define TCputs(str) tputs(str, 1, putstdout)
#define putcap(str) (void)((str) != NULL ? TCputs(str) : 0)
#define Move_to(x, y)	TCputs(tgoto(cursor_motion, x, y))

extern char ch_erase;			/* set to the user's erase character */
extern char ch_kill;			/* set to the user's kill  character */
extern char smart_terminal;		/* set if the terminal has sufficient termcap
								 * capabilities for normal operation */

/* These are some termcap strings for use outside of "screen.c" */
extern char *cursor_motion;
extern char *clear_line;
extern char *clear_to_end;

/* rows and columns on the screen according to termcap */
extern int	screen_length;
extern int	screen_width;

int			putstdout(int);
void		get_screensize();
void		init_termcap(int interactive);
void		init_screen();
void		end_screen();
void		reinit_screen();
void		standout(char *msg);
void		clear();
int			clear_eol(int len);
void		go_home();

#endif							/* _SCREEN_H_ */
07070100000042000081A400000000000000000000000166622DE80000097F000000000000000000000000000000000000001700000000pg_top-4.1.1/sprompt.c/*-------------------------------------------------------------------------
 *
 * sprompt.c
 *	  simple_prompt() routine
 *
 * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $PostgreSQL: pgsql/src/port/sprompt.c,v 1.18 2006/10/04 00:30:14 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */


/*
 * simple_prompt
 *
 * Generalized function especially intended for reading in usernames and
 * password interactively. Reads from /dev/tty or stdin/stderr.
 *
 * prompt:		The prompt to print
 * maxlen:		How many characters to accept
 * echo:		Set to false if you want to hide what is entered (for passwords)
 *
 * Returns a malloc()'ed string with the input (w/o trailing newline).
 */
#include "c.h"

#include <termios.h>

extern char *simple_prompt(const char *prompt, int maxlen, bool echo);

char *
simple_prompt(const char *prompt, int maxlen, bool echo)
{
	int			length;
	char	   *destination;
	FILE	   *termin,
			   *termout;

	struct termios t_orig,
				t;

	destination = (char *) malloc(maxlen + 1);
	if (!destination)
		return NULL;

	/*
	 * Do not try to collapse these into one "w+" mode file. Doesn't work on
	 * some platforms (eg, HPUX 10.20).
	 */
	termin = fopen(DEVTTY, "r");
	termout = fopen(DEVTTY, "w");
	if (!termin || !termout
		)
	{
		if (termin)
			fclose(termin);
		if (termout)
			fclose(termout);
		termin = stdin;
		termout = stderr;
	}

	if (!echo)
	{
		tcgetattr(fileno(termin), &t);
		t_orig = t;
		t.c_lflag &= ~ECHO;
		tcsetattr(fileno(termin), TCSAFLUSH, &t);
	}

	if (prompt)
	{
		fputs(_(prompt), termout);
		fflush(termout);
	}

	if (fgets(destination, maxlen + 1, termin) == NULL)
		destination[0] = '\0';

	length = strlen(destination);
	if (length > 0 && destination[length - 1] != '\n')
	{
		/* eat rest of the line */
		char		buf[128];
		int			buflen;

		do
		{
			if (fgets(buf, sizeof(buf), termin) == NULL)
				break;
			buflen = strlen(buf);
		} while (buflen > 0 && buf[buflen - 1] != '\n');
	}

	if (length > 0 && destination[length - 1] == '\n')
		/* remove trailing newline */
		destination[length - 1] = '\0';

	if (!echo)
	{
		tcsetattr(fileno(termin), TCSAFLUSH, &t_orig);
		fputs("\n", termout);
		fflush(termout);
	}

	if (termin != stdin)
	{
		fclose(termin);
		fclose(termout);
	}

	return destination;
}
07070100000043000041ED00000000000000000000000266622DE800000000000000000000000000000000000000000000001300000000pg_top-4.1.1/tools07070100000044000081A400000000000000000000000166622DE8000000D5000000000000000000000000000000000000001E00000000pg_top-4.1.1/tools/README.rstThis is a collection of scripts to use Podman for specific tasks:

* build-appimage - Create an AppImage for pg_top.
* build-appimage-container - Build a container image to use for creating an
          AppImage.
07070100000045000081ED00000000000000000000000166622DE800000301000000000000000000000000000000000000002200000000pg_top-4.1.1/tools/build-appimage#!/bin/sh

if ! which podman > /dev/null 2>&1; then
	echo "podman is not in your path"
	exit 1
fi

CONTAINER_DIR=$(realpath "$(dirname "$0")")
CONTAINER_TAG="pg_top-appimage"

# Use the return code from `podman inspect` to determine if the container image
# needs to be created.
if ! podman inspect $CONTAINER_TAG > /dev/null 2>&1; then
	"${CONTAINER_DIR}/build-appimage-container" || exit 1
fi

podman run \
		--rm \
		-v "${CONTAINER_DIR}/..:/usr/local/src/pg_top:rw,Z" \
		-w /usr/local/src/pg_top \
		$CONTAINER_TAG \
		make -f Makefile.cmake appimage

podman run \
        --rm \
        -v "${CONTAINER_DIR}/..:/usr/local/src/pg_top:rw,Z" \
        -w /usr/local/src/pg_top \
        -u root \
        $CONTAINER_TAG \
        chown -R root:root builds/appimage/
07070100000046000081ED00000000000000000000000166622DE8000001A2000000000000000000000000000000000000002C00000000pg_top-4.1.1/tools/build-appimage-container#!/bin/sh

if ! which podman > /dev/null 2>&1; then
	echo "podman is not in your path"
	exit 1
fi

CONTAINER_DIR=$(realpath "$(dirname "$0")")
CONTAINER_TAG="pg_top-appimage"

ARCH="$(uname -m)"

if [ "${ARCH}" = "arm64" ]; then
	ARCH="aarch64"
fi

cd "$CONTAINER_DIR/.." && \
		podman build \
				--isolation=chroot \
				--build-arg ARCH="${ARCH}" \
				-t "${CONTAINER_TAG}" \
				-f Containerfile.appimage \
				.
07070100000047000081A400000000000000000000000166622DE800003470000000000000000000000000000000000000001500000000pg_top-4.1.1/utils.c/*
 *	Top users/processes display for Unix
 *	Version 3
 *
 *	This program may be freely redistributed,
 *	but this entire comment MUST remain intact.
 *
 *	Copyright (c) 1984, 1989, William LeFebvre, Rice University
 *	Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
 */

/*
 *	This file contains various handy utilities used by top.
 */

#include "os.h"
#include <ctype.h>
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#else
#undef DEBUG
#endif
#include "pg_top.h"
#include "utils.h"

static int
alldigits(char *s)
{
	int			ch;

	while ((ch = *s++) != '\0')
	{
		if (!isdigit(ch))
		{
			return 0;
		}
	}
	return 1;
}

int
atoiwi(char *str)
{
	register int len;

	len = strlen(str);
	if (len != 0)
	{
		if (strncmp(str, "infinity", len) == 0 ||
			strncmp(str, "all", len) == 0 ||
			strncmp(str, "maximum", len) == 0)
		{
			return (Infinity);
		}
		else if (alldigits(str))
		{
			return (atoi(str));
		}
		else
		{
			return (Invalid);
		}
	}
	return (0);
}

/*
 *	itoa - convert integer (decimal) to ascii string for positive numbers
 *		   only (we don't bother with negative numbers since we know we
 *	   don't use them).
 */

 /*
  * How do we know that 16 will suffice? Because the biggest number that we
  * will ever convert will be 2^32-1, which is 10 digits.
  */

char *
itoa(int val)
{
	register char *ptr;
	static char buffer[16];		/* result is built here */

	/*
	 * 16 is sufficient since the largest number we will ever convert will be
	 * 2^32-1, which is 10 digits.
	 */

	ptr = buffer + sizeof(buffer);
	*--ptr = '\0';
	if (val == 0)
	{
		*--ptr = '0';
	}
	else
		while (val != 0)
		{
			*--ptr = (val % 10) + '0';
			val /= 10;
		}
	return (ptr);
}

/*
 *	itoa7(val) - like itoa, except the number is right justified in a 7
 *	character field.  This code is a duplication of itoa instead of
 *	a front end to a more general routine for efficiency.
 */

char *
itoa7(uid_t val)
{
	register char *ptr;
	static char buffer[16];		/* result is built here */

	/*
	 * 16 is sufficient since the largest number we will ever convert will be
	 * 2^32-1, which is 10 digits.
	 */

	ptr = buffer + sizeof(buffer);
	*--ptr = '\0';
	if (val == 0)
	{
		*--ptr = '0';
	}
	else
		while (val != 0)
		{
			*--ptr = (val % 10) + '0';
			val /= 10;
		}
	while (ptr > buffer + sizeof(buffer) - 7)
	{
		*--ptr = ' ';
	}
	return (ptr);
}

/*
 *	digits(val) - return number of decimal digits in val.  Only works for
 *	positive numbers.  If val < 0 then digits(val) == 0, but
 *		digits(0) == 1.
 */

int
digits(int val)
{
	register int cnt = 0;

	if (val == 0)
	{
		return 1;
	}
	while (val > 0)
	{
		cnt++;
		val /= 10;
	}
	return (cnt);
}

/*
 *	printable(char *str) - make the string pointed to by "str" into one that is
 *	printable (i.e.: all ascii), by converting all non-printable
 *	characters into '?'.  Replacements are done in place and a pointer
 *	to the original buffer is returned.
 */

char *
printable(char *str)
{
	register char *ptr;
	register char ch;

	ptr = str;
	while ((ch = *ptr) != '\0')
	{
		if (!isprint(ch))
		{
			*ptr = '?';
		}
		ptr++;
	}
	return (str);
}

/*
 *	strecpy(to, from) - copy string "from" into "to" and return a pointer
 *	to the END of the string "to".
 */

char *
strecpy(char *to, char *from)
{
	while ((*to++ = *from++) != '\0');
	return (--to);
}

/*
 * char *
 * homogenize(char *str)
 *
 * Remove unwanted characters from "str" and make everything lower case.
 * Newly allocated string is returned: the original is not altered.
 */

char *
homogenize(char *str)
{
	char	   *ans;
	char	   *fr;
	char	   *to;
	int			ch;

	to = fr = ans = strdup(str);
	while ((ch = *fr++) != '\0')
	{
		if (isalnum(ch))
		{
			*to++ = tolower(ch);
		}
	}

	*to = '\0';
	return ans;
}

/*
 * string_index(string, array) - find string in array and return index
 */

int
string_index(char *string, char **array)
{
	register int i = 0;

	while (*array != NULL)
	{
		if (strcmp(string, *array) == 0)
		{
			return (i);
		}
		array++;
		i++;
	}
	return (-1);
}

/*
 * char *string_list(char **strings)
 *
 * Create a comma-separated list of the strings in the NULL-terminated
 * "strings".  Returned string is malloc-ed and should be freed when the
 * caller is done.	Note that this is not an efficient function.
 */

char *
string_list(char **strings)
{
	int			cnt = 0;
	char	  **pp;
	char	   *p;
	char	   *result = NULL;
	char	   *resp = NULL;

	pp = strings;
	while ((p = *pp++) != NULL)
	{
		cnt += strlen(p) + 2;
	}

	if (cnt > 0)
	{
		resp = result = (char *) malloc(cnt);
		pp = strings;
		while ((p = *pp++) != NULL)
		{
			resp = strecpy(resp, p);
			if (*pp != NULL)
			{
				resp = strecpy(resp, ", ");
			}
		}
	}

	return result;
}

/*
 * argparse(line, cntp) - parse arguments in string "line", separating them
 *	out into an argv-like array, and setting *cntp to the number of
 *	arguments encountered.	This is a simple parser that doesn't understand
 *	squat about quotes.
 */

char	  **
argparse(char *line, int *cntp)
{
	register char *from;
	register char *to;
	register int cnt;
	register int ch;
	int			length;
	int			lastch;
	register char **argv;
	char	  **argarray;
	char	   *args;

	/*
	 * unfortunately, the only real way to do this is to go thru the input
	 * string twice.
	 */

	/* step thru the string counting the white space sections */
	from = line;
	lastch = cnt = length = 0;
	while ((ch = *from++) != '\0')
	{
		length++;
		if (ch == ' ' && lastch != ' ')
		{
			cnt++;
		}
		lastch = ch;
	}

	/*
	 * add three to the count:	one for the initial "dummy" argument, one for
	 * the last argument and one for NULL
	 */
	cnt += 3;

	/* allocate a char * array to hold the pointers */
	argarray = (char **) malloc(cnt * sizeof(char *));

	/* allocate another array to hold the strings themselves */
	args = (char *) malloc(length + 2);

	/* initialization for main loop */
	from = line;
	to = args;
	argv = argarray;
	lastch = '\0';

	/* create a dummy argument to keep getopt happy */
	*argv++ = to;
	*to++ = '\0';
	cnt = 2;

	/* now build argv while copying characters */
	*argv++ = to;
	while ((ch = *from++) != '\0')
	{
		if (ch != ' ')
		{
			if (lastch == ' ')
			{
				*to++ = '\0';
				*argv++ = to;
				cnt++;
			}
			*to++ = ch;
		}
		lastch = ch;
	}
	*to++ = '\0';

	/* set cntp and return the allocated array */
	*cntp = cnt;
	return (argarray);
}

/*
 *	percentages(cnt, out, new, old, diffs) - calculate percentage change
 *	between array "old" and "new", putting the percentages i "out".
 *	"cnt" is size of each array and "diffs" is used for scratch space.
 *	The array "old" is updated on each call.
 *	The routine assumes modulo arithmetic.	This function is especially
 *	useful on BSD mchines for calculating cpu state percentages.
 */

long
percentages(int cnt, int64_t * out, int64_t * new, int64_t * old,
		int64_t * diffs)
{
	register int i;
	register int64_t change;
	register int64_t total_change;
	register int64_t * dp;
	int64_t		half_total;

	/* initialization */
	total_change = 0;
	dp = diffs;

	/* calculate changes for each state and the overall change */
	for (i = 0; i < cnt; i++)
	{
		if ((change = *new - *old) < 0)
		{
			/* this only happens when the counter wraps */
			change = (int64_t) ((int64_t) * new - (int64_t) * old);
		}
		total_change += (*dp++ = change);
		*old++ = *new++;
	}

	/* avoid divide by zero potential */
	if (total_change == 0)
	{
		total_change = 1;
	}

	/* calculate percentages based on overall change, rounding up */
	half_total = total_change / 2l;
	for (i = 0; i < cnt; i++)
	{
		*out++ = (int64_t) ((*diffs++ * 1000 + half_total) / total_change);
	}

	/* return the total in case the caller wants to use it */
	return (total_change);
}

/*
 * errmsg(errnum) - return an error message string appropriate to the
 *			 error number "errnum".  This is a substitute for the System V
 *			 function "strerror".  There appears to be no reliable way to
 *			 determine if "strerror" exists at compile time, so I make do
 *			 by providing something of similar functionality.  For those
 *			 systems that have strerror and NOT errlist, define
 *			 -DHAVE_STRERROR in the module file and this function will
 *			 use strerror.
 */

/* externs referenced by errmsg */

#ifndef HAVE_STRERROR
#if !HAVE_DECL_SYS_ERRLIST
extern char *sys_errlist[];
#endif

extern int	sys_nerr;
#endif

char *
errmsg(int errnum)
{
#ifdef HAVE_STRERROR
	char	   *msg = strerror(errnum);

	if (msg != NULL)
	{
		return msg;
	}
#else
	if (errnum > 0 && errnum < sys_nerr)
	{
		return ((char *) (sys_errlist[errnum]));
	}
#endif
	return ("No error");
}

/* format_percent(v) - format a double as a percentage in a manner that
 *		does not exceed 5 characters (excluding any trailing
 *		percent sign).	Since it is possible for the value
 *		to exceed 100%, we format such values with no fractional
 *		component to fit within the 5 characters.
 */

char *
format_percent(double v)
{
	static char result[10];

	/* enumerate the possibilities */
	if (v < 0 || v >= 100000.)
	{
		/* we dont want to try extreme values */
		strcpy(result, "  ???");
	}
	else if (v > 99.99)
	{
		sprintf(result, "%5.0f", v);
	}
	else
	{
		sprintf(result, "%5.2f", v);
	}

	return result;
}

/* format_time(seconds) - format number of seconds into a suitable
 *		display that will fit within 6 characters.	Note that this
 *		routine builds its string in a static area.  If it needs
 *		to be called more than once without overwriting previous data,
 *		then we will need to adopt a technique similar to the
 *		one used for format_k.
 */

/* Explanation:
   We want to keep the output within 6 characters.	For low values we use
   the format mm:ss.  For values that exceed 999:59, we switch to a format
   that displays hours and fractions:  hhh.tH.	For values that exceed
   999.9, we use hhhh.t and drop the "H" designator.  For values that
   exceed 9999.9, we use "???".
 */

char *
format_time(long seconds)
{
	static char result[10];

	/* sanity protection */
	if (seconds < 0 || seconds > (99999l * 360l))
	{
		strcpy(result, "   ???");
	}
	else if (seconds >= (1000l * 60l))
	{
		/* alternate (slow) method displaying hours and tenths */
		sprintf(result, "%5.1fH", (double) seconds / (double) (60l * 60l));

		/*
		 * It is possible that the sprintf took more than 6 characters. If so,
		 * then the "H" appears as result[6].  If not, then there is a \0 in
		 * result[6].  Either way, it is safe to step on.
		 */
		result[6] = '\0';
	}
	else
	{
		/* standard method produces MMM:SS */
		/* we avoid printf as must as possible to make this quick */
		sprintf(result, "%3ld:%02ld", seconds / 60l, seconds % 60l);
	}
	return (result);
}

#define NUM_STRINGS 8

/*
 * format_b(amt) - format a byte memory value, returning a string
 *		suitable for display.  Returns a pointer to a static
 *		area that changes each call.  "amt" is converted to a
 *		string with a trailing "B".  If "amt" is 10000 or greater,
 *		then it is formatted as megabytes (rounded) with a
 *		trailing "K".  And so on...
 */

char *
format_b(long long amt)
{
	static char retarray[NUM_STRINGS][32];
	static int	index = 0;
	register char *ret;
	register char tag = 'B';

	ret = retarray[index];
	index = (index + 1) % NUM_STRINGS;

	if (amt >= 10000)
	{
		amt = (amt + 512) / 1024;
		tag = 'K';
		if (amt >= 10000)
		{
			amt = (amt + 512) / 1024;
			tag = 'B';
			if (amt >= 10000)
			{
				amt = (amt + 512) / 1024;
				tag = 'G';
			}
		}
	}

	snprintf(ret, sizeof(retarray[index]) - 1, "%lld%c", amt, tag);

	return (ret);
}

/*
 * format_k(amt) - format a kilobyte memory value, returning a string
 *		suitable for display.  Returns a pointer to a static
 *		area that changes each call.  "amt" is converted to a
 *		string with a trailing "K".  If "amt" is 10000 or greater,
 *		then it is formatted as megabytes (rounded) with a
 *		trailing "M".
 */

/*
 * Compromise time.  We need to return a string, but we don't want the
 * caller to have to worry about freeing a dynamically allocated string.
 * Unfortunately, we can't just return a pointer to a static area as one
 * of the common uses of this function is in a large call to sprintf where
 * it might get invoked several times.	Our compromise is to maintain an
 * array of strings and cycle thru them with each invocation.  We make the
 * array large enough to handle the above mentioned case.  The constant
 * NUM_STRINGS defines the number of strings in this array:  we can tolerate
 * up to NUM_STRINGS calls before we start overwriting old information.
 * Keeping NUM_STRINGS a power of two will allow an intelligent optimizer
 * to convert the modulo operation into something quicker.	What a hack!
 */

char *
format_k(long amt)
{
	static char retarray[NUM_STRINGS][32];
	static int	index = 0;
	register char *ret;
	register char tag = 'K';

	ret = retarray[index];
	index = (index + 1) % NUM_STRINGS;

	if (amt >= 10000)
	{
		amt = (amt + 512) / 1024;
		tag = 'M';
		if (amt >= 10000)
		{
			amt = (amt + 512) / 1024;
			tag = 'G';
		}
	}

	snprintf(ret, sizeof(retarray[index]) - 1, "%ld%c", amt, tag);

	return (ret);
}

static int	debug_on = 0;

#ifdef DEBUG
FILE	   *debugfile;
#endif

void
debug_set(int i)
{
	debug_on = i;
#ifdef DEBUG
	debugfile = fopen("/tmp/top.debug", "w");
#endif
}

#ifdef DEBUG
void
xdprintf(char *fmt,...)
{
	va_list		argp;

	va_start(argp, fmt);

	if (debug_on)
	{
		vfprintf(debugfile, fmt, argp);
		fflush(stdout);
	}

	va_end(argp);
}

#endif
07070100000048000081A400000000000000000000000166622DE800000402000000000000000000000000000000000000001500000000pg_top-4.1.1/utils.h/*
 *	Top users/processes display for Unix
 *	Version 3
 *
 *	This program may be freely redistributed,
 *	but this entire comment MUST remain intact.
 *
 *	Copyright (c) 1984, 1989, William LeFebvre, Rice University
 *	Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
 */

#ifndef _UTILS_H_
#define _UTILS_H_

/* prototypes for functions found in utils.c */

int			atoiwi(char *);
char	   *itoa(int);
char	   *itoa7(uid_t);
int			digits(int);
char	   *printable(char *);
char	   *strecpy(char *, char *);
char	   *homogenize(char *);
int			string_index(char *, char **);
char	  **argparse(char *, int *);
long		percentages(int, int64_t *, int64_t *, int64_t *, int64_t *);
char	   *errmsg(int);
char	   *format_percent(double);
char	   *format_time(long);
char	   *format_b(long long);
char	   *format_k(long);
char	   *string_list(char **);
void		debug_set(int);

#ifdef DEBUG
#define dprintf xdprintf
void		xdprintf(char *fmt,...);
#else
#define dprintf if (0)
#endif

#endif							/* _UTILS_H_ */
07070100000049000081A400000000000000000000000166622DE80000018B000000000000000000000000000000000000001700000000pg_top-4.1.1/version.c/*
 *	Top users/processes display for Unix
 *	Version 3
 *
 *	This program may be freely redistributed,
 *	but this entire comment MUST remain intact.
 *
 *	Copyright (c) 1984, 1989, William LeFebvre, Rice University
 *	Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
 */

#include "config.h"
#include "pg_top.h"

char *
version_string()
{
	return (PACKAGE_VERSION);
}
0707010000004A000081A400000000000000000000000166622DE80000018F000000000000000000000000000000000000001700000000pg_top-4.1.1/version.h/*
 *	Top users/processes display for Unix
 *	Version 3
 *
 *	This program may be freely redistributed,
 *	but this entire comment MUST remain intact.
 *
 *	Copyright (c) 1984, 1989, William LeFebvre, Rice University
 *	Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
 */

#ifndef _VERSION_H_
#define _VERSION_H_

char	   *version_string();

#endif							/* _VERSION_H_ */
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!1005 blocks
openSUSE Build Service is sponsored by