/*
  Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com>
  This file is part of GlusterFS.

  This file is licensed to you under your choice of the GNU Lesser
  General Public License, version 3 or any later version (LGPLv3 or
  later), or the GNU General Public License, version 2 (GPLv2), in all
  cases as published by the Free Software Foundation.
*/

#ifndef _RPCSVC_H
#define _RPCSVC_H

#include <glusterfs/gf-event.h>
#include "rpc-transport.h"
#include <glusterfs/dict.h>
#include "xdr-rpc.h"
#include "rpcsvc-common.h"

#include <pthread.h>
#include <sys/uio.h>
#include <inttypes.h>
#include <glusterfs/compat.h>
#include <glusterfs/client_t.h>

/* TODO: we should store prognums at a centralized location to avoid conflict
         or use a robust random number generator to avoid conflicts
*/

#define RPCSVC_INFRA_PROGRAM 7712846 /* random number */

typedef enum {
    RPCSVC_PROC_EVENT_THREAD_DEATH = 0,
} rpcsvc_infra_procnum_t;

#define RPCSVC_DEFAULT_OUTSTANDING_RPC_LIMIT                                   \
    64 /* Default for protocol/server */
#ifdef BUILD_GNFS
#define RPCSVC_DEF_NFS_OUTSTANDING_RPC_LIMIT 16 /* Default for nfs/server */
#endif
#define RPCSVC_MAX_OUTSTANDING_RPC_LIMIT 65536
#define RPCSVC_MIN_OUTSTANDING_RPC_LIMIT 0 /* No limit i.e. Unlimited */

#define GF_RPCSVC "rpc-service"

#define RPCSVC_DEFAULT_MEMFACTOR 8
#define RPCSVC_POOLCOUNT_MULT 64
#define RPC_ROOT_UID 0
#define RPC_ROOT_GID 0
#define RPC_NOBODY_UID 65534
#define RPC_NOBODY_GID 65534

#if 0
#error "defined in /usr/include/rpc/auth.h"

#define AUTH_NONE 0 /* no authentication */
#define AUTH_NULL 0 /* backward compatibility */
#define AUTH_SYS 1  /* unix style (uid, gids) */
#define AUTH_UNIX AUTH_SYS
#define AUTH_SHORT 2     /* short hand unix style */
#define AUTH_DES 3       /* des style (encrypted timestamps) */
#define AUTH_DH AUTH_DES /* Diffie-Hellman (this is DES) */
#define AUTH_KERB 4      /* kerberos style */
#endif                   /* */

typedef struct rpcsvc_program rpcsvc_program_t;

struct rpcsvc_notify_wrapper {
    struct list_head list;
    void *data;
    rpcsvc_notify_t notify;
};
typedef struct rpcsvc_notify_wrapper rpcsvc_notify_wrapper_t;

typedef struct rpcsvc_request rpcsvc_request_t;

typedef struct {
    rpc_transport_t *trans;
    rpcsvc_t *svc;
    /* FIXME: remove address from this structure. Instead use get_myaddr
     * interface implemented by individual transports.
     */
    struct sockaddr_storage sa;
    struct list_head list;
} rpcsvc_listener_t;

#define rpcsvc_auth_flavour(au) ((au).flavour)

typedef struct drc_client drc_client_t;
typedef struct drc_cached_op drc_cached_op_t;

/* The container for the RPC call handed up to an actor.
 * Dynamically allocated. Lives till the call reply is completely
 * transmitted.
 * */
struct rpcsvc_request {
    /* connection over which this request came. */
    rpc_transport_t *trans;

    rpcsvc_t *svc;

    rpcsvc_program_t *prog;

    int prognum;

    int progver;

    int procnum;

    /* Uid and gid filled by the rpc-auth module during the authentication
     * phase.
     */
    uid_t uid;
    gid_t gid;
    pid_t pid;

    gf_lkowner_t lk_owner;

    /* The identifier for the call from client.
     * Needed to pair the reply with the call.
     */
    uint32_t xid;

    /* Might want to move this to AUTH_UNIX specific state since this array
     * is not available for every authentication scheme.
     */
    gid_t *auxgids;
    gid_t auxgidsmall[SMALL_GROUP_COUNT];
    gid_t *auxgidlarge;
    int auxgidcount;

    /* The RPC message payload, contains the data required
     * by the program actors. This is the buffer that will need to
     * be de-xdred by the actor.
     */
    int count;
    struct iovec msg[MAX_IOVEC];

    struct iobref *iobref;

    /* While the reply record is being built, this variable keeps track
     * of how many bytes have been added to the record.
     */
    size_t payloadsize;

    /* The credentials extracted from the rpc request */
    client_auth_data_t cred;

    /* The verified extracted from the rpc request. In request side
     * processing this contains the verifier sent by the client, on reply
     * side processing, it is filled with the verified that will be
     * sent to the client.
     */
    client_auth_data_t verf;

#if defined(BUILD_GNFS)
    /* Container for a RPC program wanting to store a temp
     * request-specific item. Currently this is used only
     * by the legacy gnfs server xlator.
     */
    void *private;
#endif /* BUILD_GNFS */

    /* Container for transport to store request-specific item */
    void *trans_private;

    /* pointer to cached reply for use in DRC */
    drc_cached_op_t *reply;

    /* request queue in rpcsvc */
    struct list_head request_list;

    /* Status of the RPC call, whether it was accepted or denied. */
    int rpc_status;

    /* In case, the call was denied, the RPC error is stored here
     * till the reply is sent.
     */
    int rpc_err;

    /* In case the failure happened because of an authentication problem
     * , this value needs to be assigned the correct auth error number.
     */
    int auth_err;

    /* Things passed to rpc layer from client */

    /* @flags: Can be used for binary data passed in xdata to be
       passed here instead */
    unsigned int flags;

    /* ctime: origin of time on the client side, ideally this is
       the one we should consider for time */
    struct timespec ctime;

    /* If latency measurement is enabled, marks the request handling
     * start time.
     */
    struct timespec begin;

    /* Execute this request's actor function in ownthread of program?*/
    gf_boolean_t ownthread;

    gf_boolean_t synctask;
};

#define rpcsvc_request_program(req) ((rpcsvc_program_t *)((req)->prog))
#define rpcsvc_request_procnum(req) (((req)->procnum))
#define rpcsvc_request_program_private(req)                                    \
    (((rpcsvc_program_t *)((req)->prog))->private)
#define rpcsvc_request_accepted(req) ((req)->rpc_status == MSG_ACCEPTED)
#define rpcsvc_request_accepted_success(req) ((req)->rpc_err == SUCCESS)
#define rpcsvc_request_prog_minauth(req) (rpcsvc_request_program(req)->min_auth)
#define rpcsvc_request_cred_flavour(req) (rpcsvc_auth_flavour(req->cred))
#define rpcsvc_request_verf_flavour(req) (rpcsvc_auth_flavour(req->verf))
#define rpcsvc_request_service(req) ((req)->svc)
#define rpcsvc_request_uid(req) ((req)->uid)
#define rpcsvc_request_gid(req) ((req)->gid)
#define rpcsvc_request_private(req) ((req)->private)
#define rpcsvc_request_xid(req) ((req)->xid)
#define rpcsvc_request_set_private(req, prv) (req)->private = (void *)(prv)
#define rpcsvc_request_iobref_ref(req) (iobref_ref((req)->iobref))
#define rpcsvc_request_record_ref(req) (iobuf_ref((req)->recordiob))
#define rpcsvc_request_record_unref(req) (iobuf_unref((req)->recordiob))
#define rpcsvc_request_record_iob(req) ((req)->recordiob)
#define rpcsvc_request_set_vecstate(req, state) ((req)->vecstate = state)
#define rpcsvc_request_vecstate(req) ((req)->vecstate)
#define rpcsvc_request_transport(req) ((req)->trans)
#define rpcsvc_request_transport_ref(req) (rpc_transport_ref((req)->trans))
#define RPC_AUTH_ROOT_SQUASH(req)                                              \
    do {                                                                       \
        int gidcount = 0;                                                      \
        if (req->svc->root_squash) {                                           \
            if (req->uid == RPC_ROOT_UID)                                      \
                req->uid = req->svc->anonuid;                                  \
            if (req->gid == RPC_ROOT_GID)                                      \
                req->gid = req->svc->anongid;                                  \
                                                                               \
            for (gidcount = 0; gidcount < req->auxgidcount; ++gidcount) {      \
                if (!req->auxgids[gidcount])                                   \
                    req->auxgids[gidcount] = req->svc->anongid;                \
            }                                                                  \
        }                                                                      \
    } while (0);

#define RPC_AUTH_ALL_SQUASH(req)                                               \
    do {                                                                       \
        int gidcount = 0;                                                      \
        if (req->svc->all_squash) {                                            \
            req->uid = req->svc->anonuid;                                      \
            req->gid = req->svc->anongid;                                      \
                                                                               \
            for (gidcount = 0; gidcount < req->auxgidcount; ++gidcount) {      \
                if (!req->auxgids[gidcount])                                   \
                    req->auxgids[gidcount] = req->svc->anongid;                \
            }                                                                  \
        }                                                                      \
    } while (0);

#define RPCSVC_ACTOR_SUCCESS 0
#define RPCSVC_ACTOR_ERROR (-1)
#define RPCSVC_ACTOR_IGNORE (-2)

/* Functor for every type of protocol actor
 * must be defined like this.
 *
 * See the request structure for info on how to handle the request
 * in the program actor.
 *
 * On successful santify checks inside the actor, it should return
 * RPCSVC_ACTOR_SUCCESS.
 * On an error, on which the RPC layer is expected to return a reply, the actor
 * should return RPCSVC_ACTOR_ERROR.
 *
 */
typedef int (*rpcsvc_actor)(rpcsvc_request_t *req);
typedef int (*rpcsvc_vector_sizer)(int state, ssize_t *readsize,
                                   char *base_addr, char *curr_addr);

/* Every protocol actor will also need to specify the function the RPC layer
 * will use to serialize or encode the message into XDR format just before
 * transmitting on the connection.
 */
typedef void *(*rpcsvc_encode_reply)(void *msg);

/* Once the reply has been transmitted, the message will have to be de-allocated
 * , so every actor will need to provide a function that deallocates the message
 * it had allocated as a response.
 */
typedef void (*rpcsvc_deallocate_reply)(void *msg);

#define RPCSVC_NAME_MAX 32
/* The descriptor for each procedure/actor that runs
 * over the RPC service.
 */
typedef struct rpcsvc_actor_desc {
    char procname[RPCSVC_NAME_MAX];
    rpcsvc_actor actor;

    /* Handler for cases where the RPC requests fragments are large enough
     * to benefit from being decoded into aligned memory addresses. While
     * decoding the request in a non-vectored manner, due to the nature of
     * the XDR scheme, RPC cannot guarantee memory aligned addresses for
     * the resulting message-specific structures. Allowing a specialized
     * handler for letting the RPC program read the data from the network
     * directly into its aligned buffers.
     */
    rpcsvc_vector_sizer vector_sizer;

    int procnum;

    /* Can actor be ran on behalf an unprivileged requestor? */
    drc_op_type_t op_type;
    gf_boolean_t unprivileged;
} rpcsvc_actor_t;

typedef struct rpcsvc_request_queue {
    struct list_head request_queue;
    pthread_mutex_t queue_lock;
    pthread_cond_t queue_cond;
    pthread_t thread;
    struct rpcsvc_program *program;
    int gen;
    gf_boolean_t waiting;
} rpcsvc_request_queue_t;

/* Describes a program and its version along with the function pointers
 * required to handle the procedures/actors of each program/version.
 * Never changed ever by any thread so no need for a lock.
 */
struct rpcsvc_program {
    char progname[RPCSVC_NAME_MAX];
    int prognum;
    int progver;
    /* FIXME */
    dict_t *options; /* An opaque dictionary
                      * populated by the program
                      * (probably from xl->options)
                      * which contain enough
                      * information for transport to
                      * initialize. As a part of
                      * cleanup, the members of
                      * options which are of interest
                      * to transport should be put
                      * into a structure for better
                      * readability and structure
                      * should replace options member
                      * here.
                      */
#if 0
        int                     progaddrfamily; /* AF_INET or AF_INET6 */
        char                    *proghost;      /* Bind host, can be NULL */
#endif
    rpcsvc_actor_t *actors; /* All procedure handlers */
    int numactors;          /* Num actors in actor array */
    int proghighvers;       /* Highest ver for program
                               supported by the system. */
    /* Program specific state handed to actors */
    void *private;
    gf_latency_t *latencies; /*Tracks latency statistics for the rpc call*/

    /* This upcall is provided by the program during registration.
     * It is used to notify the program about events like connection being
     * destroyed etc. The rpc program may take appropriate actions, for eg.,
     * in the case of connection being destroyed, it should cleanup its
     * state stored in the connection.
     */
    rpcsvc_notify_t notify;

    int proglowvers; /* Lowest ver */

    /* An integer that identifies the min auth strength that is required
     * by this protocol, for eg. MOUNT3 needs AUTH_UNIX at least.
     * See RFC 1813, Section 5.2.1.
     */
    int min_auth;

    /* list member to link to list of registered services with rpcsvc */
    struct list_head program;
    rpcsvc_request_queue_t request_queue[EVENT_MAX_THREADS];
    pthread_mutex_t thr_lock;
    int threadcount;
    int thr_queue;
    pthread_key_t req_queue_key;

    /* eventthreadcount is just a readonly copy of the actual value
     * owned by the event sub-system
     * It is used to control the scaling of rpcsvc_request_handler threads
     */
    int eventthreadcount;
    uint16_t progport; /* Registered with portmap */
    /* Execute actor function in program's own thread? This will reduce */
    /* the workload on poller threads */
    gf_boolean_t ownthread;
    gf_boolean_t alive;

    gf_boolean_t synctask;
    unsigned long request_queue_status[EVENT_MAX_THREADS / __BITS_PER_LONG];
};

typedef struct rpcsvc_cbk_program {
    char *progname;
    int prognum;
    int progver;
} rpcsvc_cbk_program_t;
/* All users of RPC services should use this API to register their
 * procedure handlers.
 */
extern int
rpcsvc_program_register(rpcsvc_t *svc, rpcsvc_program_t *program,
                        gf_boolean_t add_to_head);

extern int
rpcsvc_program_unregister(rpcsvc_t *svc, rpcsvc_program_t *program);

/* This will create and add a listener to listener pool. Programs can
 * use any of the listener in this pool. A single listener can be used by
 * multiple programs and vice versa. There can also be a one to one mapping
 * between a program and a listener. After registering a program with rpcsvc,
 * the program has to be associated with a listener using
 * rpcsvc_program_register_portmap.
 */
/* FIXME: can multiple programs registered on same port? */
extern int32_t
rpcsvc_create_listeners(rpcsvc_t *svc, dict_t *options, char *name);

void
rpcsvc_listener_destroy(rpcsvc_listener_t *listener);

extern int
rpcsvc_program_register_portmap(rpcsvc_program_t *newprog, uint32_t port);

#ifdef IPV6_DEFAULT
extern int
rpcsvc_program_register_rpcbind6(rpcsvc_program_t *newprog, uint32_t port);
extern int
rpcsvc_program_unregister_rpcbind6(rpcsvc_program_t *newprog);
#endif

extern int
rpcsvc_program_unregister_portmap(rpcsvc_program_t *newprog);

extern int
rpcsvc_register_portmap_enabled(rpcsvc_t *svc);

/* Inits the global RPC service data structures.
 * Called in main.
 */
extern rpcsvc_t *
rpcsvc_init(xlator_t *xl, glusterfs_ctx_t *ctx, dict_t *options,
            uint32_t poolcount);

extern int
rpcsvc_reconfigure_options(rpcsvc_t *svc, dict_t *options);

int
rpcsvc_register_notify(rpcsvc_t *svc, rpcsvc_notify_t notify, void *mydata);

/* unregister a notification callback @notify with data @mydata from svc.
 * returns the number of notification callbacks unregistered.
 */
int
rpcsvc_unregister_notify(rpcsvc_t *svc, rpcsvc_notify_t notify, void *mydata);

int
rpcsvc_transport_submit(rpc_transport_t *trans, struct iovec *rpchdr,
                        int rpchdrcount, struct iovec *proghdr,
                        int proghdrcount, struct iovec *progpayload,
                        int progpayloadcount, struct iobref *iobref,
                        void *priv);

int
rpcsvc_submit_message(rpcsvc_request_t *req, struct iovec *proghdr,
                      int hdrcount, struct iovec *payload, int payloadcount,
                      struct iobref *iobref);

int
rpcsvc_submit_generic(rpcsvc_request_t *req, struct iovec *proghdr,
                      int hdrcount, struct iovec *payload, int payloadcount,
                      struct iobref *iobref);

extern int
rpcsvc_error_reply(rpcsvc_request_t *req);

#define RPCSVC_PEER_STRLEN 1024
#define RPCSVC_AUTH_ACCEPT 1
#define RPCSVC_AUTH_REJECT 2
#define RPCSVC_AUTH_DONTCARE 3

extern int
rpcsvc_transport_peername(rpc_transport_t *trans, char *hostname, int hostlen);

extern int
rpcsvc_transport_peeraddr(rpc_transport_t *trans, char *addrstr, int addrlen,
                          struct sockaddr_storage *returnsa, socklen_t sasize);

extern int
rpcsvc_auth_check(rpcsvc_t *svc, char *volname, char *ipaddr);

extern int
rpcsvc_transport_privport_check(rpcsvc_t *svc, char *volname, uint16_t port);

#define rpcsvc_request_seterr(req, err) ((req)->rpc_err = (int)(err))
#define rpcsvc_request_set_autherr(req, err) ((req)->auth_err = (int)(err))

typedef int (*auth_init_trans)(rpc_transport_t *trans, void *priv);
typedef int (*auth_init_request)(rpcsvc_request_t *req, void *priv);
typedef int (*auth_request_authenticate)(rpcsvc_request_t *req, void *priv);

/* This structure needs to be registered by every authentication scheme.
 * Our authentication schemes are stored per connection because
 * each connection will end up using a different authentication scheme.
 */
typedef struct rpcsvc_auth_ops {
    auth_init_trans transport_init;
    auth_init_request request_init;
    auth_request_authenticate authenticate;
} rpcsvc_auth_ops_t;

typedef struct rpcsvc_auth_flavour_desc {
    char authname[RPCSVC_NAME_MAX];
    rpcsvc_auth_ops_t *authops;
    void *authprivate;
    int authnum;
} rpcsvc_auth_t;

typedef void *(*rpcsvc_auth_initer_t)(rpcsvc_t *svc, dict_t *options);

struct rpcsvc_auth_list {
    struct list_head authlist;
    rpcsvc_auth_initer_t init;
    /* Should be the name with which we identify the auth scheme given
     * in the volfile options.
     * This should be different from the authname in rpc_auth_t
     * in way that makes it easier to specify this scheme in the volfile.
     * This is because the technical names of the schemes can be a bit
     * arcane.
     */
    char name[RPCSVC_NAME_MAX];
    rpcsvc_auth_t *auth;
    int enable;
};

extern int
rpcsvc_auth_request_init(rpcsvc_request_t *req, struct rpc_msg *callmsg);

extern int
rpcsvc_auth_init(rpcsvc_t *svc, dict_t *options);

extern int
rpcsvc_auth_reconf(rpcsvc_t *svc, dict_t *options);

extern int
rpcsvc_authenticate(rpcsvc_request_t *req);

extern int
rpcsvc_auth_array(rpcsvc_t *svc, char *volname, int *autharr, int arrlen);

/* If the request has been sent using AUTH_UNIX, this function returns the
 * auxiliary gids as an array, otherwise, it returns NULL.
 * Move to auth-unix specific source file when we need to modularize the
 * authentication code even further to support mode auth schemes.
 */
extern gid_t *
rpcsvc_auth_unix_auxgids(rpcsvc_request_t *req, int *arrlen);

extern char *
rpcsvc_volume_allowed(dict_t *options, char *volname);

int
rpcsvc_request_submit(rpcsvc_t *rpc, rpc_transport_t *trans,
                      rpcsvc_cbk_program_t *prog, int procnum, void *req,
                      glusterfs_ctx_t *ctx, xdrproc_t xdrproc);

int
rpcsvc_callback_submit(rpcsvc_t *rpc, rpc_transport_t *trans,
                       rpcsvc_cbk_program_t *prog, int procnum,
                       struct iovec *proghdr, int proghdrcount,
                       struct iobref *iobref);

rpcsvc_actor_t *
rpcsvc_program_actor(rpcsvc_request_t *req);

int
rpcsvc_transport_unix_options_build(dict_t *options, char *filepath);
int
rpcsvc_set_allow_insecure(rpcsvc_t *svc, dict_t *options);
int
rpcsvc_set_addr_namelookup(rpcsvc_t *svc, dict_t *options);
int
rpcsvc_set_root_squash(rpcsvc_t *svc, dict_t *options);
int
rpcsvc_set_all_squash(rpcsvc_t *svc, dict_t *options);
int
rpcsvc_set_outstanding_rpc_limit(rpcsvc_t *svc, dict_t *options, int defvalue);

int
rpcsvc_set_throttle_on(rpcsvc_t *svc);

int
rpcsvc_set_throttle_off(rpcsvc_t *svc);

gf_boolean_t
rpcsvc_get_throttle(rpcsvc_t *svc);

int
rpcsvc_auth_array(rpcsvc_t *svc, char *volname, int *autharr, int arrlen);
rpcsvc_vector_sizer
rpcsvc_get_program_vector_sizer(rpcsvc_t *svc, uint32_t prognum,
                                uint32_t progver, int procnum);
void
rpcsvc_autoscale_threads(glusterfs_ctx_t *ctx, rpcsvc_t *rpc, int incr);

extern int
rpcsvc_destroy(rpcsvc_t *svc);
void
rpcsvc_statedump(rpcsvc_t *svc);
#endif
