/*
    yamm, Yet Another Micro Monitor
    renice.c
    Copyright (C) 1992  Andrea Marangoni
    Copyright (C) 1994, 1995  Riccardo Facchetti

    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 2 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, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#if !defined(linux)
/*
 * This is the HP way to renice a process
 * Linux have the syscalls get/setpriority
 * and it is much less time-consuming make use of the library functions :)
 *
 * - Riccardo -
 */

#include <stdio.h>
#include <unistd.h>
#include <sys/proc.h>
#include <errno.h>
#include <sys/rtprio.h>
#include <fcntl.h>
#include <nlist.h>
#include <sys/pstat.h>

/*
 * Compile with  this flags if you want to debug the HP-UX renice code
 */
#undef RENICE_DEBUG

#include "yamm.h"

#define strerror(e) \
	(((e) >= 0 && (e) < sys_nerr) ? sys_errlist[(e)] : "Unknown Error")

#define SZ(x) (( size_t )sizeof(x))

/*
 * If don't work try to substitute "nproc" = "_nproc", "proc" = "_proc"
 * I think in HP9000s300 but I don't have the possibility to try. 
 */

static struct nlist nl[] = {
	{ "nproc" },
	{ "proc" },
	{ NULL }
};

static struct proc *proc;
static int   nproc;
extern int sys_nerr;
extern char *sys_errlist[];


#define HPKERNEL     "/hp-ux"
#define NEW_RTPRIO 20
#if !defined(NO_CURSES)
#define ERROR(x)   {curses_error((x));}
#else
#define ERROR(x)   {fprintf(stderr,x);}
#endif /* !NO_CURSES */

void renice ( int pid, int new_nice )
{
	int count, old_prio, kfd;   /* Kernel file descriptor */
	off_t offset;
	char nnice = ( char )new_nice;
	struct proc p;
	char err [ 80 ];

	if ( new_nice < 0 || new_nice > 39 ) {
		sprintf ( err, "Nice must be between 0 and 39." );
		ERROR ( err );
		return;
	}

	if ( ( kfd = kinit () ) == -1 )
		return;

/*
 * RTPRIO:
 * I don't known why but my sysadmin use this. Probably problem for
 * working on kmem.
 */
#if !defined(RENICE_DEBUG)

	if ( ( old_prio = rtprio ( ( pid_t )0 , NEW_RTPRIO ) )== -1 ) {

		sprintf ( err, "Sorry Rtprio problem, can't set to %d (%s).",
			NEW_RTPRIO, strerror(errno));
		ERROR ( err );

		( void ) close ( kfd );
		return;
	}
#endif /* !RENICE_DEBUG */

	for ( offset = ( off_t )proc, count = nproc; count > 0;
		--count, offset += ( off_t )sizeof( struct proc )) {

		if ( m_read ( kfd, offset, ( char *)&p, SZ( struct proc ) ) == -1 )
			break;

		if ( p.p_stat == 0 ||  p.p_pid != ( pid_t )pid )
			continue;

#if defined(RENICE_DEBUG)
#if !defined(NO_CURSES)
#define PF (void)printw
		clear_screen();
#else
#define PF (void)printf
#endif /* !NO_CURSES */

		PF ( "\n\nDEBUGGING SCREEN OF RENICE.\n\n" );
		PF ( "Pid  %d.\n", p.p_pid );
		PF ( "Uid  %d.\n", p.p_uid );
		PF ( "Suid %d.\n", p.p_suid );
		PF ( "Priority      %d.\n", p.p_pri );
		PF ( "RTPriority    %d.\n", p.p_rtpri );
		PF ( "User priority %d.\n", p.p_usrpri );
		PF ( "Address %lu.\n", ( u_long )offset );
		PF ( "Proc    %ld.\n", proc);
		PF ( "Process %d Old nice %d New Nice %d\n",
			pid, p.p_nice, nnice);

#if !defined(NO_CURSES)
		( void )curses_get_string ( "Type <RETURN> to exit." );
#endif /* !NO_CURSES */
#else
		( void )m_write ( kfd, offset +
			( off_t )( ( u_long )&p.p_nice - ( u_long )&p ),
			&nnice, SZ(nnice));
		
#endif /* RENICE_DEBUG */
		break;
	}

	( void ) close ( kfd );

#if !defined(RENICE_DEBUG)
	if ( rtprio ( ( pid_t )0 , old_prio ) == -1 ) {
		sprintf ( err, "Sorry Rtprio problem, can't set to %d (%s).",
			old_prio, strerror(errno));
		ERROR ( err );
		return;
	}
#endif /* !RENICE_DEBUG */

	if ( count == 0 ) {
		sprintf ( err, "Process Not Found (%d).", pid );
		ERROR ( err );
	}
}

static int kinit(void)
{
	int kfd = -1;
	char err[ 80 ];

	if ( ( kfd = open ( "/dev/kmem",
#if defined(RENICE_DEBUG)
	O_RDONLY
#else
	O_RDWR
#endif /* RENICE_DEBUG */
	)) == -1) {
		sprintf ( err, "Cannot open \"/dev/kmem\" (%s).",
			strerror(errno));
		ERROR ( err );
		return ( -1 );
	}

	if (nlist(HPKERNEL, nl) == -1) {
		sprintf ( err, "Cannot nlist (%s).",
			strerror(errno));
		ERROR ( err );
		return ( -1 );
	}

	if ( nl[ 0 ].n_type == 0 ) {
		ERROR ( "Cannot get value for \"nlist 0\"." );
		return ( -1 );
	}

	if ( nl[ 1 ].n_type == 0 ) {
		ERROR ( "Cannot get value for \"nlist 1\"." );
		return ( -1 );
	}

	if ( m_read ( kfd, ( off_t )(nl[0].n_value),
		( char *)&nproc, SZ( nproc ) ) == -1 ) {
		( void ) close ( kfd );
		return ( -1 );
	}

	if ( m_read ( kfd, ( off_t )(nl[1].n_value),
	( char *)&proc, SZ( proc ) ) == -1 ) {
		( void ) close ( kfd );
		return ( -1 );
	}

	return ( kfd );
}

static int m_read ( int fd, off_t offset, char *buf, size_t size )
{
	char err [ 80 ];

	if ( lseek ( fd, offset, SEEK_SET ) != offset ) {
		sprintf ( err, "Seek Failed (%s).", strerror(errno));
		ERROR ( err );
		return ( -1 );
	}

	if ( read ( fd, buf, size ) != size ) {
		sprintf ( err, "Read Failed (%s).", strerror(errno));
		ERROR ( err );
		return ( -1 );
	}

	return ( ( int )size );
}

#if !defined(RENICE_DEBUG)
static int m_write ( int fd, off_t offset, char *buf, size_t size )
{
	char err [ 80 ];

	if ( lseek ( fd, offset, SEEK_SET ) != offset ) {
		sprintf ( err, "Seek Failed (%s).", strerror(errno));
		ERROR ( err );
		return ( -1 );
	}

	if ( write ( fd, buf, size ) != size ) {
		sprintf ( err, "Read Failed (%s).", strerror(errno));
		ERROR ( err );
		return ( -1 );
	}

	return ( ( int )size );
}
#endif /* !RENICE_DEBUG */

#else
/*
 * I think it is a good idea enhance the
 * renice function to renice the process group or all the user processes.
 * Linux can do it.
 *
 * - Riccardo -
 */
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/resource.h>

#include "yamm.h"

#if !defined(NO_CURSES)
#define ERROR(x)   {curses_error((x));}
#else
#define ERROR(x)   {fprintf(stderr,x);}
#endif /* !NO_CURSES */

void renice ( int which, int id, int new_nice )
{
	if ( new_nice > 14 ) {
		ERROR ( "Nice must be minor of 14, and can be negative of course." );
		return;
	}

	if ( setpriority(which, id, new_nice) == -1 )
		ERROR( strerror( errno ) );
}

#endif /* !linux */
