/*
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. You use
this program at your own risk. The author disclaims responsibility for
any damages that might result from the use of this program, even
if they result from negligence on the part of the author.

Also, please don't use this program to steal software. The intended
use is for emergency situations where an application has to be moved from
one computer to another (e.g. in the event of a hardware malfunction)
and licence keys cannot be obtained quickly from the vendor. Many
vendors will not supply licence keys outsethostide of business hours.

before you install this modify /etc/name_to_sysnum to bind sethostid to 
an _unused_ system call number

As far as I can determine the details of how to load system calls in
Solaris 2.3 are undocumented. I've done this by poking around in the
header files in sys and the man pages, and extrapolating from what 
I know about how loadable device drivers work and how the procedure 
works in Solaris 1.1

Mark Henderson <mch@squirrel.com>
14 Aug 1994
*/

#include <sys/param.h>
#include <sys/syscall.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/modctl.h>
#include <sys/cred.h>
#include <sys/systeminfo.h>

static char serial[16];
char xyzzy_rbuf[16];

int sethostid();

struct sysent sethostid_sysent = {1,0,sethostid,0};

extern struct mod_ops mod_syscallops;

static struct modlsys modlsys = {
&mod_syscallops,
"simple sethostid v1.1", 
&sethostid_sysent,
};

#ifdef PRE_SOLARIS_2_5
extern systeminfo();
#else
extern longlong_t systeminfo();
#endif

static struct modlinkage modlinkage = {
    MODREV_1, /* magic number */
    &modlsys, 
    0
};

#ifdef PRE_SOLARIS_2_5
struct as2 {int command; char *buf; long count;};
int new_systeminfo(struct as2 *uap, rval_t *foo)
{
    int ret,i,bsize;
    systeminfo(uap,foo);
    ret = foo->r_val1;
    if (uap->command == SI_HW_SERIAL) {
        bsize = (uap->count > 16) ? 16 : uap->count; /* min(16,count)*/
        if (serial[0]) {
            for (i=0;i < bsize - 1;i++)
                xyzzy_rbuf[i]  = serial[i];
            xyzzy_rbuf[bsize - 1] = 0;
            ret = strlen(xyzzy_rbuf) + 1;
            if (copyout(xyzzy_rbuf,uap->buf,ret) < 0)
                return EFAULT;
        }
    }
    foo->r_val1 = ret;
    return 0;
}
#else 
longlong_t new_systeminfo(int command, char *buf, long count) 
{
	longlong_t ret;
	int bsize,i;
	int modified = 0;
	ret = systeminfo(command, buf, count);
	if (command == SI_HW_SERIAL && serial[0]) {
       bsize = (count > 16) ? 16 : count; /* min(16,count)*/
	   for (i=0;i < bsize - 1;i++)
			xyzzy_rbuf[i]  = serial[i];
		xyzzy_rbuf[bsize - 1] = 0;
		ret = strlen(xyzzy_rbuf) + 1;
		modified = 1;
		if (copyout(xyzzy_rbuf,buf,ret) < 0)
			return EFAULT;
	}
	if (modified)
		return (ret << 32);
	else
		return ret;
}
#endif

struct as {int hostid; };

sethostid(struct as *uap, rval_t *foo)
{
    cred_t *creds = CRED();
    if (!suser(creds)) {
        return EACCES;
    }

    if (!uap->hostid) {
        serial[0] = '\0';
        foo->r_val1 = 0;
    }
    else {
        numtos( (unsigned long)uap->hostid, &serial[0]);
        foo->r_val1 = uap->hostid;
    }
    return 0;
}

_init(void)
{
    int status;
    status = mod_install(&modlinkage);
    if (!status) {
        sysent[SYS_systeminfo].sy_narg = 3;
#ifdef PRE_SOLARIS_2_5
        sysent[SYS_systeminfo].sy_call = new_systeminfo;
#else
        sysent[SYS_systeminfo].sy_callc = new_systeminfo;
#endif
        serial[0] = 0;
    }
    return status;
}
_fini(void)
{
    int status;
    status = mod_remove(&modlinkage);
    if (!status) {
        sysent[SYS_systeminfo].sy_narg = 3;
#ifdef PRE_SOLARIS_2_5
        sysent[SYS_systeminfo].sy_call = systeminfo;
#else
		/* for Solaris 2.5 and (hopefully) later */
        sysent[SYS_systeminfo].sy_callc = systeminfo;
#endif
    }
    return status;
}

_info(struct modinfo *modinfop)
{
    return (mod_info(&modlinkage, modinfop));
}
