root/usr/src/cmd/sh/service.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
 */

/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */

/*
 * UNIX shell
 */

#include        "defs.h"
#include        <errno.h>
#include        <fcntl.h>

#define ARGMK   01

static unsigned char    *execs();
static void     gsort();
static int      split();
extern void makearg(struct argnod *);
extern short topfd;


/*
 * service routines for `execute'
 */
short
initio(struct ionod *iop, int save)
{
        unsigned char   *ion;
        int     iof, fd;
        int             ioufd;
        short   lastfd;
        int     newmode;

        lastfd = topfd;
        while (iop) {
                iof = iop->iofile;
                ion = mactrim(iop->ioname);
                ioufd = iof & IOUFD;

                if (*ion && (flags&noexec) == 0) {
                        if (save) {
                                fdmap[topfd].org_fd = ioufd;
                                fdmap[topfd++].dup_fd = savefd(ioufd);
                        }

                        if (iof & IODOC_SUBST) {
                                struct tempblk tb;

                                subst(chkopen(ion, 0), (fd = tmpfil(&tb)));

                                /*
                                 * pushed in tmpfil() --
                                 * bug fix for problem with
                                 * in-line scripts
                                 */
                                poptemp();

                                fd = chkopen(tmpout, 0);
                                unlink((const char *)tmpout);
                        } else if (iof & IOMOV) {
                                if (eq(minus, ion)) {
                                        fd = -1;
                                        close(ioufd);
                                } else if ((fd = stoi(ion)) >= USERIO) {
                                        failed(ion, badfile);
                                }
                                else
                                        fd = dup(fd);
                        } else if (((iof & IOPUT) == 0) && ((iof & IORDW) == 0))
                                fd = chkopen(ion, 0);
                        else if (iof & IORDW) /* For <> */ {
                                newmode = O_RDWR|O_CREAT;
                                fd = chkopen(ion, newmode);
                        } else if (flags & rshflg) {
                                failed(ion, restricted);
                        } else if (iof & IOAPP &&
                            (fd = open((char *)ion, 1)) >= 0) {
                                lseek(fd, (off_t)0, SEEK_END);
                        } else {
                                fd = create(ion);
                        }
                        if (fd >= 0)
                                renamef(fd, ioufd);
                }

                iop = iop->ionxt;
        }
        return (lastfd);
}

unsigned char *
simple(s)
unsigned char   *s;
{
        unsigned char   *sname;

        sname = s;
        while (1) {
                if (any('/', sname))
                        while (*sname++ != '/')
                                ;
                else
                        return (sname);
        }
}

unsigned char *
getpath(s)
        unsigned char   *s;
{
        unsigned char   *path, *newpath;
        int pathlen;

        if (any('/', s))
        {
                if (flags & rshflg)
                        failed(s, restricted);
                else
                        return ((unsigned char *)nullstr);
        } else if ((path = pathnod.namval) == 0)
                return ((unsigned char *)defpath);
        else {
                pathlen = length(path)-1;
                /* Add extra ':' if PATH variable ends in ':' */
                if (pathlen > 2 && path[pathlen - 1] == ':' &&
                                path[pathlen - 2] != ':') {
                        newpath = locstak();
                        (void) memcpystak(newpath, path, pathlen);
                        newpath[pathlen] = ':';
                        endstak(newpath + pathlen + 1);
                        return (newpath);
                } else
                        return (cpystak(path));
        }
}

int
pathopen(unsigned char *path, unsigned char *name)
{
        int     f;

        do
        {
                path = catpath(path, name);
        } while ((f = open((char *)curstak(), 0)) < 0 && path);
        return (f);
}

unsigned char *
catpath(unsigned char *path, unsigned char *name)
{
        /*
         * leaves result on top of stack
         */
        unsigned char   *scanp = path;
        unsigned char   *argp = locstak();

        while (*scanp && *scanp != COLON) {
                if (argp >= brkend)
                        growstak(argp);
                *argp++ = *scanp++;
        }
        if (scanp != path) {
                if (argp >= brkend)
                        growstak(argp);
                *argp++ = '/';
        }
        if (*scanp == COLON)
                scanp++;
        path = (*scanp ? scanp : 0);
        scanp = name;
        do
        {
                if (argp >= brkend)
                        growstak(argp);
        }
        while (*argp++ = *scanp++)
                ;
        return (path);
}

unsigned char *
nextpath(unsigned char *path)
{
        unsigned char   *scanp = path;

        while (*scanp && *scanp != COLON)
                scanp++;

        if (*scanp == COLON)
                scanp++;

        return (*scanp ? scanp : 0);
}

static const char       *xecmsg;
static unsigned char    **xecenv;

void
execa(unsigned char *at[], short pos)
{
        unsigned char   *path;
        unsigned char   **t = at;
        int             cnt;

        if ((flags & noexec) == 0) {
                xecmsg = notfound;
                path = getpath(*t);
                xecenv = local_setenv();

                if (pos > 0) {
                        cnt = 1;
                        while (cnt != pos) {
                                ++cnt;
                                path = nextpath(path);
                        }
                        execs(path, t);
                        path = getpath(*t);
                }
                while (path = execs(path, t))
                        ;
                failed(*t, xecmsg);
        }
}

static unsigned char *
execs(unsigned char *ap, unsigned char *t[])
{
        unsigned char   *p, *prefix;
        unsigned char   *savptr;

        prefix = catpath(ap, t[0]);
        trim(p = curstak());
        sigchk();

        execve((const char *)p, (char *const *)&t[0],
            (char *const *)xecenv);

        switch (errno) {
        case ENOEXEC:           /* could be a shell script */
                funcnt = 0;
                flags = 0;
                *flagadr = 0;
                comdiv = 0;
                ioset = 0;
                clearup();      /* remove open files and for loop junk */
                if (input)
                        close(input);
                input = chkopen(p, 0);

#ifdef ACCT
                preacct(p);     /* reset accounting */
#endif

                /*
                 * set up new args
                 */

                setargs(t);
                longjmp(subshell, 1);

        case ENOMEM:
                failed(p, toobig);

        case E2BIG:
                failed(p, arglist);

        case ETXTBSY:
                failed(p, txtbsy);

        case ELIBACC:
                failed(p, libacc);

        case ELIBBAD:
                failed(p, libbad);

        case ELIBSCN:
                failed(p, libscn);

        case ELIBMAX:
                failed(p, libmax);

        default:
                xecmsg = badexec;
        case ENOENT:
                return (prefix);
        }
}

BOOL            nosubst;

void
trim(unsigned char *at)
{
        unsigned char   *last;
        unsigned char   *current;
        unsigned char   c;
        int     len;
        wchar_t wc;

        nosubst = 0;
        if (current = at) {
                last = at;
                while (c = *current) {
                        if ((len = mbtowc(&wc, (char *)current,
                            MB_LEN_MAX)) <= 0) {
                                *last++ = c;
                                current++;
                                continue;
                        }

                        if (wc != '\\') {
                                memcpy(last, current, len);
                                last += len;
                                current += len;
                                continue;
                        }

                        /* remove \ and quoted nulls */
                        nosubst = 1;
                        current++;
                        if (c = *current) {
                                if ((len = mbtowc(&wc, (char *)current,
                                    MB_LEN_MAX)) <= 0) {
                                        *last++ = c;
                                        current++;
                                        continue;
                                }
                                memcpy(last, current, len);
                                last += len;
                                current += len;
                        } else
                                current++;
                }

                *last = 0;
        }
}

/* Same as trim, but only removes backlashes before slashes */
void
trims(at)
unsigned char   *at;
{
        unsigned char   *last;
        unsigned char   *current;
        unsigned char   c;
        int     len;
        wchar_t wc;

        if (current = at)
        {
                last = at;
                while (c = *current) {
                        if ((len = mbtowc(&wc, (char *)current,
                                        MB_LEN_MAX)) <= 0) {
                                *last++ = c;
                                current++;
                                continue;
                        }

                        if (wc != '\\') {
                                memcpy(last, current, len);
                                last += len; current += len;
                                continue;
                        }

                        /* remove \ and quoted nulls */
                        current++;
                        if (!(c = *current)) {
                                current++;
                                continue;
                        }

                        if (c == '/') {
                                *last++ = c;
                                current++;
                                continue;
                        }

                        *last++ = '\\';
                        if ((len = mbtowc(&wc, (char *)current,
                                        MB_LEN_MAX)) <= 0) {
                                *last++ = c;
                                current++;
                                continue;
                        }
                        memcpy(last, current, len);
                        last += len; current += len;
                }
                *last = 0;
        }
}

unsigned char *
mactrim(s)
unsigned char   *s;
{
        unsigned char   *t = macro(s);

        trim(t);
        return (t);
}

unsigned char **
scan(argn)
int     argn;
{
        struct argnod *argp =
                        (struct argnod *)(Rcheat(gchain) & ~ARGMK);
        unsigned char **comargn, **comargm;

        comargn = (unsigned char **)getstak(BYTESPERWORD * argn + BYTESPERWORD);
        comargm = comargn += argn;
        *comargn = ENDARGS;
        while (argp)
        {
                *--comargn = argp->argval;

                trim(*comargn);
                argp = argp->argnxt;

                if (argp == 0 || Rcheat(argp) & ARGMK)
                {
                        gsort(comargn, comargm);
                        comargm = comargn;
                }
                argp = (struct argnod *)(Rcheat(argp) & ~ARGMK);
        }
        return (comargn);
}

static void
gsort(from, to)
unsigned char   *from[], *to[];
{
        int     k, m, n;
        int     i, j;

        if ((n = to - from) <= 1)
                return;
        for (j = 1; j <= n; j *= 2)
                ;
        for (m = 2 * j - 1; m /= 2; )
        {
                k = n - m;
                for (j = 0; j < k; j++)
                {
                        for (i = j; i >= 0; i -= m)
                        {
                                unsigned char **fromi;

                                fromi = &from[i];
                                if (cf(fromi[m], fromi[0]) > 0)
                                {
                                        break;
                                }
                                else
                                {
                                        unsigned char *s;

                                        s = fromi[m];
                                        fromi[m] = fromi[0];
                                        fromi[0] = s;
                                }
                        }
                }
        }
}

/*
 * Argument list generation
 */
int
getarg(ac)
struct comnod   *ac;
{
        struct argnod   *argp;
        int             count = 0;
        struct comnod   *c;

        if (c = ac)
        {
                argp = c->comarg;
                while (argp)
                {
                        count += split(macro(argp->argval), 1);
                        argp = argp->argnxt;
                }
        }
        return (count);
}

static int
split(s)                /* blank interpretation routine */
unsigned char   *s;
{
        unsigned char   *argp;
        int             c;
        int             count = 0;
        for (;;)
        {
                int length;
                sigchk();
                argp = locstak() + BYTESPERWORD;
                while (c = *s) {
                        wchar_t wc;
                        if ((length = mbtowc(&wc, (char *)s,
                                        MB_LEN_MAX)) <= 0) {
                                wc = (unsigned char)*s;
                                length = 1;
                        }

                        if (c == '\\') { /* skip over quoted characters */
                                if (argp >= brkend)
                                        growstak(argp);
                                *argp++ = c;
                                s++;
                                /* get rest of multibyte character */
                                if ((length = mbtowc(&wc, (char *)s,
                                                MB_LEN_MAX)) <= 0) {
                                        wc = (unsigned char)*s;
                                        length = 1;
                                }
                                if (argp >= brkend)
                                        growstak(argp);
                                *argp++ = *s++;
                                while (--length > 0) {
                                        if (argp >= brkend)
                                                growstak(argp);
                                        *argp++ = *s++;
                                }
                                continue;
                        }

                        if (anys(s, ifsnod.namval)) {
                                /* skip to next character position */
                                s += length;
                                break;
                        }

                        if (argp >= brkend)
                                growstak(argp);
                        *argp++ = c;
                        s++;
                        while (--length > 0) {
                                if (argp >= brkend)
                                        growstak(argp);
                                *argp++ = *s++;
                        }
                }
                if (argp == staktop + BYTESPERWORD)
                {
                        if (c)
                        {
                                continue;
                        }
                        else
                        {
                                return (count);
                        }
                }
                /*
                 * file name generation
                 */

                argp = endstak(argp);
                trims(((struct argnod *)argp)->argval);
                if ((flags & nofngflg) == 0 &&
                        (c = expand(((struct argnod *)argp)->argval, 0)))
                        count += c;
                else
                {
                        makearg((struct argnod *)argp);
                        count++;
                }
                gchain = (struct argnod *)((int)gchain | ARGMK);
        }
}

#ifdef ACCT
#include        <sys/types.h>
#include        <sys/acct.h>
#include        <sys/times.h>

struct acct sabuf;
struct tms buffer;
static clock_t before;
static int shaccton;    /* 0 implies do not write record on exit */
                        /* 1 implies write acct record on exit */
static comp_t compress(clock_t);


/*
 *      suspend accounting until turned on by preacct()
 */
void
suspacct(void)
{
        shaccton = 0;
}

void
preacct(unsigned char *cmdadr)
{
        unsigned char *simple();

        if (acctnod.namval && *acctnod.namval) {
                sabuf.ac_btime = time((time_t *)0);
                before = times(&buffer);
                sabuf.ac_uid = getuid();
                sabuf.ac_gid = getgid();
                movstrn(simple(cmdadr), sabuf.ac_comm, sizeof (sabuf.ac_comm));
                shaccton = 1;
        }
}

void
doacct(void)
{
        int fd;
        clock_t after;

        if (shaccton) {
                after = times(&buffer);
                sabuf.ac_utime = compress(buffer.tms_utime + buffer.tms_cutime);
                sabuf.ac_stime = compress(buffer.tms_stime + buffer.tms_cstime);
                sabuf.ac_etime = compress(after - before);

                if ((fd = open((char *)acctnod.namval,
                    O_WRONLY | O_APPEND | O_CREAT, 0666)) != -1) {
                        write(fd, &sabuf, sizeof (sabuf));
                        close(fd);
                }
        }
}

/*
 *      Produce a pseudo-floating point representation
 *      with 3 bits base-8 exponent, 13 bits fraction
 */

static comp_t
compress(clock_t t)
{
        int exp = 0;
        int rund = 0;

        while (t >= 8192) {
                exp++;
                rund = t & 04;
                t >>= 3;
        }

        if (rund) {
                t++;
                if (t >= 8192) {
                        t >>= 3;
                        exp++;
                }
        }
        return ((exp << 13) + t);
}
#endif