/*
 * QUOTA    An implementation of the diskquota system for the LINUX
 *          operating system. QUOTA is implemented using the BSD systemcall
 *          interface as the means of communication with the user level.
 *          Should work for all filesystems because of integration into the
 *          VFS layer of the operating system.
 *          This is based on the Melbourne quota system wich uses both user and
 *          group quota files.
 *
 *          This part does the rpc-communication with the rquotad.
 *
 * Version: $Id: rquota_client.c,v 1.5 2000/09/05 18:47:15 mvw Exp mvw $
 *
 * Author:  Marco van Wieringen <mvw@planets.elm.net>
 *
 *          This program is free software; you can redistribute it and/or
 *          modify it under the terms of the GNU General Public License
 *          as published by the Free Software Foundation; either version
 *          2 of the License, or (at your option) any later version.
 */
#include <rpc/rpc.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/quota.h>
#include <errno.h>
#include <mntent.h>
#include <pwd.h>
#include <grp.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <rquota.h>

#define DEFAULT_QUOTA_BLOCKSIZE		1024

#if defined(RPC)
/*
 * Collect the requested quota information from a remote host.
 */
void rpc_rquota_get(char *fsname, int id, int type, struct dqblk *dq_dqb)
{
   CLIENT *clnt;
   getquota_rslt *result;
   union {
      getquota_args arg;
      ext_getquota_args ext_arg;
   } args;
   char *fsname_tmp, *host, *pathname;
   struct timeval timeout = { 2, 0};
   
   /*
    * Initialize with NULL.
    */
   memset(dq_dqb, 0, sizeof(struct dqblk));
   
   /*
    * Convert host:pathname to seperate host and pathname.
    */
   fsname_tmp = (char *)malloc(strlen(fsname) + 1);
   strcpy(fsname_tmp, fsname);
   host = fsname_tmp;
   
   /*
    * Strip off pathname on nfs mounted dir. Ignore entries of any
    * automounter.
    */
   if ((pathname = strchr(fsname_tmp, ':')) == (char *)0 || *(pathname + 1) == '(')
      return;

   *pathname++ = '\0';
   
   /*
    * First try EXT_RQUOTAPROG (Extended (LINUX) RPC quota program)
    */
   args.ext_arg.gqa_pathp = pathname;
   args.ext_arg.gqa_id = id;
   args.ext_arg.gqa_type = type;
   
   /*
    * Create a RPC client.
    */
   if ((clnt = clnt_create(host, RQUOTAPROG, EXT_RQUOTAVERS, "udp")) != NULL) {
      /*
       * Initialize unix authentication
       */
      clnt->cl_auth = authunix_create_default();

      /*
       * Setup protocol timeout.
       */
      clnt_control(clnt, CLSET_TIMEOUT, (caddr_t)&timeout);
      
      /*
       * Do RPC call and check result.
       */
      result = rquotaproc_getquota_2(&args.ext_arg, clnt);
      if (result != NULL && result->status == Q_OK) {
         memcpy((caddr_t *)dq_dqb, (caddr_t *)&result->getquota_rslt_u.gqr_rquota.rq_bhardlimit, sizeof(struct dqblk));

         /*
          * Convert the quota block units using the remote blocksize.
          */
         if (result->getquota_rslt_u.gqr_rquota.rq_bsize != DEFAULT_QUOTA_BLOCKSIZE) {
            int conversion_unit;

            conversion_unit = result->getquota_rslt_u.gqr_rquota.rq_bsize / DEFAULT_QUOTA_BLOCKSIZE;
            if (conversion_unit == 0) {
               conversion_unit = DEFAULT_QUOTA_BLOCKSIZE / result->getquota_rslt_u.gqr_rquota.rq_bsize;

               dq_dqb->dqb_bhardlimit = dq_dqb->dqb_bhardlimit / conversion_unit;
               dq_dqb->dqb_bsoftlimit = dq_dqb->dqb_bsoftlimit / conversion_unit;
               dq_dqb->dqb_curblocks = dq_dqb->dqb_curblocks / conversion_unit;
            } else {
               dq_dqb->dqb_bhardlimit = dq_dqb->dqb_bhardlimit * conversion_unit;
               dq_dqb->dqb_bsoftlimit = dq_dqb->dqb_bsoftlimit * conversion_unit;
               dq_dqb->dqb_curblocks = dq_dqb->dqb_curblocks * conversion_unit;
            }
         }
      }

      /*
       * Destroy unix authentication and RPC client structure.
       */
      auth_destroy(clnt->cl_auth);
      clnt_destroy(clnt);
   } else {
      result = NULL;
   }
   
   if (result == NULL || !result->status) {
      if (type == USRQUOTA) {
         /*
          * Try RQUOTAPROG because server doesn't seem to understand EXT_RQUOTAPROG. (NON-LINUX servers.)
          */
         args.arg.gqa_pathp = pathname;
         args.arg.gqa_uid = id;
         
         /*
          * Create a RPC client.
          */
         if ((clnt = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp")) != NULL) {
            /*
             * Initialize unix authentication
             */
            clnt->cl_auth = authunix_create_default();

            /*
             * Setup protocol timeout.
             */
            clnt_control(clnt, CLSET_TIMEOUT, (caddr_t)&timeout);
            
            /*
             * Do RPC call and check result.
             */
            result = rquotaproc_getquota_1(&args.arg, clnt);
            if (result != NULL && result->status == Q_OK) {
               /*
                * Copy the quota stats.
                */
               memcpy((caddr_t *)dq_dqb, (caddr_t *)&result->getquota_rslt_u.gqr_rquota.rq_bhardlimit, sizeof(struct dqblk));

               /*
                * Convert the quota block units using the remote blocksize.
                */
               if (result->getquota_rslt_u.gqr_rquota.rq_bsize != DEFAULT_QUOTA_BLOCKSIZE) {
                  int conversion_unit;

                  conversion_unit = result->getquota_rslt_u.gqr_rquota.rq_bsize / DEFAULT_QUOTA_BLOCKSIZE;
                  if (conversion_unit == 0) {
                     conversion_unit = DEFAULT_QUOTA_BLOCKSIZE / result->getquota_rslt_u.gqr_rquota.rq_bsize;

                     dq_dqb->dqb_bhardlimit = dq_dqb->dqb_bhardlimit / conversion_unit;
                     dq_dqb->dqb_bsoftlimit = dq_dqb->dqb_bsoftlimit / conversion_unit;
                     dq_dqb->dqb_curblocks = dq_dqb->dqb_curblocks / conversion_unit;
                  } else {
                     dq_dqb->dqb_bhardlimit = dq_dqb->dqb_bhardlimit * conversion_unit;
                     dq_dqb->dqb_bsoftlimit = dq_dqb->dqb_bsoftlimit * conversion_unit;
                     dq_dqb->dqb_curblocks = dq_dqb->dqb_curblocks * conversion_unit;
                  }
               }
            }

            /*
             * Destroy unix authentication and RPC client structure.
             */
            auth_destroy(clnt->cl_auth);
            clnt_destroy(clnt);
         }
      }
   }

   free(fsname_tmp);
}

/*
 * Set the requested quota information on a remote host.
 */
void rpc_rquota_set(int qcmd, char *fsname, int id, struct dqblk *dq_dqb, int type)
{
#if defined(RPC_SETQUOTA)
   CLIENT *clnt;
   setquota_rslt *result;
   union {
      setquota_args arg;
      ext_setquota_args ext_arg;
   } args;
   char *fsname_tmp, *host, *pathname;
   struct timeval timeout = { 2, 0};
   
   /*
    * Convert host:pathname to seperate host and pathname.
    */
   fsname_tmp = (char *) malloc(strlen(fsname) + 1);
   strcpy(fsname_tmp, fsname);
   host = fsname_tmp;
   
   /*
    * Strip off pathname on nfs mounted dir. Ignore entries of any
    * automounter.
    */
   if ((pathname = strchr(fsname_tmp, ':')) == (char *)0 || *(pathname + 1) == '(')
      return;

   *pathname++ = '\0';
   
   /*
    * First try EXT_RQUOTAPROG (Extended (LINUX) RPC quota program)
    */
   args.ext_arg.sqa_qcmd = qcmd;
   args.ext_arg.sqa_pathp = pathname;
   args.ext_arg.sqa_id = id;
   args.ext_arg.sqa_type = type;
   memcpy((caddr_t *)&args.ext_arg.sqa_dqblk.rq_bhardlimit,(caddr_t *)dq_dqb,sizeof(struct dqblk));
   
   if ((clnt = clnt_create(host, RQUOTAPROG, EXT_RQUOTAVERS, "udp")) != NULL) {
      /*
       * Initialize unix authentication
       */
      clnt->cl_auth = authunix_create_default();

      /*
       * Setup protocol timeout.
       */
      clnt_control(clnt, CLSET_TIMEOUT, (caddr_t)&timeout);
      
      /*
       * Do RPC call and check result.
       */
      result = rquotaproc_setquota_2(&args.ext_arg, clnt);
      if (result != NULL && result->status == Q_OK) {
         memcpy((caddr_t *)dq_dqb, (caddr_t *)&result->setquota_rslt_u.sqr_rquota.rq_bhardlimit, sizeof(struct dqblk));
      }

      /*
       * Destroy unix authentication and RPC client structure.
       */
      auth_destroy(clnt->cl_auth);
      clnt_destroy(clnt);
   } else
      result = NULL;
   
   if (result == NULL || !result->status) {
      if (type == USRQUOTA) {
         /*
          * Try RQUOTAPROG because server doesn't seem to understand EXT_RQUOTAPROG. (NON-LINUX servers.)
          */
         args.arg.sqa_qcmd = qcmd;
         args.arg.sqa_pathp = pathname;
         args.arg.sqa_id = id;
         memcpy((caddr_t *)&args.arg.sqa_dqblk.rq_bhardlimit,(caddr_t *)dq_dqb,sizeof(struct dqblk));
         
         /*
          * Create a RPC client.
          */
         if ((clnt = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp")) != NULL) {
            /*
             * Initialize unix authentication
             */
            clnt->cl_auth = authunix_create_default();

            /*
             * Setup protocol timeout.
             */
            clnt_control(clnt, CLSET_TIMEOUT, (caddr_t)&timeout);
            
            /*
             * Do RPC call and check result.
             */
            result = rquotaproc_setquota_1(&args.arg, clnt);
            if (result != NULL && result->status == Q_OK) {
               memcpy((caddr_t *)dq_dqb, (caddr_t *)&result->setquota_rslt_u.sqr_rquota.rq_bhardlimit, sizeof(struct dqblk));
            }

            /*
             * Destroy unix authentication and RPC client structure.
             */
            auth_destroy(clnt->cl_auth);
            clnt_destroy(clnt);
         }
      }
   }
   free(fsname_tmp);
#endif
}
#endif
