/*
  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 _COMMON_UTILS_H
#define _COMMON_UTILS_H

#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <pthread.h>
#include <dirent.h>
#include <unistd.h>
#ifndef GF_BSD_HOST_OS
#include <alloca.h>
#endif
#include <limits.h>
#include <fnmatch.h>
#include <uuid/uuid.h>
#include <urcu/compiler.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/uio.h>

/* FreeBSD, etc. */
#ifndef __BITS_PER_LONG
#define __BITS_PER_LONG (CHAR_BIT * (sizeof(long)))
#endif

#ifndef ffsll
#define ffsll(x) __builtin_ffsll(x)
#endif

void
trap(void);

#define GF_UNIVERSAL_ANSWER 42 /* :O */

/* To solve type punned error */
#define VOID(ptr) ((void **)((void *)ptr))

#include "glusterfs/mem-pool.h"
#include "glusterfs/compat-uuid.h"
#include "glusterfs/iatt.h"
#include "glusterfs/libglusterfs-messages.h"

#define STRINGIFY(val) #val
#define TOSTRING(val) STRINGIFY(val)

#define alloca0(size)                                                          \
    ({                                                                         \
        void *__ptr;                                                           \
        __ptr = alloca(size);                                                  \
        memset(__ptr, 0, size);                                                \
        __ptr;                                                                 \
    })

#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
#define gf_roof(a, b) ((((a) + (b)-1) / ((b != 0) ? (b) : 1)) * (b))
#define gf_floor(a, b) (((a) / ((b != 0) ? (b) : 1)) * (b))

#define IPv4_ADDR_SIZE 32

#define GF_UNIT_KB 1024ULL
#define GF_UNIT_MB 1048576ULL
#define GF_UNIT_GB 1073741824ULL
#define GF_UNIT_TB 1099511627776ULL
#define GF_UNIT_PB 1125899906842624ULL

#define GF_UNIT_B_STRING "B"
#define GF_UNIT_KB_STRING "KB"
#define GF_UNIT_MB_STRING "MB"
#define GF_UNIT_GB_STRING "GB"
#define GF_UNIT_TB_STRING "TB"
#define GF_UNIT_PB_STRING "PB"

#define GF_UNIT_PERCENT_STRING "%"

#define GEOREP "geo-replication"
#define GLUSTERD_NAME "glusterd"

#define GF_SELINUX_XATTR_KEY "security.selinux"

#define WIPE(statp)                                                            \
    do {                                                                       \
        typeof(*statp) z = {                                                   \
            0,                                                                 \
        };                                                                     \
        if (statp)                                                             \
            *statp = z;                                                        \
    } while (0)

/* process mode definitions */
#define GF_SERVER_PROCESS 0
#define GF_CLIENT_PROCESS 1
#define GF_GLUSTERD_PROCESS 2

/* Defining this here as it is needed by glusterd for setting
 * nfs port in volume status.
 */
#define GF_NFS3_PORT 2049

#define GF_CLIENT_PORT_CEILING 1024
#define GF_IANA_PRIV_PORTS_START 49152 /* RFC 6335 */
#define GF_CLNT_INSECURE_PORT_CEILING (GF_IANA_PRIV_PORTS_START - 1)
#define GF_PORT_MAX 65535
#define GF_PORT_ARRAY_SIZE ((GF_PORT_MAX + 7) / 8)
#define GF_LOCK_TIMER 180
#define GF_MINUTE_IN_SECONDS 60
#define GF_HOUR_IN_SECONDS (60 * 60)
#define GF_DAY_IN_SECONDS (24 * 60 * 60)
#define GF_WEEK_IN_SECONDS (7 * 24 * 60 * 60)
#define GF_SEC_IN_NS 1000000000
#define GF_MS_IN_NS 1000000
#define GF_US_IN_NS 1000

/* Default timeout for both barrier and changelog translator, in seconds. */
#define BARRIER_TIMEOUT 120

/* Default signing waiting time to sign a file for bitrot, in seconds. */
#define SIGNING_TIMEOUT 120

/* Threading parameters for bitrot. */
#define BR_DEFAULT_THREADS 4

/* Threading parameters for parallel heal, both for AFR and EC xlators. */
#define SHD_MIN_THREADS 1
#define SHD_MAX_THREADS 64
#define SHD_DEFAULT_THREADS SHD_MIN_THREADS

/* xxhash */
#define GF_XXH64_DIGEST_LENGTH 8
#define GF_XXHSUM64_DEFAULT_SEED 0

/* Shard */
#define GF_XATTR_SHARD_FILE_SIZE "trusted.glusterfs.shard.file-size"
#define SHARD_ROOT_GFID "be318638-e8a0-4c6d-977d-7a937aa84806"
#define DOT_SHARD_REMOVE_ME_GFID "77dd5a45-dbf5-4592-b31b-b440382302e9"

/* Lease: buffer length for stringified lease id
 * Format: 4hexnum-4hexnum-4hexnum-4hexnum-4hexnum-4hexnum-4hexnum-4hexnum
 * Eg:6c69-6431-2d63-6c6e-7431-0000-0000-0000
 */
#define GF_LEASE_ID_BUF_SIZE ((LEASE_ID_SIZE * 2) + (LEASE_ID_SIZE / 2))

/* pthread related */
/* as per the man page, thread-name should be at max 16 bytes */
/* with prefix of 'glfs_' (5), we are left with 11 more bytes */
#define GF_THREAD_NAME_LIMIT 16
#define GF_THREAD_NAME_PREFIX "glfs_"

/* Advisory buffer size for formatted timestamps (see gf_time_fmt) */
#define GF_TIMESTR_SIZE 256

/* Allow empty relative pathname */
#define AT_EMPTY_PATH 0x1000

/*
 * we could have initialized these as +ve values and treated
 * them as negative while comparing etc.. (which would have
 * saved us with the pain of assigning values), but since we
 * only have a few clients that use this feature, it's okay.
 */
enum _gf_special_pid {
    GF_CLIENT_PID_MAX = 0,
    GF_CLIENT_PID_GSYNCD = -1,
    GF_CLIENT_PID_HADOOP = -2,
    GF_CLIENT_PID_DEFRAG = -3,
    GF_CLIENT_PID_NO_ROOT_SQUASH = -4,
    GF_CLIENT_PID_QUOTA_MOUNT = -5,
    GF_CLIENT_PID_SELF_HEALD = -6,
    GF_CLIENT_PID_GLFS_HEAL = -7,
    GF_CLIENT_PID_BITD = -8,
    GF_CLIENT_PID_SCRUB = -9,
    GF_CLIENT_PID_TIER_DEFRAG = -10,
    GF_SERVER_PID_TRASH = -11,
    GF_CLIENT_PID_ADD_REPLICA_MOUNT = -12,
    GF_CLIENT_PID_SET_UTIME = -13,
    GF_CLIENT_PID_QUOTA_HELPER = -14,
};

enum _gf_xlator_ipc_targets {
    GF_IPC_TARGET_CHANGELOG = 0,
    GF_IPC_TARGET_CTR = 1,
    GF_IPC_TARGET_UPCALL = 2
};

typedef enum _gf_special_pid gf_special_pid_t;
typedef enum _gf_xlator_ipc_targets _gf_xlator_ipc_targets_t;

/* Array to hold custom xattr keys */
extern char *xattrs_to_heal[];

char **
get_xattrs_to_heal(void);

char *
gf_gethostname(void);

#ifndef GF_DARWIN_HOST_OS

void
gf_set_nofile(rlim_t high, rlim_t low);

#else /* Darwin */

#define gf_set_nofile(low, high)                                               \
    do {                                                                       \
    } while (0)

#endif /* not GF_DARWIN_HOST_OS */

/* The DHT file rename operation is not a straightforward rename.
 * It involves creating linkto and linkfiles, and can unlink or rename the
 * source file depending on the hashed and cached subvols for the source
 * and target files. this makes it difficult for geo-rep to figure out that
 * a rename operation has taken place.
 *
 * We now send a special key and the values of the source and target pargfids
 * and basenames to indicate to changelog that the operation in question
 * should be treated as a rename. We are explicitly filling and sending this
 * as a binary value in the dictionary as the unlink op will not have the
 * source file information. The lengths of the src and target basenames
 * are used to calculate where to start reading the names in the structure.
 * XFS allows a max of 255 chars for filenames but other file systems might
 * not have such restrictions
 */
typedef struct dht_changelog_rename_info {
    uuid_t old_pargfid;
    uuid_t new_pargfid;
    int32_t oldname_len;
    int32_t newname_len;
    char buffer[1];
} dht_changelog_rename_info_t;

typedef int (*gf_cmp)(void *, void *);

struct _dict;

struct dnscache {
    struct _dict *cache_dict;
    time_t ttl;
};

struct dnscache_entry {
    char *ip;
    char *fqdn;
    time_t timestamp;
};

struct dnscache6 {
    struct addrinfo *first;
    struct addrinfo *next;
};

struct list_node {
    void *ptr;
    struct list_head list;
};

extern char *vol_type_str[];

void
gf_log_dump_graph(FILE *specfp, glusterfs_graph_t *graph);
void
gf_print_trace(int32_t signal, glusterfs_ctx_t *ctx);
int
gf_set_log_file_path(cmd_args_t *cmd_args, glusterfs_ctx_t *ctx);

int
gf_process_getspec_servers_list(cmd_args_t *cmd_args, const char *servers_list);
int
gf_set_volfile_server_common(cmd_args_t *cmd_args, const char *host,
                             const char *transport, int port);

static inline void
BIT_SET(unsigned char *array, unsigned int index)
{
    unsigned int offset = index / 8;
    unsigned int shift = index % 8;

    array[offset] |= (1 << shift);
}

static inline void
BIT_CLEAR(unsigned char *array, unsigned int index)
{
    unsigned int offset = index / 8;
    unsigned int shift = index % 8;

    array[offset] &= ~(1 << shift);
}

static inline unsigned int
BIT_VALUE(unsigned char *array, unsigned int index)
{
    unsigned int offset = index / 8;
    unsigned int shift = index % 8;

    return (array[offset] >> shift) & 0x1;
}

#define VECTORSIZE(count) (count * (sizeof(struct iovec)))

#define STRLEN_0(str) (strlen(str) + 1)

#define VALIDATE_OR_GOTO(arg, label)                                           \
    do {                                                                       \
        if (caa_unlikely(!arg)) {                                              \
            errno = EINVAL;                                                    \
            gf_msg_callingfn((this ? (this->name) : "(Govinda! Govinda!)"),    \
                             GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,       \
                             "invalid argument: " #arg);                       \
            goto label;                                                        \
        }                                                                      \
    } while (0)

#define GF_VALIDATE_OR_GOTO(name, arg, label)                                  \
    do {                                                                       \
        if (caa_unlikely(!arg)) {                                              \
            errno = EINVAL;                                                    \
            gf_msg_callingfn(name, GF_LOG_ERROR, errno, LG_MSG_INVALID_ARG,    \
                             "invalid argument: " #arg);                       \
            goto label;                                                        \
        }                                                                      \
    } while (0)

#define GF_VALIDATE_OR_GOTO_WITH_ERROR(name, arg, label, op_errno, error)      \
    do {                                                                       \
        if (caa_unlikely(!arg)) {                                              \
            op_errno = error;                                                  \
            gf_msg_callingfn(name, GF_LOG_ERROR, EINVAL, LG_MSG_INVALID_ARG,   \
                             "invalid argument: " #arg);                       \
            goto label;                                                        \
        }                                                                      \
    } while (0)

#define GF_CHECK_ALLOC(arg, retval, label)                                     \
    do {                                                                       \
        if (caa_unlikely(!(arg))) {                                            \
            retval = -ENOMEM;                                                  \
            goto label;                                                        \
        }                                                                      \
    } while (0)

#define GF_CHECK_ALLOC_AND_LOG(name, item, retval, msg, errlabel)              \
    do {                                                                       \
        if (!(item)) {                                                         \
            (retval) = -ENOMEM;                                                \
            gf_msg(name, GF_LOG_CRITICAL, ENOMEM, LG_MSG_NO_MEMORY, (msg));    \
            goto errlabel;                                                     \
        }                                                                      \
    } while (0)

#define GF_ASSERT_AND_GOTO_WITH_ERROR(arg, label, op_errno, error)             \
    do {                                                                       \
        if (caa_unlikely(!arg)) {                                              \
            op_errno = error;                                                  \
            GF_ASSERT(0);                                                      \
            goto label;                                                        \
        }                                                                      \
    } while (0)

#define GF_REMOVE_SLASH_FROM_PATH(path, string)                                \
    do {                                                                       \
        int i = 0;                                                             \
        for (i = 1; i < strlen(path); i++) {                                   \
            string[i - 1] = path[i];                                           \
            if (string[i - 1] == '/')                                          \
                string[i - 1] = '-';                                           \
        }                                                                      \
    } while (0)

#define GF_REMOVE_INTERNAL_XATTR(pattern, dict)                                \
    do {                                                                       \
        if (!dict) {                                                           \
            gf_msg(this->name, GF_LOG_ERROR, 0, LG_MSG_DICT_NULL,              \
                   "dict is null");                                            \
            break;                                                             \
        }                                                                      \
        dict_foreach_fnmatch(dict, pattern, dict_remove_foreach_fn, NULL);     \
    } while (0)

#define GF_IF_INTERNAL_XATTR_GOTO(pattern, dict, op_errno, label)              \
    do {                                                                       \
        if (!dict) {                                                           \
            gf_msg(this->name, GF_LOG_ERROR, 0, LG_MSG_DICT_NULL,              \
                   "setxattr dict is null");                                   \
            goto label;                                                        \
        }                                                                      \
        if (dict_foreach_fnmatch(dict, pattern, dict_null_foreach_fn, NULL) >  \
            0) {                                                               \
            op_errno = EPERM;                                                  \
            gf_msg(this->name, GF_LOG_ERROR, op_errno, LG_MSG_NO_PERM,         \
                   "attempt to set internal"                                   \
                   " xattr: %s",                                               \
                   pattern);                                                   \
            goto label;                                                        \
        }                                                                      \
    } while (0)

#define GF_IF_NATIVE_XATTR_GOTO(pattern, key, op_errno, label)                 \
    do {                                                                       \
        if (!key) {                                                            \
            gf_msg(this->name, GF_LOG_ERROR, 0, LG_MSG_NO_KEY,                 \
                   "no key for removexattr");                                  \
            goto label;                                                        \
        }                                                                      \
        if (!fnmatch(pattern, key, 0)) {                                       \
            op_errno = EPERM;                                                  \
            gf_msg(this->name, GF_LOG_ERROR, op_errno, LG_MSG_NO_PERM,         \
                   "attempt to remove internal "                               \
                   "xattr: %s",                                                \
                   key);                                                       \
            goto label;                                                        \
        }                                                                      \
    } while (0)

void
gf_assert(void);

#ifdef DEBUG
#define GF_ASSERT(x) assert(x);
#else
#define GF_ASSERT(x)                                                           \
    do {                                                                       \
        if (caa_unlikely(!(x))) {                                              \
            gf_assert();                                                       \
            gf_msg_callingfn("", GF_LOG_ERROR, 0, LG_MSG_ASSERTION_FAILED,     \
                             "Assertion failed: To attach gdb and coredump,"   \
                             " Run the script under "                          \
                             "\"glusterfs/extras/debug/gfcore.py\"");          \
        }                                                                      \
    } while (0)
#endif

/* Compile-time assert, borrowed from Linux kernel. */
#ifdef HAVE_STATIC_ASSERT
#define GF_STATIC_ASSERT(expr, ...)                                            \
    __gf_static_assert(expr, ##__VA_ARGS__, #expr)
#define __gf_static_assert(expr, msg, ...) _Static_assert(expr, msg)
#else
#define GF_STATIC_ASSERT(expr, ...)
#endif

#define GF_ABORT(msg...)                                                       \
    do {                                                                       \
        gf_msg_callingfn("", GF_LOG_CRITICAL, 0, LG_MSG_ASSERTION_FAILED,      \
                         "Assertion failed: " msg);                            \
        abort();                                                               \
    } while (0)

#define GF_UUID_ASSERT(u)                                                      \
    if (gf_uuid_is_null(u))                                                    \
        GF_ASSERT(!"uuid null");

#define GF_IGNORE_IF_GSYNCD_SAFE_ERROR(frame, op_errno)                        \
    (((frame->root->pid == GF_CLIENT_PID_GSYNCD) &&                            \
      (op_errno == EEXIST || op_errno == ENOENT))                              \
         ? 0                                                                   \
         : 1)

union gf_sock_union {
    struct sockaddr_storage storage;
    struct sockaddr_in6 sin6;
    struct sockaddr_in sin;
    struct sockaddr sa;
};

#define GF_HIDDEN_PATH ".glusterfs"
#define GF_UNLINK_PATH GF_HIDDEN_PATH "/unlink"
#define GF_LANDFILL_PATH GF_HIDDEN_PATH "/landfill"

#define MAX_IOVEC 16

static inline gf_boolean_t
gf_irrelevant_entry(struct dirent *entry)
{
    GF_ASSERT(entry);

    return (!strcmp(entry->d_name, ".") ||
            !fnmatch("*.tmp", entry->d_name, 0) ||
            !strcmp(entry->d_name, ".."));
}

static inline void
iov_free(struct iovec *vector, int count)
{
    int i;

    for (i = 0; i < count; i++)
        FREE(vector[i].iov_base);

    GF_FREE(vector);
}

static inline int
iov_length(const struct iovec *vector, int count)
{
    int i = 0;
    size_t size = 0;

    for (i = 0; i < count; i++)
        size += vector[i].iov_len;

    return size;
}

static inline struct iovec *
iov_dup(const struct iovec *vector, int count)
{
    int bytecount = 0;
    struct iovec *newvec = NULL;

    bytecount = (count * sizeof(struct iovec));
    newvec = GF_MALLOC(bytecount, gf_common_mt_iovec);
    if (newvec != NULL) {
        memcpy(newvec, vector, bytecount);
    }

    return newvec;
}

typedef struct _iov_iter {
    const struct iovec *iovec;
    void *ptr;
    uint32_t len;
    uint32_t count;
} iov_iter_t;

static inline bool
iov_iter_init(iov_iter_t *iter, const struct iovec *iovec, uint32_t count,
              uint32_t offset)
{
    uint32_t len;

    while (count > 0) {
        count--;
        len = iovec->iov_len;
        if (offset < len) {
            iter->ptr = iovec->iov_base + offset;
            iter->len = len - offset;
            iter->iovec = iovec + 1;
            iter->count = count;

            return true;
        }
        offset -= len;
    }

    memset(iter, 0, sizeof(*iter));

    return false;
}

static inline bool
iov_iter_end(iov_iter_t *iter)
{
    return iter->count == 0;
}

static inline bool
iov_iter_next(iov_iter_t *iter, uint32_t size)
{
    GF_ASSERT(size <= iter->len);

    if (iter->len > size) {
        iter->len -= size;
        iter->ptr += size;

        return true;
    }
    if (iter->count > 0) {
        iter->count--;
        iter->ptr = iter->iovec->iov_base;
        iter->len = iter->iovec->iov_len;
        iter->iovec++;

        return true;
    }

    memset(iter, 0, sizeof(*iter));

    return false;
}

static inline uint32_t
iov_iter_copy(iov_iter_t *dst, iov_iter_t *src, uint32_t size)
{
    uint32_t len;

    len = src->len;
    if (len > dst->len) {
        len = dst->len;
    }
    if (len > size) {
        len = size;
    }
    memcpy(dst->ptr, src->ptr, len);

    return len;
}

static inline uint32_t
iov_iter_to_iovec(iov_iter_t *iter, struct iovec *iovec, int32_t idx,
                  uint32_t size)
{
    uint32_t len;

    len = iter->len;
    if (len > size) {
        len = size;
    }
    iovec[idx].iov_base = iter->ptr;
    iovec[idx].iov_len = len;

    return len;
}

static inline int
iov_subset(struct iovec *src, int src_count, uint32_t start, uint32_t size,
           struct iovec **dst, int32_t dst_count)
{
    struct iovec iovec[src_count];
    iov_iter_t iter;
    uint32_t len;
    int32_t idx;

    if ((size == 0) || !iov_iter_init(&iter, src, src_count, start)) {
        return 0;
    }

    idx = 0;
    do {
        len = iov_iter_to_iovec(&iter, iovec, idx, size);
        idx++;
        size -= len;
    } while ((size > 0) && iov_iter_next(&iter, len));

    if (*dst == NULL) {
        *dst = iov_dup(iovec, idx);
        if (*dst == NULL) {
            return -1;
        }
    } else if (idx > dst_count) {
        return -1;
    } else {
        memcpy(*dst, iovec, idx * sizeof(struct iovec));
    }

    return idx;
}

static inline int
iov_skip(struct iovec *iovec, uint32_t count, uint32_t size)
{
    uint32_t len, idx;

    idx = 0;
    while ((size > 0) && (idx < count)) {
        len = iovec[idx].iov_len;
        if (len > size) {
            iovec[idx].iov_len -= size;
            iovec[idx].iov_base += size;
            break;
        }
        idx++;
        size -= len;
    }

    if (idx > 0) {
        memmove(iovec, iovec + idx, (count - idx) * sizeof(struct iovec));
    }

    return count - idx;
}

static inline size_t
iov_range_copy(const struct iovec *dst, uint32_t dst_count, uint32_t dst_offset,
               const struct iovec *src, uint32_t src_count, uint32_t src_offset,
               uint32_t size)
{
    iov_iter_t src_iter, dst_iter;
    uint32_t len, total;

    if ((size == 0) || !iov_iter_init(&src_iter, src, src_count, src_offset) ||
        !iov_iter_init(&dst_iter, dst, dst_count, dst_offset)) {
        return 0;
    }

    total = 0;
    do {
        len = iov_iter_copy(&dst_iter, &src_iter, size);
        total += len;
        size -= len;
    } while ((size > 0) && iov_iter_next(&src_iter, len) &&
             iov_iter_next(&dst_iter, len));

    return total;
}

static inline void
iov_unload(char *buf, const struct iovec *vector, int count)
{
    int i;
    int copied = 0;

    for (i = 0; i < count; i++) {
        memcpy(buf + copied, vector[i].iov_base, vector[i].iov_len);
        copied += vector[i].iov_len;
    }
}

static inline size_t
iov_load(const struct iovec *vector, int count, char *buf, int size)
{
    size_t left = size;
    size_t cp = 0;
    int ret = 0;
    int i = 0;

    while (left && i < count) {
        cp = min(vector[i].iov_len, left);
        if (vector[i].iov_base != buf + (size - left))
            memcpy(vector[i].iov_base, buf + (size - left), cp);
        ret += cp;
        left -= cp;
        if (left)
            i++;
    }

    return ret;
}

static inline size_t
iov_copy(const struct iovec *dst, int dcnt, const struct iovec *src, int scnt)
{
    return iov_range_copy(dst, dcnt, 0, src, scnt, 0, UINT32_MAX);
}

/* based on the amusing discussion @ https://rusty.ozlabs.org/?p=560 */
static bool
memeqzero(const void *data, size_t length)
{
    const unsigned char *p = data;
    size_t len;

    /* Check first 16 bytes manually */
    for (len = 0; len < 16; len++) {
        if (!length)
            return true;
        if (*p)
            return false;
        p++;
        length--;
    }

    /* Now we know that's zero, memcmp with self. */
    return memcmp(data, p, length) == 0;
}

static inline int
mem_0filled(const char *buf, size_t size)
{
    return !memeqzero(buf, size);
}

static inline int
iov_0filled(const struct iovec *vector, int count)
{
    int i = 0;
    int ret = 0;

    for (i = 0; i < count; i++) {
        ret = mem_0filled(vector[i].iov_base, vector[i].iov_len);
        if (ret)
            break;
    }

    return ret;
}

typedef enum {
    gf_timefmt_default = 0,
    gf_timefmt_FT = 0, /* YYYY-MM-DD hh:mm:ss */
    gf_timefmt_Ymd_T,  /* YYYY/MM-DD-hh:mm:ss */
    gf_timefmt_bdT,    /* MMM DD hh:mm:ss */
    gf_timefmt_F_HMS,  /* YYYY-MM-DD hhmmss */
    gf_timefmt_dirent,
    gf_timefmt_s,
    gf_timefmt_last
} gf_timefmts;

static const char *__gf_timefmts[] = {
    "%F %T", "%Y/%m/%d-%T", "%b %d %T", "%F %H%M%S", "%Y-%m-%d-%T", "%s",
};

static const char *__gf_zerotimes[] = {
    "0000-00-00 00:00:00", "0000/00/00-00:00:00", "xxx 00 00:00:00",
    "0000-00-00 000000",   "0000-00-00-00:00:00", "0",
};

static void
_gf_timestuff(const char ***fmts, const char ***zeros)
{
    *fmts = __gf_timefmts;
    *zeros = __gf_zerotimes;
}

static inline char *
gf_time_fmt_tv(char *dst, size_t sz_dst, struct timeval *tv, unsigned int fmt)
{
    static gf_timefmts timefmt_last = (gf_timefmts)-1;
    static const char **fmts;
    static const char **zeros;
    struct tm tm, *res;
    int localtime = 0;
    int len = 0;
    int pos = 0;

    if (timefmt_last == ((gf_timefmts)-1)) {
        _gf_timestuff(&fmts, &zeros);
        timefmt_last = gf_timefmt_last;
    }
    if (timefmt_last <= fmt) {
        fmt = gf_timefmt_default;
    }
    localtime = gf_log_get_localtime();
    res = localtime ? localtime_r(&tv->tv_sec, &tm)
                    : gmtime_r(&tv->tv_sec, &tm);
    if (tv->tv_sec && (res != NULL)) {
        len = strftime(dst, sz_dst, fmts[fmt], &tm);
        if (len == 0)
            return dst;
        pos += len;
        if (tv->tv_usec >= 0) {
            len = snprintf(dst + pos, sz_dst - pos, ".%" GF_PRI_SUSECONDS,
                           tv->tv_usec);
            if (len >= sz_dst - pos)
                return dst;
            pos += len;
        }
        strftime(dst + pos, sz_dst - pos, " %z", &tm);
    } else {
        strncpy(dst, "N/A", sz_dst);
    }
    return dst;
}

static inline char *
gf_time_fmt(char *dst, size_t sz_dst, time_t utime, unsigned int fmt)
{
    struct timeval tv = {utime, -1};

    return gf_time_fmt_tv(dst, sz_dst, &tv, fmt);
}

static inline size_t
gf_time_fmt_tv_FT(char *dst, size_t sz_dst, struct timeval *tv,
                  gf_log_handle_t *log)
{
    struct tm tm, *res;
    int localtime = 0;
    int len = 0;
    int pos = 0;

    if (log != NULL)
        localtime = log->localtime;
    else
        localtime = gf_log_get_localtime();
    res = localtime ? localtime_r(&tv->tv_sec, &tm)
                    : gmtime_r(&tv->tv_sec, &tm);
    if (tv->tv_sec && (res != NULL)) {
        len = strftime(dst, sz_dst, "%F %T", &tm);
        if (len == 0)
            return len;
        pos += len;
        if (tv->tv_usec >= 0) {
            len = snprintf(dst + pos, sz_dst - pos, ".%" GF_PRI_SUSECONDS,
                           tv->tv_usec);
            if (len >= sz_dst - pos)
                return len;
            pos += len;
        }
        return strftime(dst + pos, sz_dst - pos, " %z", &tm);
    } else {
        strncpy(dst, "N/A", sz_dst);
        return SLEN("N/A");
    }
}

static inline int
gf_time_fmt_FT(char *dst, size_t sz_dst, time_t utime)
{
    struct timeval tv = {utime, -1};

    return gf_time_fmt_tv_FT(dst, sz_dst, &tv, NULL);
}

/* This function helps us use gfid (unique identity) to generate inode's unique
 * number in glusterfs.
 */
ino_t
gfid_to_ino(uuid_t gfid);

int
mkdir_p(char *path, mode_t mode, gf_boolean_t allow_symlinks);
/*
 * rounds up nr to power of two. If nr is already a power of two, just returns
 * nr
 */

int
gf_lstat_dir(const char *path, struct stat *stbuf_in);

int32_t
gf_roundup_power_of_two(int32_t nr);

/*
 * rounds up nr to next power of two. If nr is already a power of two, next
 * power of two is returned.
 */

int32_t
gf_roundup_next_power_of_two(int32_t nr);

char *
gf_trim(char *string);

int
gf_string2long(const char *str, long *n);
int
gf_string2ulong(const char *str, unsigned long *n);
int
gf_string2int(const char *str, int *n);
int
gf_string2uint(const char *str, unsigned int *n);
int
gf_string2double(const char *str, double *n);
int
gf_string2longlong(const char *str, long long *n);
int
gf_string2ulonglong(const char *str, unsigned long long *n);

int
gf_string2int8(const char *str, int8_t *n);
int
gf_string2int16(const char *str, int16_t *n);
int
gf_string2int32(const char *str, int32_t *n);
int
gf_string2int64(const char *str, int64_t *n);
int
gf_string2uint8(const char *str, uint8_t *n);
int
gf_string2uint16(const char *str, uint16_t *n);
int
gf_string2uint32(const char *str, uint32_t *n);
int
gf_string2uint64(const char *str, uint64_t *n);

int
gf_strstr(const char *str, const char *delim, const char *match);

int
gf_string2ulong_base10(const char *str, unsigned long *n);
int
gf_string2uint_base10(const char *str, unsigned int *n);
int
gf_string2uint8_base10(const char *str, uint8_t *n);
int
gf_string2uint16_base10(const char *str, uint16_t *n);
int
gf_string2uint32_base10(const char *str, uint32_t *n);
int
gf_string2uint64_base10(const char *str, uint64_t *n);
int
gf_string2bytesize_uint64(const char *str, uint64_t *n);
int
gf_string2bytesize_int64(const char *str, int64_t *n);
int
gf_string2percent_or_bytesize(const char *str, double *n,
                              gf_boolean_t *is_percent);

int
gf_string2boolean(const char *str, gf_boolean_t *b);
int
gf_strn2boolean(const char *str, const int len, gf_boolean_t *b);
int
gf_string2percent(const char *str, double *n);
int
gf_string2time(const char *str, time_t *n);

int
log_base2(unsigned long x);

char *
strtail(char *str, const char *pattern);
void
skipwhite(char **s);
char *
nwstrtail(char *str, char *pattern);
/* returns a new string with nth word of given string. n>=1 */

typedef struct token_iter {
    char *end;
    char sep;
} token_iter_t;
char *
token_iter_init(char *str, char sep, token_iter_t *tit);
gf_boolean_t
next_token(char **tokenp, token_iter_t *tit);
void
drop_token(char *token, token_iter_t *tit);

gf_boolean_t
mask_match(const uint32_t a, const uint32_t b, const uint32_t m);
gf_boolean_t
gf_is_ip_in_net(const char *network, const char *ip_str);
char
valid_host_name(char *address, int length);
char
valid_ipv4_address(char *address, int length, gf_boolean_t wildcard_acc);
char
valid_ipv6_address(char *address, int length, gf_boolean_t wildcard_acc);
char
valid_internet_address(char *address, gf_boolean_t wildcard_acc,
                       gf_boolean_t cidr);
char *
gf_rev_dns_lookup(const char *ip);

char *
uuid_utoa(uuid_t uuid);
char *
uuid_utoa_r(uuid_t uuid, char *dst);
char *
lkowner_utoa(gf_lkowner_t *lkowner);
char *
leaseid_utoa(const char *lease_id);
gf_boolean_t
is_valid_lease_id(const char *lease_id);
char *
gf_leaseid_get(void);
char *
gf_existing_leaseid(void);

char *gf_uint64_2human_readable(uint64_t);
char *
get_host_name(char *word, char **host);
void
gf_path_strip_trailing_slashes(char *path);
uint64_t
get_mem_size(void);
int
gf_canonicalize_path(char *path);
char *
generate_glusterfs_ctx_id(void);
int
gf_process_reserved_ports(unsigned char *ports, uint32_t ceiling);
int
gf_get_hostname_from_ip(char *client_ip, char **hostname);
gf_boolean_t
gf_is_local_addr(char *hostname);
gf_boolean_t
gf_is_same_address(char *host1, char *host2);
void
gf_xxh64_wrapper(const unsigned char *data, size_t const len,
                 unsigned long long const seed, char *xxh64);
int
gf_gfid_generate_from_xxh64(uuid_t gfid, char *key);

int
gf_set_timestamp(const char *src, const char *dest);

static inline int
__gf_thread_set_name(pthread_t thread, const char *name)
{
#if defined(HAVE_PTHREAD_SETNAME_NP_TWO_ARGS)
    return pthread_setname_np(thread, name);
#elif defined(HAVE_PTHREAD_SETNAME_NP_THREE_ARGS)
    return pthread_setname_np(thread, name, NULL);
#elif defined(HAVE_PTHREAD_SET_NAME_NP)
    pthread_set_name_np(thread, name);
    return 0;
#else  /* none found */
    return -ENOSYS;
#endif /* set name */
}

int
gf_thread_create(pthread_t *thread, const pthread_attr_t *attr,
                 void *(*start_routine)(void *), void *arg, const char *name,
                 ...) __attribute__((__format__(__printf__, 5, 6)));

int
gf_thread_create_detached(pthread_t *thread, void *(*start_routine)(void *),
                          void *arg, const char *name, ...)
    __attribute__((__format__(__printf__, 4, 5)));

void
gf_thread_set_name(pthread_t thread, const char *name, ...)
    __attribute__((__format__(__printf__, 2, 3)));

gf_boolean_t
gf_is_service_running(char *pidfile, int *pid);

struct iatt;
struct _dict;

char *
gf_backtrace_save(char *buf);

gf_loglevel_t
fop_log_level(glusterfs_fop_t fop, int op_errno);

int
recursive_rmdir(const char *delete_path);

gf_boolean_t
gf_unlink(const char *path);

int
gf_rebalance_thread_count(char *str, char **errmsg);

int
gf_get_index_by_elem(char **array, char *elem);

int
glusterfs_is_local_pathinfo(char *pathinfo, gf_boolean_t *local);

int
gf_thread_cleanup_xint(pthread_t thread);

ssize_t
gf_nread(int fd, void *buf, size_t count);

ssize_t
gf_nwrite(int fd, const void *buf, size_t count);

void
_mask_cancellation(void);
void
_unmask_cancellation(void);

gf_boolean_t
gf_is_zero_filled_stat(struct iatt *buf);

gf_boolean_t
gf_is_valid_xattr_namespace(char *k);

const char *
gf_inode_type_to_str(ia_type_t type);

int32_t
gf_bits_count(uint64_t n);

int32_t
gf_bits_index(uint64_t n);

const char *
gf_fop_string(glusterfs_fop_t fop);

int
gf_fop_int(char *fop);

int
close_fds_except_custom(int *fdv, size_t count, void *prm,
                        void closer(int fd, void *prm));

int
close_fds_except(int *fdv, size_t count);

int
gf_getgrouplist(const char *user, gid_t group, gid_t **groups);

int
glusterfs_compute_sha256(const unsigned char *content, size_t size,
                         char *sha256_hash);

char *
gf_strncpy(char *dest, const char *src, const size_t dest_size);

xlator_cmdline_option_t *
find_xlator_option_in_cmd_args_t(const char *option_name, cmd_args_t *args);

int
gf_d_type_from_ia_type(ia_type_t type);

int
gf_pipe(int fd[2], int flags);

int
gf_nanosleep(uint64_t nsec);

static inline time_t
gf_time(void)
{
    return time(NULL);
}

/* Return delta value in microseconds. */

static inline double
gf_tvdiff(struct timeval *start, struct timeval *end)
{
    struct timeval t;

    if (start->tv_usec > end->tv_usec)
        t.tv_sec = end->tv_sec - 1, t.tv_usec = end->tv_usec + 1000000;
    else
        t.tv_sec = end->tv_sec, t.tv_usec = end->tv_usec;

    return (double)(t.tv_sec - start->tv_sec) * 1e6 +
           (double)(t.tv_usec - start->tv_usec);
}

/* Return delta value in nanoseconds. */

static inline int64_t
gf_tsdiff(struct timespec *start, struct timespec *end)
{
    return (int64_t)(end->tv_sec - start->tv_sec) * GF_SEC_IN_NS +
           (int64_t)(end->tv_nsec - start->tv_nsec);
}

#endif /* _COMMON_UTILS_H */
