Skip to content

Instantly share code, notes, and snippets.

@mpage
Created December 7, 2011 21:45

Revisions

  1. mpage created this gist Dec 7, 2011.
    177 changes: 177 additions & 0 deletions this could conceivably replace repquota
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,177 @@
    #include <assert.h>
    #include <errno.h>
    #include <fstab.h>
    #include <inttypes.h>
    #include <malloc.h>
    #include <mntent.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/quota.h>

    #define dqblk_print_field(dqblk, field) printf("%llu ", (dqblk).(field))

    /**
    * Attempts to look up the device name associated with supplied mount point.
    *
    * @param dir Mount point to look up
    * @param dev_name Where to place the device name. Caller must free.
    *
    * @return 0 on success
    * -1 mount point not found
    */
    static int lookup_device(const char* dir, char** dev_name) {
    FILE* mtab = NULL;
    struct mntent* mtab_entry = NULL;
    size_t fsname_len = 0;
    int retval = -1;

    /* Using /etc/mtab here is supposedly a no-no, but the macro we're supposed
    * to use (_PATH_MNTTAB) resolves to "/etc/fstab" (at least on my Ubuntu system).
    */
    mtab = setmntent("/etc/mtab", "r");
    if (NULL == mtab) {
    return -2;
    }

    /* Could move into conditional, but prefer this stylistically */
    mtab_entry = getmntent(mtab);
    while (NULL != mtab_entry) {
    if (!strcmp(mtab_entry->mnt_dir, dir)) {
    fsname_len = strlen(mtab_entry->mnt_fsname) + 1;

    *dev_name = malloc(fsname_len);
    assert(NULL != *dev_name);

    strncpy(*dev_name, mtab_entry->mnt_fsname, fsname_len);
    retval = 0;
    break;
    }

    mtab_entry = getmntent(mtab);
    }

    endmntent(mtab);

    return retval;
    }

    /**
    * Attempts to print a helpful error message to stderr.
    *
    * @param msg Error message to prepend.
    */
    static void print_quotactl_error(const char* msg) {
    assert(NULL != msg);

    switch (errno) {
    case EFAULT:
    fprintf(stderr, "%s: Block device invalid.\n", msg);
    break;

    case ENOENT:
    fprintf(stderr, "%s: Block device doesn't exist.\n", msg);
    break;

    case ENOSYS:
    fprintf(stderr, "%s: Kernel doesn't haven quota support.\n", msg);
    break;

    case ENOTBLK:
    fprintf(stderr, "%s: Not a block device.\n", msg);
    break;

    case EPERM:
    fprintf(stderr, "%s: Insufficient privilege.\n", msg);
    break;

    case ESRCH:
    fprintf(stderr, "%s: No quota for supplied user.\n", msg);
    break;

    default:
    perror(msg);
    break;
    }
    }

    /**
    * Prints relevant quota information to stdout for the supplied uid.
    * On failure, will attempt to print a helpful error messge to stderr.
    *
    * @param filesystem Filesystem to report quota information for
    * @param uid Uid to report quota information for
    *
    * @return -1 on error, 0 otherwise
    */
    static int print_quota_usage(const char* filesystem, int uid) {
    assert(NULL != filesystem);

    char emsg[1024];
    struct dqblk quota_info;

    memset(&quota_info, 0, sizeof(struct dqblk));

    if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), filesystem, uid, (caddr_t) &quota_info) < 0) {
    sprintf(emsg, "Failed retrieving quota for uid=%d", uid);
    print_quotactl_error(emsg);
    return -1;
    }

    printf("%d ", uid);

    /* Block info */
    printf("%llu %llu %llu %llu ",
    (long long unsigned int) quota_info.dqb_curspace,
    (long long unsigned int) quota_info.dqb_bsoftlimit,
    (long long unsigned int) quota_info.dqb_bhardlimit,
    (long long unsigned int) quota_info.dqb_btime);

    /* Inode info */
    printf("%llu %llu %llu %llu\n",
    (long long unsigned int) quota_info.dqb_curinodes,
    (long long unsigned int) quota_info.dqb_isoftlimit,
    (long long unsigned int) quota_info.dqb_ihardlimit,
    (long long unsigned int) quota_info.dqb_itime);

    return 0;
    }

    int
    main(int argc, char* argv[]) {
    char* device_name = NULL;
    int* uids = NULL;
    int ii = 0, num_uids = 0;

    if (argc < 3) {
    printf("Usage: report_quota [filesystem] [uid]+\n");
    printf("Reports quota information for the supplied uids on the given filesystem\n");
    printf("Format is: <uid> <blocks used> <soft> <hard> <grace> <inodes used> <soft> <hard> <grace>\n");
    exit(1);
    }

    if (lookup_device(argv[1], &device_name) < 0) {
    printf("Couldn't find device for %s", argv[1]);
    exit(1);
    }

    num_uids = argc - 2;
    uids = malloc(sizeof(int) * num_uids);
    if (NULL == uids) {
    perror("malloc()");
    free(device_name);
    exit(1);
    }
    memset(uids, 0, sizeof(int) * num_uids);
    for (ii = 0; ii < num_uids; ii++) {
    uids[ii] = atoi(argv[ii + 2]);
    }

    for (ii = 0; ii < num_uids; ii++) {
    print_quota_usage(device_name, uids[ii]);
    }

    free(device_name);

    return 0;
    }