/*****************************************************************************
* 
* pst3
* 
* License: GPL
* Copyright (c) 2008 Nagios Plugin Development Team
* 
* Description:
* 
* This file contains the pst3 executable. This is a replacement ps command
* for Solaris to get output which provides a long argument listing, which 
* is not possible with the standard ps command (due to truncation). /usr/ucb/ps
* also has issues where some fields run into each other.
* 
* This executable works by reading the kernel memory structures, so needs
* to be executed as root
*
* Originally written by R.W.Ingraham
* 
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* 
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see .
* 
*****************************************************************************/
#define _KMEMUSER	1
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
/*
 *	Constants
 */
#define PROC_DIR	"/proc"
#define MAX_PATH	1024
#define OK 0
#define FAIL NULL
/*
 *	Structures
 */
/*
 *	Globals
 */
static char *        szProg;
static kvm_t *       kd;
static struct proc * pProc;
static struct user * pUser;
static char **       myArgv;
/*
 *	Prototypes
 */
static int output_info(struct proc *proc_kvm, psinfo_t procinfo,char **proc_argv);
static psinfo_t get_procinfo(struct proc *proc);
static int HandleProc(struct proc *proc);
/*----------------------------------------------------------------------------*/
int main (int argc, char **argv)
{
	DIR *pDir;
	struct dirent *pDent;
	int retcode = 0;
	struct proc *proc;
	struct pid pid;
	/* Set our program name global */
	if ((szProg = strrchr(argv[0], '/')) != NULL)
		szProg++;
	else
		szProg = argv[0];
	/* Make sure that our euid is root */
	if (geteuid() != 0)
	{
		fprintf(stderr, "%s: This program can only be run by the root user!\n", szProg);
		exit(1);
	}
	/* Get a handle to the running kernel image */
	if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, argv[0])) == NULL)
	{
		fprintf(stderr, "%s: Failed to open kernel memory: %s\n", szProg, strerror(errno));
		exit(2);
	}
	/* reset to first proc in list */
	if(kvm_setproc(kd) == -1) {
                perror("kvm_setproc");
                exit(2);
        }
	/* Display column headings */
	printf("%c %5s %5s %5s %6s %6s %4s %s %s\n",
                'S',
                "UID",
                "PID",
                "PPID",
                "VSZ",
                "RSS",
                "%CPU",
                "COMMAND",
                "ARGS"
        );
	/* Zip through all of the process entries */
	while((proc = kvm_nextproc(kd)) != 0) {
		HandleProc(proc);
	}
	/* Close the handle to the running kernel image */
	kvm_close(kd);
	return retcode;
}
/*----------------------------------------------------------------------------*/
static int HandleProc(struct proc *proc)
{
	struct pid pid;
	struct user *user;
	psinfo_t procinfo;
	char **proc_argv = 0;
	if(kvm_kread(kd, (unsigned long) proc->p_pidp, (char *) &pid, sizeof pid) == -1) {
		perror("kvm_read error");
		exit(2);
	}
	proc->p_pidp = &pid;
	user = kvm_getu(kd, proc);
	if(kvm_getcmd(kd, proc, user, &proc_argv, NULL) == -1) {
		return FAIL;
	}
	procinfo = get_procinfo(proc);
	return output_info(proc, procinfo, proc_argv);
}
static psinfo_t get_procinfo(struct proc *proc)
{
	char procpath[MAX_PATH];
	psinfo_t procinfo;
	int fd, len;
	sprintf(procpath, "/proc/%d/psinfo", proc->p_pidp->pid_id);
	if ((fd = open(procpath, O_RDONLY)) >= 0)
	{
		if ((len = read(fd, &procinfo, sizeof(procinfo))) != sizeof(procinfo))
		{
			fprintf(stderr,"%s: Read error of psingo structure (%d)\n", procpath, len);
			exit(2);
		}
		close(fd);
	}
	return procinfo;
}
static int output_info(struct proc *proc_kvm, psinfo_t procinfo, char **proc_argv)
{
	char *procname;
	int i;
	if((procname = strrchr(proc_argv[0], '/')) != NULL)
		procname++;
	else
		procname = proc_argv[0];
	printf("%c %5d %5d %5d %6lu %6lu %4.1f %s ",
		procinfo.pr_lwp.pr_sname,
		(int)(procinfo.pr_euid),
		(int)proc_kvm->p_pidp->pid_id,
		(int)proc_kvm->p_ppid,
		(unsigned long)(procinfo.pr_size),
		(unsigned long)(procinfo.pr_rssize),
		((float)(procinfo.pr_pctcpu) / 0x8000 * 100.0),
		procname
	);
	for(i=0;proc_argv[i];i++) {
		printf(" %s", proc_argv[i]);
	}
	printf("\n");
	return OK;
}