Title: svrctl()


From svrctl(2):

Svrctl allows root to control the kernel in various ways or implements some very Minix specific system calls that don't deserve their own system call number.

This system call makes it easy to add new ways of setting and getting kernel parameters, but at the same time, backwards compatibility is not guaranteed. Read the include file to see what the struct's mentioned below look like. Most calls are root-only, unless specified otherwise.

The only way to know how to properly use these calls is to study the associated kernel or server code, or the programs that already use these calls.


Here is a description of the different requests made by the network service (from svrctl(2)):

MMSIGNON
Inform MM that the current process wants to become a server.

FSSIGNON
Register a new device with FS.

SYSSIGNON
Inform the kernel that the process want to become a server. The processes task number is filled-in in a struct systaskinfo.



In order to understand svrctl() better, it is helpful to look at some code. Here is the relevant code for the call from the network service that registers itself as a server to the memory manager. In other words, this is the relevant code for the svrctl(MMSIGNON, (void *) NULL) call that the network service makes:


From include/sys/svrctl.h:

#define MMSIGNON       _IO ('M', 4)

#define _IO(x,y)       ((x << 8) | y | _IOC_VOID)

#define _IOC_VOID       0x20000000


Therefore,

MMSIGNON = _IO ('M', 4) = ( (0x4D<<8) | 4 | 0x20000000 )
= 0x4D00 | 4 | 0x20000000
= 0x20004D04


From src/lib/other/_svrctl.c:

int svrctl(int request, void *argp)

{
       message m;

       m.m2_i1 = request;
       m.m2_p1 = argp;

       switch ((request >> 8) & 0xFF) {
       case 'M':
       case 'S':
              /* MM handles calls for itself and the kernel. */
              return _syscall(MM, SVRCTL, &m);
       case 'F':
       case 'I':
              /* FS handles calls for itself and inet. */
              return _syscall(FS, SVRCTL, &m);
       default:
              errno = EINVAL;
              return -1;
       }
}
(Note that MM is #define'd as 0 and FS is #define'd as 1 in include/lib.h.)


Since (0x20004D04 >> 8) & 0xFF = 0x4D = 'M', _syscall(MM, SVRCTL, &m) is called. _syscall(), in turn, calls _sendrec(). _sendrec() is not covered in this general comment but in its own general comment.

From src/lib/other/syscall.c:

PUBLIC int _syscall(who, syscallnr, msgptr)

int who;
int syscallnr;
register message *msgptr;
{
int status;

msgptr->m_type = syscallnr;
status = _sendrec(who, msgptr);
if (status != 0) {
       /* 'sendrec' itself failed. */
       /* XXX - strerror doesn't know all the codes */
       msgptr->m_type = status;
}
if (msgptr->m_type < 0) {
       errno = -msgptr->m_type;
       return(-1);
}
return(msgptr->m_type);
}
Through the normal message passing system, a message is sent to the memory manager from the network service informing the memory manager of the network service's status as a server (in other words, the network service signs on to the memory manager as a server; a server has greater privileges than a normal process). This message is handled by the relevant function in call_vec[], an array of functions.

From src/mm/main.c:

/*===========================================================================*

*                            main                                    *
*===========================================================================*/
PUBLIC void main()
{
/* Main routine of the memory manager. */

int result, proc_nr;
struct mproc *rmp;

mm_init();                     /* initialize memory manager tables */

/* This is MM's main loop- get work and do it, forever and forever. */
while (TRUE) {
       get_work();              /* wait for an MM system call */

       /* If the call number is valid, perform the call. */
       if ((unsigned) mm_call >= NCALLS) {
              result = ENOSYS;
       } else {
              result = (*call_vec[mm_call])();
       }

       /* Send the results back to the user to indicate completion. */
       if (result != E_NO_MESSAGE) setreply(who, result);

       swap_in();              /* maybe a process can be swapped in? */

       /* Send out all pending reply messages, including the answer to
        * the call just made above. The processes must not be swapped out.
        */
       for (proc_nr = 0, rmp = mproc; proc_nr < NR_PROCS; proc_nr++, rmp++) {
              if ((rmp->mp_flags & (REPLY | ONSWAP)) == REPLY) {
                     if (send(proc_nr, &rmp->mp_reply) != OK)
                            panic("MM can't reply to", proc_nr);
                     rmp->mp_flags &= ~REPLY;
              }
       }
}
}


get_work() has two important tasks - to receive the incoming message and set the global variable mm_call to the message type.

/*===========================================================================*

*                            get_work                             *
*===========================================================================*/
PRIVATE void get_work()
{
/* Wait for the next message and extract useful information from it. */

if (receive(ANY, &mm_in) != OK) panic("MM receive error", NO_NUM);
who = mm_in.m_source;              /* who sent the message */
mm_call = mm_in.m_type;       /* system call number */

/* Process slot of caller. Misuse MM's own process slot for tasks (KSIG?). */
mp = &mproc[who < 0 ? MM_PROC_NR : who];
}
From src/mm/table.c:

_PROTOTYPE (int (*call_vec[NCALLS]), (void) ) = {

       no_sys,              /* 0 = unused       */
       do_mm_exit,       /* 1 = exit       */
       do_fork,       /* 2 = fork       */
       no_sys,              /* 3 = read       */
       no_sys,              /* 4 = write       */
       no_sys,              /* 5 = open       */
       no_sys,              /* 6 = close       */
       do_waitpid,       /* 7 = wait       */
       no_sys,              /* 8 = creat       */
       no_sys,              /* 9 = link       */
       no_sys,              /* 10 = unlink       */
       do_waitpid,       /* 11 = waitpid       */
       no_sys,              /* 12 = chdir       */
       no_sys,              /* 13 = time       */
       no_sys,              /* 14 = mknod       */
       no_sys,              /* 15 = chmod       */
       no_sys,              /* 16 = chown       */
       do_brk,              /* 17 = break       */
       no_sys,              /* 18 = stat       */
       no_sys,              /* 19 = lseek       */
       do_getset,       /* 20 = getpid       */
       no_sys,              /* 21 = mount       */
       no_sys,              /* 22 = umount       */
       do_getset,       /* 23 = setuid       */
       do_getset,       /* 24 = getuid       */
       no_sys,              /* 25 = stime       */
       do_trace,       /* 26 = ptrace       */
       do_alarm,       /* 27 = alarm       */
       no_sys,              /* 28 = fstat       */
       do_pause,       /* 29 = pause       */
       no_sys,              /* 30 = utime       */
       no_sys,              /* 31 = (stty)       */
       no_sys,              /* 32 = (gtty)       */
       no_sys,              /* 33 = access       */
       no_sys,              /* 34 = (nice)       */
       no_sys,              /* 35 = (ftime)       */
       no_sys,              /* 36 = sync       */
       do_kill,       /* 37 = kill       */
       no_sys,              /* 38 = rename       */
       no_sys,              /* 39 = mkdir       */
       no_sys,              /* 40 = rmdir       */
       no_sys,              /* 41 = dup       */
       no_sys,              /* 42 = pipe       */
       no_sys,              /* 43 = times       */
       no_sys,              /* 44 = (prof)       */
       no_sys,              /* 45 = unused       */
       do_getset,       /* 46 = setgid       */
       do_getset,       /* 47 = getgid       */
       no_sys,              /* 48 = (signal)*/
       no_sys,              /* 49 = unused       */
       no_sys,              /* 50 = unused       */
       no_sys,              /* 51 = (acct)       */
       no_sys,              /* 52 = (phys)       */
       no_sys,              /* 53 = (lock)       */
       no_sys,              /* 54 = ioctl       */
       no_sys,              /* 55 = fcntl       */
       no_sys,              /* 56 = (mpx)       */
       no_sys,              /* 57 = unused       */
       no_sys,              /* 58 = unused       */
       do_exec,       /* 59 = execve       */
       no_sys,              /* 60 = umask       */
       no_sys,              /* 61 = chroot       */
       do_getset,       /* 62 = setsid       */
       do_getset,       /* 63 = getpgrp       */

       do_ksig,       /* 64 = KSIG: signals originating in the kernel       */
       no_sys,              /* 65 = UNPAUSE       */
       no_sys,        /* 66 = unused */
       no_sys,              /* 67 = REVIVE       */
       no_sys,              /* 68 = TASK_REPLY */
       no_sys,              /* 69 = unused       */
       no_sys,              /* 70 = unused       */
       do_sigaction,       /* 71 = sigaction */
       do_sigsuspend,       /* 72 = sigsuspend */
       do_sigpending,       /* 73 = sigpending */
       do_sigprocmask,       /* 74 = sigprocmask */
       do_sigreturn,       /* 75 = sigreturn */
       do_reboot,       /* 76 = reboot       */
       do_svrctl,       /* 77 = svrctl       */
};
From src/mm/misc.c:

*=====================================================================*

*                      do_svrctl                             *
*=====================================================================*/
PUBLIC int do_svrctl()
{
int req;
vir_bytes ptr;

req = svrctl_req;
ptr = (vir_bytes) svrctl_argp;

/* Is the request for the kernel? */
if (((req >> 8) & 0xFF) == 'S') {
       return(sys_sysctl(who, req, mp->mp_effuid == SUPER_USER, ptr));
}

switch(req) {
case MMSIGNON: {
       /* A user process becomes a task. Simulate an exit by
        * releasing a waiting parent and disinheriting children.
        */
       struct mproc *rmp;
       pid_t pidarg;

       if (mp->mp_effuid != SUPER_USER) return(EPERM);

       rmp = &mproc[mp->mp_parent];
       tell_fs(EXIT, who, 0, 0);

       pidarg = rmp->mp_wpid;
       if ((rmp->mp_flags & WAITING) && (pidarg == -1
              || pidarg == mp->mp_pid || -pidarg == mp->mp_procgrp))
       {
              /* Wake up the parent. */
              rmp->reply_res2 = 0;
              setreply(mp->mp_parent, mp->mp_pid);
              rmp->mp_flags &= ~WAITING;
       }

       /* Disinherit children. */
       for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) {
              if (rmp->mp_flags & IN_USE && rmp->mp_parent == who) {
                     rmp->mp_parent = INIT_PROC_NR;
              }
       }

       /* Become like MM and FS. */
       mp->mp_pid = mp->mp_procgrp = 0;
       mp->mp_parent = 0;
       return(OK); }

case MMSWAPON: {
       struct mmswapon swapon;

       if (mp->mp_effuid != SUPER_USER) return(EPERM);

       if (sys_copy(who, D, (phys_bytes) ptr,
              MM_PROC_NR, D, (phys_bytes) &swapon,
              (phys_bytes) sizeof(swapon)) != OK) return(EFAULT);

       return(swap_on(swapon.file, swapon.offset, swapon.size)); }

case MMSWAPOFF: {
       if (mp->mp_effuid != SUPER_USER) return(EPERM);

       return(swap_off()); }

default:
       return(EINVAL);
}
}
For an MMSIGNON request, the mp_pid (servers do not have pid's), mp_procgrp (servers are in process group 0; process groups are used for signals), and mp_parent (servers do not have parents) fields of the process must be set to 0, any parents of the process must be released (i.e., the current process is the child of fork() and the parent of the process called wait()), and any children of the process must be adopted by init.