/*
 * $Log:        sys1.c,v $
 * Revision 22.5  90/11/12  19:19:10  root
 * Новые вещи для СМ1425 и перемещение include.
 *
 * Revision 22.4  89/07/01  20:13:46  avg
 * Увеличен размер области свопинга, для которой начнут выполняться
 * проверки.
 *
 * Revision 22.3  89/04/28  17:02:44  avg
 * Добавлен #include "h/space.h" для вытаскивания констант SEMMNS и MSGMNI.
 *
 * Revision 22.2  89/04/12  15:36:09  korotaev
 * "param.h" ==> <sys/param.h>
 *
 * Revision 22.1  89/04/02  13:33:09  abs
 * заменил один anic на printf
 *
 * Revision 22.0  89/03/31  17:59:54  korotaev
 * Begining DEMOS 2.2.
 *
 * Revision 1.12  89/03/03  14:16:12  abs
 * semaphores
 *
 * Revision 1.11  89/02/09  16:36:59  abs
 * механизм копирования аргументов при ШеллСкрипте переписан вообще на фиг !
 *
 * Revision 1.10  89/02/07  15:51:28  abs
 * UCB_SHELLSCRIPT теперь имеет аргументы.
 *
 * Revision 1.9  89/02/04  18:51:33  avg
 * Ускорено переключение пользовательских оверлеев.
 *
 * Revision 1.8  89/01/26  15:55:42  korotaev
 * Уменьшены диагностики.
 *
 * Revision 1.7  89/01/07  20:56:57  korotaev
 * Слияние с исправлениями насчет FIFO, +fcntl, + разные режимы открытия
 *
 * (from VAX/UTS SV.2)
 *
 * Revision 1.5  88/03/23  13:37:36  korotaev
 * Состояние после слияния с АЗЛК, Э-85 и Бурковским планировщиком
 *
 * Revision 1.4  88/01/23  19:53:30  korotaev
 * Состояние после слияния с AVG-Э-85.
 *
 * Revision 1.3  87/12/09  16:31:52  andrew
 * 31 оверлей
 *
 * Revision 1.2  87/04/18  14:42:44  avg
 * Правки в vfork by alex.
 *
 * Revision 1.1  86/04/19  15:52:28  avg
 * Initial revision
 *
 */

/* Структура стека сразу после exec-а :
     ----------------
     |   na-ne= argc
     |    a0
     |    ...
     |    an
     |   NULL
     |    e0
     |    ...
     |    em
     |   NULL
  a0:|    "argv0\0"
     |    ...
  an:|    "argvn\0"
  e0:|    "envp0\0"
     |    ...
  em:|    "envpm\0"
     |     0
   0:| пустой стек
 -----

*/
#include <sys/param.h>
#include <sys/systm.h>
#include "../include/map.h"
#include <sys/dir.h>
#include <sys/user.h>
#include <sys/proc.h>
#include "../include/buf.h"
#include "../include/reg.h"
#include "../include/psw.h"
#include <sys/inode.h>
#include <sys/seg.h>
#include <sys/acct.h>
#include <sys/file.h>
#include <sys/wait.h>

#include "h/space.h"    /* для SEMMNS */

extern panicreboot;

/*
 * exec system call, with and without environments.
 */
struct execa {
        char    *fname;
        char    **argp;
        char    **envp;
};

exec()
{
        ((struct execa *)u.u_ap)->envp = NULL;
        exece();
}

#define byteaddress int

exece()
{
        register nc;    /* общее число байт в argp и envp */
        int na,         /* число строк в argp и envp вместе */
            ne,         /* число строк в envp */
            ucp,c;
        byteaddress ap;
                /* грязное место. В дальнейшем время от времени
                 * ap используется по разному: как   char *
                 * как char **     и как    int
                 */
        register char *cp;
        register struct buf *bp;
        register struct execa *uap;
        memaddr bno;
        struct inode *ip;
        char *next;
#define  AP (uap->argp)
#define  EP (uap->envp)

# define SCRMAG  "#!"
# define SCRMAG2 "/*#!"
# define ARGPLACE "$*"
# define SHSIZE  70
# define ARGV    11
        char **AV;

        extern int schar();
        int uid, gid;
        char indir, local;
        off_t  off;
        char shellcmd[ SHSIZE ];        /* прочитанная команда */
        char *argv[ ARGV ];             /* разобранная команда */

        if ((ip = namei(uchar, LOOKUP, 1)) == NULL)
                return;
#ifdef SCHED
        u.u_procp->p_sflag |= SEXEC;
#endif SCHED
        bno = 0;
        bp = (struct buf *) NULL;
        indir = 0;
        uid = u.u_uid;
        gid = u.u_gid;
        if (ip->i_mode&ISUID)
            uid = ip->i_uid;
        if (ip->i_mode&ISGID)
            gid = ip->i_gid;
again:
        if (access(ip, IEXEC))
                goto bad;
        if ((ip->i_mode & IFMT) != IFREG ||
           (ip->i_mode & (IEXEC | (IEXEC >> 3) | (IEXEC >> 6))) == 0) {
                u.u_error = EACCES;
                goto bad;
        }

        /* moved from getxfile() */
        u.u_base = (caddr_t) &u.u_exdata;
        u.u_count = sizeof u.u_exdata;
        u.u_offset = 0;
        u.u_segflg = 1;

        readi(ip);

        u.u_segflg = 0;
        if (u.u_error)
            goto bad;

        /* check if script. */
        if (indir < NSYMLNK && u.u_offset > (off_t)2 ){
            cp = (char *) &u.u_exdata;
            if( !bcmp(cp, SCRMAG, 2 ))
                off = (off_t) 2;
            else if( u.u_offset > (off_t)4 && !bcmp( cp, SCRMAG2, 4))
                off = (off_t) 4;
            else goto NoScript;

            /* да, имеется shell script */
            indir++;

            /* зачитать в буфер первые SHSIZE байт файла */
            u.u_count = SHSIZE;
            u.u_base  = shellcmd;
            u.u_offset= off;
            u.u_segflg = 1;

            readi( ip );
            u.u_segflg = 0;

            if( u.u_error )
                goto bad;

            iput( ip );
            off = u.u_offset - off;
            /* столько байт на самом деле прочлось внутрь */

            /* обработка */
            /* пока nc используется как временная переменная */

            for( nc=0; nc < (int) off; nc++ )
                if( shellcmd[ nc ] == '\n' ){
                        shellcmd[ nc] = '\0';
                        goto Parse;
                }

Big:
            /* строка слишком длинная */
            u.u_error = E2BIG;
Ret:
# ifdef SCHED
            u.u_procp->p_sflag &= ~SEXEC;
# endif SCHED
            return;
Parse:

            nc = 0;
            cp = shellcmd;
            for(;;){
                /* пропуск пробелов */
                while( *cp == ' ' ) cp++;
                if( ! *cp ) break;      /* конец строки */

                if( nc == ARGV-1 ) goto Big;
                argv[ nc++ ] = cp;

                /* пропуск аргумента */
                while( *cp && *cp != ' ' ) cp++;
                if( ! *cp ) break;

                *cp ++ = '\0';
            }
            argv[ nc ] = NULL;

            if( !nc ){
                u.u_error = EINVAL;
                goto Ret;
            }

            u.u_dirp = argv[0];

            /* смотрим, что лежит в указуемом файле */
            if ((ip = namei(schar, LOOKUP, 1)) == NULL) {
# ifdef SCHED
                u.u_procp->p_sflag &= ~SEXEC;
# endif SCHED
                return;
            }
            goto again;
        }
        if( indir == NSYMLNK ){
                u.u_error = ELOOP;
                goto bad;
        }

NoScript:
        /*other magic numbers are described in getxfile()*/

        /*
         * Collect arguments on "file" in swap space.
         * ip - locked ino of executable core image file.
         */
        uap = (struct execa *)u.u_ap;
        ap = na = ne = nc = 0;

        /* заказать в своппинге место для размещения
         * аргументов и окружения новой программы
         */
#ifndef UCB_NKB
        if ((bno = malloc(swapmap, (NCARGS + BSIZE - 1) / BSIZE)) == 0) {
#else   UCB_NKB
        if ((bno = malloc(swapmap, ctod((int) btoc(NCARGS + BSIZE)))) == 0) {
#endif  UCB_NKB
                /* panicreboot++;
                 * panic("out of swap");
                 */
                printf( "misswap\n");
                u.u_error = ENOSPC;
                goto bad;
        }

     /* формат:
         пусть есть файл   /.../.../x
         со следующим содержимым:
         -------------------------------
         |#!CMD A1 A2 A3
         | .......

        Вызываем команду:
                x B1 B2

        Запустится на самом деле такая команда:
                CMD A1 A2 A3 /.../.../x B1 B2

        Один из аргументов Ai может иметь вид $*
        Тогда аргументы запуска подставятся на это место, а не в конец:
         -------------------------------
         |#!CMD A1 $* A2 A3
         | .......

        Вызываем:
                x B1 B2

        Запустится:
                CMD A1 /.../.../x B1 B2 A2 A3
     */

        if( indir ) AV = argv;
        else        AV = NULL;

        /* цикл чтения сначала argp, затем envp в своппинг */
        if ( AP ) for (;;) {

GetNext:

                /* здесь ap используется как флаг, что можно
                 * брать аргументы из argv
                 */
                if( AV  && ! ap ){
                        next = *AV++;
                        if( next == NULL ){
                                AV = NULL;
                                goto GetNext;
                        }
                        if( !bcmp( next, ARGPLACE, 2 ))
                                goto Argp;
                        local = 1;
                }else

Argp:           if( AP ){
                        next = fuword ( (caddr_t) AP++ );
                        if( next == NULL ){
                                AP = NULL;
                                ap = 0;
                                /* можно дочитывать argv,
                                 * если еще осталось
                                 */
                                goto GetNext;
                        }
                        /* полное имя косвенного файла задаем как
                         * argp0 скриптовой программе.
                         */
                        if( ap == 0 && indir )
                                next = (caddr_t) (uap->fname);
                        local = 0;
                        ap++;   /* не ноль */
                }else
                if( EP ){
                        next = fuword( (caddr_t) EP++ );
                        if( next == NULL ) break;
                        local = 0;
                        ne++;
                        ap++;
                }else
                        break;

                /* число пересланных строк */
                na++;
                if (next == (char *)-1)
                        u.u_error = EFAULT;

                /* Адрес строки сейчас хранится в next.
                 * Начинаем цикл пересылки строки в "файл" в своппинге
                 */
                do {
                        if (nc >= NCARGS - 1)
                                u.u_error = E2BIG;
                        if( local ){
                             /* строка - в пространстве ядра */
                             c = *next++;
                             c &= 0377;
                        } else
                             /* строка - в пространстве юзера */
                             if ((c = fubyte((caddr_t) next++)) < 0)
                                   u.u_error = EFAULT;

                        if (u.u_error)
                                goto bad;
                        if ((nc & BMASK) == 0) {
                                if (bp) {
                                        mapout(bp);
                                        bdwrite(bp);
                                }
                        /* заказываем кэш-буфер для записи в
                         * "своппинговый файл"
                         */
#ifndef UCB_NKB
                                bp = getblk(swapdev, swplo + bno + (nc >> BSHIFT));
#else
                                bp = getblk(swapdev,
                                  dbtofsb(clrnd(swplo + bno)) + (nc >> BSHIFT));
#endif
                                cp = mapin(bp);
                        }

                        /* число засланных байтов ++ */
                        nc++;

                        /* заносим байт в кэш-буфер */
                        *cp++ = c;

                } while (c > 0);        /* пока не конец строки */
        }

        if (bp) {
                mapout(bp);
                bdwrite(bp);
        }

        /* выделяем память для новой программы,
         * зачитываем программу из файла в эту память
         */
        bp = 0;
        nc = (nc + NBPW - 1) & ~(NBPW - 1);
        if (getxfile(ip, (na * NBPW) + nc, uid, gid) || u.u_error)
                goto bad;

        /*
         * copy back arglist
         * формируем стек нового процесса (для вызова main)
         */

        ucp = -nc - NBPW;
        ap = ucp - na * NBPW - 3 * NBPW;
        u.u_ar0[R6] = ap;
        suword((caddr_t)ap, na - ne);
        nc = 0;
        for (;;) {
                ap += NBPW;
                if (na == ne) {
                        suword((caddr_t)ap, 0);
                        ap += NBPW;
                }
                if (--na < 0)
                        break;
                suword((caddr_t)ap, ucp);
                do {
                        if ((nc & BMASK) == 0) {
                                if (bp) {
                                        mapout(bp);
                                        bp->b_flags |= B_AGE;
                                        brelse(bp);
                                }
#ifndef UCB_NKB
                                bp = bread(swapdev, swplo + bno + (nc>>BSHIFT));
#else
                                bp = bread(swapdev,
                                  dbtofsb(clrnd(swplo + bno)) + (nc >> BSHIFT));
#endif
                                bp->b_flags &= ~B_DELWRI;
                                cp = mapin(bp);
                                /* stick in interpreter name for accounting */
                                if (indir && nc == 0)
                                        bcopy(cp, (caddr_t)u.u_dbuf, DIRSIZ);
                        }
                        subyte((caddr_t)ucp++, (c = *cp++));
                        nc++;
                } while(c & 0377);
        }
        suword((caddr_t) ap, 0);
        suword((caddr_t) (-NBPW), 0);
        /* завершено формирование стека */

        if (bp) {
                mapout(bp);
                bp->b_flags |= B_AGE;
                brelse(bp);
                bp = 0;
        }
        setregs();

bad:
#ifdef SCHED
        u.u_procp->p_sflag &= ~SEXEC;
#endif SCHED
        if (bp) {
                mapout(bp);
                bp->b_flags |= B_AGE;
                brelse(bp);
        }
        if (bno)
#ifndef UCB_NKB
                mfree(swapmap, (NCARGS + BSIZE - 1) / BSIZE, bno);
#else
                mfree(swapmap, ctod((int) btoc(NCARGS + BSIZE)), bno);
#endif
        iput(ip);
}

/*
 * Read in and set up memory for executed file.
 * Zero return is normal;
 * non-zero means only the text is being replaced
 */
getxfile(ip, nargc, uid, gid)
int nargc, uid, gid;
register struct inode *ip;
{

[...]

}

/*
 * Clear registers on exec
 */
setregs()
{

[...]

}

/*
 * exit system call:
 * pass back caller's arg
 */
rexit()
{
        register struct a {
                int     rval;
        } *uap;

        uap = (struct a *)u.u_ap;
        exit((uap->rval & 0377) << 8);
}

/*
 * Release resources.
 * Save u. area for parent to look at.
 * Enter zombie state.
 * Wake up parent and init processes,
 * and dispose of children.
 */
exit(rv)
{

[...]

}

/*
 * Wait system call.
 * Search for a terminated (zombie) child,
 * finally lay it to rest, and collect its status.
 * Look also for stopped (traced) children,
 * and pass back status from them.
 */
wait()
{

[...]

}

/*
 * fork system call.
 */
#ifdef  VIRUS_VFORK
fork()
{
        fork1(0);
}

/*
 * vfork system call
 */
vfork()
{
        fork1(1);
}

fork1(isvfork)
#else   VIRUS_VFORK
fork()
#endif
{

[...]

}

/*
 * break system call.
 *  -- bad planning: "break" is a dirty word in C.
 */
sbreak()
{

[...]

}