root/usr/src/cmd/bnu/grades.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) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include "uucp.h"
#include <grp.h>

#define G_EXT   0
#define G_INT   1
#define G_RES   2
#define G_ACT   3
#define G_IDF   4
#define G_MAX   512     /* max number of fields in the Grades file line */
#define SMBUF   128

#define TYPE    0
#define FILE1   1
#define FILE2   2
#define USER    3
#define OPTS    4
#define FILE3   5

extern int rdfulline(), jsize(), gdirf(), gnamef();
extern void wfcommit();

static void     mailAdmin();            /* Send mail to administrator. */

/*
 * chkgrp - checks to see the group has permission
 *              to use a service grade queue.
 *
 * returns
 *
 *      SUCCESS - if the group has permissions
 *      FAIL - if group does not
 *
 */

static int
chkgrp(carray,na)
char **carray;
int na;
{
        struct group *grp;
        int i;
        gid_t gid;

        gid = getgid();
        grp = getgrgid(gid);

        for (i = G_IDF; i < na; i++)
                        if (EQUALS(carray[i], grp->gr_name))
                                return(SUCCESS);

        return(FAIL);
}

/*
 * chkusr - checks the permission fields of the Grades file
 *          to determine if the user can queue to a particular service grade.
 *
 * returns
 *
 *      SUCCESS - if the user can queue to the service grade.
 *      FAIL - if the user can not queue to this service grade.
 *
 */

static int
chkusr(carray, na)
char **carray;
int na;
{
        int i;

        /*
         * start at the point where the users are supposed to be in the
         * Grades file. Loop thru until the end of the user list is
         * found or the user name is found. If the user name is found then
         * return TRUE. If the end of the list is found, return FAIL.
         */

        DEBUG(9, "User (%s)\n", User);

        /* check for any user and return if so */

        if (EQUALS(carray[G_IDF], "Any"))
                return(SUCCESS);

        DEBUG(9, "Members of administrator defined service grade (%s)\n", carray[G_EXT]);

        for (i = G_IDF; i < na; i++) {
                DEBUG(9, "%s\n", carray[i]);
                if (EQUALS(User, carray[i]))
                        return(SUCCESS);
        }

        return(FAIL);
}

/*
 *      fgrade - finds the appropiate queue to queue a job into
 *
 *      returns
 *              SUCCESS -> found a queue
 *              FAIL    -> can't find a queue
 */

int
fgrade(scfile)
struct cs_struct *scfile;
{
        char fdgrade();
        FILE *cfd;
        char line[BUFSIZ];
        char *carray[G_MAX];
        long climit;

        /* Check for the default service grade first */

        if (strcmp(scfile->sgrade, "default") == 0) {
                scfile->grade = fdgrade();
                return(SUCCESS);
        }

        /* open grades file to begin a linear for the grade requested */

        cfd = fopen(GRADES, "r");

        /* loop until the file is empty or we find the grade we want */

        while (rdfulline(cfd, line, BUFSIZ) != 0) {
                (void) getargs(line, carray, G_MAX);

                /* check to see if this is the grade we want */

                if (!EQUALS(scfile->sgrade, carray[G_EXT]))
                        continue;

                if (jsize(scfile, carray[G_RES], &climit) != FAIL) {
                        (void) fclose(cfd);
                        scfile->grade = *carray[G_INT];
                        return(SUCCESS);
                }
        }

        (void) fclose(cfd);

        (void) fprintf(stderr, gettext("Job size (%ld bytes)"
            " exceeds maximum number of bytes (%ld bytes)"
            " allowed into this service grade (%s).\n"
            "Job queued to default grade.\n"),
            scfile->jsize, climit, scfile->sgrade);

        scfile->grade = fdgrade();
        return(SUCCESS);
}

/*
 *      fdgrade - finds the default queue for this system
 *
 *      returns
 *              a one char name for the default queue
 *
 */

char
fdgrade()
{
        FILE *cfd;
        char line[BUFSIZ];
        char *carray[G_MAX];

        /* Check for the default grade first */

                cfd = fopen(GRADES, "r");

                /* loop until the end of the file is read */

                for (; rdfulline(cfd, line, BUFSIZ) != 0;) {

                        /* parse the fields of this line */

                        (void) getargs(line, carray, G_MAX);

                        /* check to see if the administrator has defined
                         * a default grade for the machine.
                         */

                        if (strcmp(carray[G_EXT], "default") != 0)
                                continue;

                        /* default must be defined in the file
                         *  close the file, get the queue name, and return.
                         */

                        (void) fclose(cfd);
                        return(*carray[G_INT]);
                }

                /* no default defined in this file. close file.
                 * get our default queue and return.
                 */

                (void) fclose(cfd);
                return(D_QUEUE);
}

/*
 * job_size - determines the size of a job
 *
 * returns
 *
 *      SUCCESS - if the size of the job can be determined
 *      FAIL    - otherwise
 */

int
job_size(scfile)
struct cs_struct *scfile;
{
        extern int Dfileused;
        struct stat s;
        FILE *fp;
        char line[BUFSIZ];
        char *carray[G_MAX];
        int na;
        int nodfile = FALSE;
        int ret;

        scfile->jsize = 0;

        fp = fopen(scfile->file, "r");

        if (fp == NULL) {
                toCorrupt(scfile->file);
                errent(Ct_OPEN, scfile->file, errno, __FILE__,  __LINE__);
        }

        while (fgets(line, BUFSIZ, fp) != NULL) {
                na = getargs(line, carray, G_MAX);

                if (na < 6) {
                        (void) fclose(fp);
                        toCorrupt(scfile->file);
                        errent("BAD NUMBER OF ARGUMENTS", scfile->file, 0,
                                __FILE__, __LINE__);
                }

                /* if the type of a transfer is not a push
                 * then don't try to determine the size of
                 * the data file, because you can't.
                 */

                if (*carray[TYPE] == 'R')
                        continue;

                /* find the data dile that is to be transferred */

                if ((ret = stat(carray[FILE3], &s)) != 0) {
                        if (errno == ENOENT) {
                                nodfile = TRUE;
                                ret = stat(carray[FILE1], &s);
                        }
                }
                else
                        Dfileused = TRUE;

                /*
                 * check to see if the return code from stat was 0
                 * if return code was not 0, write message to error
                 * log and quit. Otherwise, add size of file to job
                 * size and continue looping.
                 */

                if (ret != 0) {
                        (void) fclose(fp);
                        errent(Ct_STAT, nodfile ?
                                carray[FILE1] : carray[FILE3], errno,
                                __FILE__, __LINE__);
                }

                nodfile = FALSE;
                scfile->jsize += s.st_size;
        }
        (void) fclose(fp);
        return(SUCCESS);
}

static void lcase();

/*
 * jsize - determines whether if a job is small enough to
 *         be placed in the appropiate queue.
 *
 * returns
 *
 *      SUCCESS - if the size of the job is less than or
 *                equal to the number of bytes in the restriction
 *                of the GRADES file.
 *
 *      FAIL    - otherwise
 */

int
jsize(scfile, climit, nlimit)
struct cs_struct *scfile;
char *climit;
long *nlimit;
{
#define ONE_K (1024)
#define ONE_MEG ((1024)*(1024))

        char rest[SMBUF];
        char msg[BUFSIZ], *p;

        if (EQUALS(climit, "Any"))
                return(SUCCESS);

        lcase(climit, rest, SMBUF);

        if (!(p = strchr(rest, 'k')) && (!(p = strchr(rest, 'm')))) {

                for(p = climit; *p; ++p) {
                        if (isdigit(*p))
                                continue;

                        /* corrupt restriction field in the Grades file.
                         * report it to the uucp administrator.
                         */

                        snprintf(msg, sizeof (msg),
                            gettext("Error encountered in the"
                            " restrictions field of the Grades file."
                            "  Field contents (%s)."), climit);
                        mailAdmin(msg);
                        return(SUCCESS);
                }

                *nlimit = atol(climit);
        }
        else if (*p == 'k') {
                *p = '\0';
                *nlimit = (long) (atof(rest) * ONE_K);
        }
        else {
                *p = '\0';
                *nlimit = (long) (atof(rest) * ONE_MEG);
        }

        if (scfile->jsize <= *nlimit)
                return(SUCCESS);
        else
                return(FAIL);
}

static void
lcase(s, t, lim)
char s[], t[];
int lim;
{
        char *p;
        int i;


        p = s;

        for (i = 0; i < lim-1 && *p; i++)
                if (isupper(*p))
                        t[i] = tolower(*p++);
                else
                        t[i] = *p++;

        t[i] = '\0';
        return;
}

/*
 * mailAdmin - mail a message to the uucp administrator.
 *
 * returns:
 *
 *      nothing
 */

static void
mailAdmin (msg)

char *  msg;

{
        char    cmd[BUFSIZ];            /* Place to build mail command. */
        FILE *  mail;                   /* Channel to write mail on. */

        (void) sprintf(cmd, "%s %s %s", PATH, MAIL, "uucp");
        if ((mail = popen(cmd, "w")) != (FILE *) NULL)
        {
                (void) fprintf(mail, "To: uucp\nSubject: %s\n\n%s\n",
                    gettext("Grades file problem"), msg);
                (void) pclose(mail);
        }

        /*
         * Ignore popen failure.  There is not much that we can do if
         * it fails, since we are already trying to notify the administrator
         * of a problem.
         */
        return;
}

/*
 * putdfiles - moves any and all of the D. to the spool directory for
 *             a C. file.
 *
 * returns
 *
 *      nothing
 */

void
putdfiles(scfile)
struct cs_struct scfile;
{
        FILE *fp;
        char line[BUFSIZ];
        char *carray[G_MAX];
        int na;
        struct stat s;

        fp = fopen(scfile.file, "r");

        if (fp == NULL) {
                toCorrupt(scfile.file);
                errent(Ct_OPEN, scfile.file, errno, __FILE__, __LINE__);
        }

        while (fgets(line, BUFSIZ, fp) != NULL) {

                na = getargs(line, carray, G_MAX);
                if (na < 6) {
                        (void) fclose(fp);
                        toCorrupt(scfile.file);
                        errent("BAD NUMBER OF ARGUMENTS", scfile.file, 0,
                                __FILE__, __LINE__);
                }

                if (*carray[TYPE] == 'R')
                        continue;

                /* move D. file to the spool area */

                if (stat(carray[FILE3], &s) != -1)
                        wfcommit(carray[FILE3], carray[FILE3], scfile.sys);
        }

        (void) fclose(fp);
        return;
}

/*
 * reads a line from a file and takes care of comment lines
 * and continuations (\) in last column.
 *
 * return:
 *      the number of chars that are placed in line.
 */

int
rdfulline(fd, line, lim)
FILE *fd;
char *line;
int lim;
{
        register char *p, *c;
        char buf[BUFSIZ];
        size_t blr, btox;

        p = line;
        for (;fgets(buf, BUFSIZ, fd) != NULL;) {
                /* check to see if it is a comment */

                if (buf[0] == '#')
                        continue;

                /* remove trailing white space */
                c = &buf[strlen(buf)-1];
                while (c>=buf && (*c == '\n' || *c == '\t' || *c == ' ') )
                        *c-- = NULLCHAR;

                if (buf[0] == '\n' || buf[0] == NULLCHAR)
                        continue;

                blr = lim - 1 - (p - line);
                btox = blr < strlen(buf) ? blr : strlen(buf);

                if (btox <= 0)
                        break;

                (void) strncpy(p, buf, btox);
                p += btox - 1;

                if ( *(p-1) == '\\')
                        p--;
                else
                        break;
        }

        *++p = '\0';
        return(p-line-1);
}

/*      upermit - checks to determine if the user has permissions
 *      to use administrator defined service grade.
 *
 *      returns
 *              SUCCESS -> if the user can queue to this service grade.
 *              FAIL -> if the user cannot queue to this service grade.
 */

int
upermit(carray, na)
char **carray;
int na;
{
#define G_USR "user"
#define G_NUSR "non-user"
#define G_GRP "group"
#define G_NGRP "non-group"

        char actn[SMBUF];
        char ufld[SMBUF];
        char msg[BUFSIZ];

        (void) strcpy(actn, carray[G_ACT]);

        lcase(actn, ufld, SMBUF);

        if (EQUALS(ufld, G_USR))
                return(chkusr(carray,na));

        if (EQUALS(ufld, G_NUSR))
                return((chkusr(carray, na) != SUCCESS) ? SUCCESS : FAIL);

        if (EQUALS(ufld, G_GRP))
                return(chkgrp(carray, na));

        if (EQUALS(ufld, G_NGRP))
                return((chkgrp(carray, na) != SUCCESS) ? SUCCESS : FAIL);

        (void) snprintf(msg, sizeof (msg),
            gettext("Error encountered in action field of"
            " the Grades file. Field contents (%s)."), carray[G_ACT]);
        mailAdmin(msg);
        return(FAIL);
}

/*
 *      vergrd - verify if the grade name is a valid administrator
 *               defined service grade name and if the user has the
 *               appropiate permission to use this grade.
 *
 *      returns
 *              SUCCESS -> grade is valid and user is
 *                         permitted to use this grade.
 *              FAIL    -> otherwise
 *
 */

int
vergrd(grade)
char *grade;
{
        FILE *cfd;
        char line[BUFSIZ];
        char *carray[G_MAX];
        int na;

        /* Check for the default grade first */

        if (EQUALS(grade, "default"))
                return(SUCCESS);

        /* open grades file to begin a linear for the grade requested */

        cfd = fopen(GRADES, "r");

        /* loop until the file is empty or we find the grade we want */

        while (rdfulline(cfd, line, BUFSIZ) != 0) {
                na = getargs(line, carray, G_MAX);

                /* check to see if this is the grade we want */

                if (!EQUALS(grade, carray[G_EXT]))
                        continue;

                /* check for the permission on this grade */

                if (upermit(carray, na) != FAIL) {
                        (void) fclose(cfd);
                        return(SUCCESS);
                }
                else {
                        (void) fclose(cfd);
                        (void) fprintf(stderr, gettext("User does not have"
                            " permission to use this service grade (%s).\n"
                            "Job has not been queued.\n"
                            "Use (uuglist) to find which service grades"
                            " you can queue to.\n"), grade);
                        return(FAIL);
                }
        }

        (void) fclose(cfd);

        (void) fprintf(stderr, gettext(
            "Service grade (%s) does not exist on this machine."
            "  Job not queued.\n"
            "Use (uuglist) to find which service grades are available on"
            " this machine.\n"), grade);
        return(FAIL);
}

/*
 * wfremove - removes a C. file from the Workspace directory and all of its
 * D. files.
 */

void
wfremove(file)
char *file;
{
        FILE *fp;
        char line[BUFSIZ];
        char *carray[G_MAX];
        int na;
        struct stat s;

        fp = fopen(file, "r");

        if (fp == NULL) {
                toCorrupt(file);
                errent(Ct_OPEN, file, errno, __FILE__, __LINE__);
        }

        while (fgets(line, BUFSIZ, fp) != NULL) {
                na = getargs(line, carray, G_MAX);

                if (na < 6) {
                        (void) fclose(fp);
                        toCorrupt(file);
                        errent("BAD NUMBER OF ARGUMENTS", file, 0,
                                __FILE__, __LINE__);
                }

                if (*carray[TYPE] == 'R')
                        continue;

                /* remove D. file */

                DEBUG(4, "Removing data file (%s)\n", carray[FILE3]);

                if ((stat(carray[FILE3], &s) != -1) && (unlink(carray[FILE3]) != 0)) {
                        (void) fclose(fp);
                        toCorrupt(file);
                        toCorrupt(carray[FILE3]);
                        errent(Ct_UNLINK, carray[FILE3], errno, __FILE__,
                                __LINE__);
                }
        }

        (void) fclose(fp);

        DEBUG(4, "Removing work file (%s)\n", file);

        if (unlink(file) != 0) {
                toCorrupt(file);
                errent(Ct_UNLINK, file, errno, __FILE__, __LINE__);
        }
        return;
}

/*
 * findgrade - finds the highest priority job grade that is not locked
 * and that has jobs.
 *
 * job grade name is null, if no job grade is found.
 */

void
findgrade(dir, jobgrade)
char *dir, *jobgrade;
{
        char prevgrade[MAXBASENAME+1], curgrade[MAXBASENAME+1],
             gradedir[MAXBASENAME+1];
        char lockname[MAXFULLNAME];
        char Cfile[MAXBASENAME+1];
        DIR *p, *q;

        *prevgrade = NULLCHAR;
        p = opendir(dir);
        ASSERT(p != NULL, Ct_OPEN, dir, errno);

        while (gdirf(p, gradedir, dir) == TRUE) {
                (void) sprintf(lockname, "%s.%.*s.%s", LOCKPRE, SYSNSIZE,
                    Rmtname, gradedir);
                if (cklock(lockname) == FAIL)
                        continue;
                q = opendir(gradedir);
                ASSERT(q != NULL, Ct_OPEN, gradedir, errno);
                while (gnamef(q, Cfile) == TRUE) {
                        if (Cfile[0] == CMDPRE) {
                                if (*prevgrade == NULLCHAR) {
                                        (void) strcpy(prevgrade, gradedir);
                                        break;
                                }
                                (void) strcpy(curgrade, gradedir);
                                if (strcmp(curgrade, prevgrade) < 0)
                                        (void) strcpy(prevgrade, curgrade);
                        }
                }
                closedir(q);
        }
        closedir(p);
        (void) strncpy(jobgrade, prevgrade, MAXBASENAME);
        jobgrade[MAXBASENAME] = NULLCHAR;
        return;
}