/*
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 outside 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>

#define NHOSTID 10

static char serials[NHOSTID][16];
char xyzzy_rbuf[16];

int sethostid();

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

extern struct mod_ops mod_syscallops;

static struct modlsys modlsys = {
&mod_syscallops,
"kern_hostid v1.02", 
&sethostid_sysent,
};

extern systeminfo();

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

struct as2 {int command; char *buf; long count;};
int new_systeminfo(struct as2 *uap, rval_t *foo)
{
    int ret,i,bsize;
    gid_t gid;
    cred_t *creds = CRED();
    systeminfo(uap,foo);
    ret = foo->r_val1;
    if (uap->command == SI_HW_SERIAL) {
        gid = creds->cr_gid;
        bsize = (uap->count > 16) ? 16 : uap->count; /* min(16,count)*/
        if (gid >= 900 && gid < 900+NHOSTID) {
            if (serials[gid - 900][0]) {
                for (i=0;i < bsize - 1;i++)
                    xyzzy_rbuf[i]  = serials[gid-900][i];
                xyzzy_rbuf[bsize - 1] = 0;
                ret = strlen(xyzzy_rbuf) + 1;
                if (copyout(xyzzy_rbuf,uap->buf,ret) < 0)
                    return EFAULT;
            }
        }
        for (gid = 900; gid < 900+NHOSTID; gid++) {
            if (groupmember(gid, creds)) {
                if (serials[gid - 900][0]) {
                    for (i=0;i< bsize - 1;i++)
                        xyzzy_rbuf[i]  = serials[gid-900][i];
                    xyzzy_rbuf[bsize - 1] = 0;
                    ret = strlen(xyzzy_rbuf) + 1;
                    if (copyout(xyzzy_rbuf,uap->buf,ret) < 0)
                        return EFAULT;
                    break;
                }
            }
        }
    }
    foo->r_val1 = ret;
    return 0;
}

struct as {int n; unsigned int hostid;};

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

    if ((uap->n >= NHOSTID )||(uap->n < 0)) {  
        return EINVAL;
    }
    numtos( (unsigned long)uap->hostid, &serials[uap->n][0]);
    foo->r_val1 = uap->hostid;
    return 0;
}

_init(void)
{
    int i,status;
    status = mod_install(&modlinkage);
    if (!status) {
        sysent[SYS_systeminfo].sy_narg = 3;
        sysent[SYS_systeminfo].sy_call = new_systeminfo;
        for (i=0; i < NHOSTID; i++) 
            serials[i][0] = 0;
    }
    return status;
}
_fini(void)
{
    int status;
    status = mod_remove(&modlinkage);
    if (!status) {
        sysent[SYS_systeminfo].sy_narg = 3;
        sysent[SYS_systeminfo].sy_call = systeminfo;
    }
    return status;
}

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