From: millson@vogon.enet.dec.com (Martin "Morgul" Millson)
Subject: Re: Disk quota report facility wanted?
Reply-To: millson@vogon.enet.dec.com (Martin "Morgul" Millson)
Organization: International Systems Engineering/Reading ,Digital Equipment Corp
Date: Tue, 8 Sep 1992 08:10:04 GMT


I have written some software to report on disk quota's. The following is
the source the cld and actual C code

------ DISKMONITOR.CLD------------
MODULE DISKMON
IDENT "V1.0"
!++
! Facility:
!   Disk Monitor - Diskmon
! 
! Abstract:
!   The CLD file for the Disk Monitor
! 
! Author:
!   Martin Millson
! 
! Creation Date: 10-JAN-1992
! 
! Modification History:
!--

DEFINE VERB DISKMONITOR
    PARAMETER P1, LABEL=SUBCOMMAND,
    		    PROMPT="Command",
    		    VALUE(REQUIRED,TYPE=SUBCOMMAND_KEYWORDS)

DEFINE TYPE SUBCOMMAND_KEYWORDS
    KEYWORD USAGE,
    		    LABEL=USAGE,
    		    SYNTAX=USAGE_SYNTAX
    KEYWORD BACKUP_CHECK,
    		    LABEL=BACKUP_CHECK,
    		    SYNTAX=BACKUP_CHECK_SYNTAX


DEFINE SYNTAX USAGE_SYNTAX
    IMAGE DISKMON$USAGE.EXE
    PARAMETER P1, LABEL=SUBCOMMAND,
    		    VALUE(REQUIRED)
    PARAMETER P2, LABEL=USAGEFILE,
    		    PROMPT="Usage File",
    		    VALUE(REQUIRED,TYPE=$INFILE)
    QUALIFIER MAILTO,
    		    LABEL=MAILTO,
    		    NONNEGATABLE,
    		    VALUE(REQUIRED)
    QUALIFIER FILES,
		    LABEL=FILES,
		    NEGATABLE
    QUALIFIER TOTALS,
		    LABEL=TOTALS,
		    NEGATABLE,
		    DEFAULT
    QUALIFIER OUTPUT,
		    VALUE(TYPE=$OUTFILE)
		    
DEFINE SYNTAX BACKUP_CHECK_SYNTAX
    IMAGE DISKMON$BACKUP_CHECK.EXE
    PARAMETER P1, LABEL=SUBCOMMAND,
    		    VALUE(REQUIRED)
    PARAMETER P2, LABEL=DISK,
    		    PROMPT="Disk",
    		    VALUE(REQUIRED)
    QUALIFIER MAILTO,
    		    LABEL=MAILTO,
    		    NONNEGATABLE,
    		    VALUE(REQUIRED)
    QUALIFIER OUTPUT,
		    VALUE(TYPE=$OUTFILE)

---- DISKMON$USAGE.C-------------

/*
**++
**  Facility:
**
**	Disk Monitor - Diskmon
**
**  Version: V1.0-0
**
**  Abstract:
**
**	Generates a Report from a disk usage file
**
**
**  Author:
**	Martin Millson,	    International Systems Engineering,
**			    Reading UK,
**			    Digital Equipement Corp.
**
**  Creation Date:  8-JAN-1992
**
**  Modification History:
**--
*/

/*
**  Include Files
*/
#include <stdio.h>
#include <nam.h>
#include <rmsdef.h>
#include <maildef>
#include <ssdef.h>
#include <climsgdef.h>
#include <descrip.h>
#include <usgdef.h>
/*									    */
/* Defines								    */
/*									    */
#define VERSION "V1.0-0"
#define IS_GENERAL_ID  80000000  /* When identifiers are created,	    */
                                 /* GENERAL identifiers automatically	    */
                                 /* have %x80000000 added to the identifier */
                                 /* value.				    */
                                 /* This differentiates them from	    */
                                 /* UIC identifiers.			    */
#define NODIR_FILES_UIC	0	/* UIC used for total of files with	    */
				/* directory spec of []			    */
#define VMS_UIC		65537	/* UIC used by VMS when init disks	    */
#define TRUE 1
#define FALSE 0
/*									    */
/* Typedefs								    */
/*									    */
typedef struct itmlst
    {
        short buffer_length;
        short item_code;
        long buffer_address;
        long return_length_address;
    } ITMLST;
    
struct list
{
    struct list *next;
    unsigned long int uic;
    unsigned long int running_total_used;
    unsigned long int running_total_alloc;
    struct list *top;
};


void add_to_total(struct list *list_header,
		 unsigned long int uic,
		 unsigned long int used,
		 unsigned long int allocated)
{

    struct list *top_of_list;

    top_of_list = list_header -> top;
    if (list_header -> uic == uic)
    {
	list_header -> running_total_used += used;
	list_header -> running_total_alloc += allocated;
    }
    else
    {
	if (list_header -> next == NULL)
	{
	    list_header -> next = malloc (sizeof(struct list));
	    list_header -> next -> uic = uic;
	    list_header -> next -> running_total_used = used;
	    list_header -> next -> running_total_alloc = allocated;
	    list_header -> next -> top = top_of_list;
	    list_header -> next -> next = NULL;
	}
	else
	{
	    add_to_total(list_header -> next,uic,used,allocated);
	}
    }
}

void PutTotals(FILE *output_file,struct list *list_header)
{

    struct list *top_of_list;
    unsigned long int grand_total_used  = 0;
    unsigned long int grand_total_alloc  = 0;
    char namebuf[100];		   
    unsigned int rights_id  = 0, attrib  = 0;
    int context = 0, status = 0;
    short namlen  = 0;
    char id_string[14];
    char title1[6] = "Owner";
    char title2[3] = "Id";
    char title3[5] = "Used";
    char title4[6] = "Alloc";
    $DESCRIPTOR(nambuf_dsc,namebuf);
    
    top_of_list = list_header -> top;
    fprintf(output_file,"\n");
    fprintf(output_file,"Disk Usage Totals\n");
    fprintf(output_file,"-----------------\n");
    fprintf(output_file,"%-32s\t%-14s\t%-6s\t%-7s\n",
						title1,
						title2,
						title3,
						title4);
    while(top_of_list != NULL)
    {
        nambuf_dsc.dsc$w_length = sizeof(namebuf)-1;
        status = SYS$IDTOASC(top_of_list -> uic, &namlen, &nambuf_dsc,
                             &rights_id,&attrib,&context);
	switch (status)
	{
	    case SS$_NORMAL: namebuf[namlen] = 0; break;
	    case SS$_IVIDENT:
	    case SS$_NOSUCHID:  {
				    switch (top_of_list -> uic)
				    {
					case NODIR_FILES_UIC:
					    sprintf(namebuf,"<NO DIRECTORY>");
					    break;
					case VMS_UIC:
					    sprintf(namebuf,"<VMS>");
					    break;
					default:
					    sprintf(namebuf,"<NOSUCHID>");
					    break;
				    }
				}; break;
	    case SS$_ACCVIO: 
	    case SS$_INSFMEM:
	    case RMS$_PRV:
	    case SS$_NOIOCHAN:
	    default: lib$signal(status); break; 
	}
	if (rights_id >= IS_GENERAL_ID)
	{
	    sprintf(id_string,"%x",rights_id);
	}
	else
	{
	    sprintf(id_string,"[%o,%o]",(top_of_list -> uic / 65536),
					(top_of_list -> uic % 65536));
	}
	fprintf(output_file,"%-32s\t%-14s\t%d\t%d \n",
					namebuf,
					id_string,
					top_of_list -> running_total_used,
					top_of_list -> running_total_alloc);
	grand_total_used += top_of_list -> running_total_used;
	grand_total_alloc += top_of_list -> running_total_alloc;
	top_of_list = top_of_list -> next;
    }
    status = SYS$FINISH_RDB(&context);
    fprintf(output_file,"Grand Total is %d Used Blocks\n",grand_total_used);
    fprintf(output_file,"            is %d Allocated
Blocks\n",grand_total_alloc);
}

int GetHeaderRec(FILE *InFp, struct usgdef *HeaderRec)
{
	memset(HeaderRec,NULL,sizeof(struct usgdef));
	fread(HeaderRec,sizeof(struct usgdef),1,InFp);
	return(TRUE);
}

int GetFileRec(FILE *InFp, struct usgdef1 *FileRec)
{
	memset(FileRec,NULL,sizeof(struct usgdef1));
	if (fgetc(InFp)==EOF) return (FALSE);
	fread(&(FileRec->usg$l_fileowner),sizeof(char),4,InFp);
	fread(&(FileRec->usg$l_allocated),sizeof(char),4,InFp);
	fread(&(FileRec->usg$l_used),sizeof(char),4,InFp);
	fread(&(FileRec->usg$w_dir_len),sizeof(char),2,InFp);
	fread(&(FileRec->usg$w_spec_len),sizeof(char),2,InFp);
	fread(&(FileRec->usg$t_filespec),sizeof(char),
		FileRec->usg$w_spec_len,InFp);
	return (TRUE);
}

PutHeader(FILE *output_file,struct usgdef headerrec)
{
    char timbuf[24];
    unsigned short timlen;

    $DESCRIPTOR(TIME_DESC,timbuf);

    fprintf(output_file,"\n");    
    fprintf(output_file,"Volume Details\n");    
    fprintf(output_file,"--------------\n");    
    fprintf(output_file,"Volume Set Name : %.12s\tVolume One Name : %.12s\n",
						headerrec.usg$t_strucname,
						headerrec.usg$t_volname);
    fprintf(output_file,"Volume Owner    : %.12s\tVolume Format   : %.12s\n",
						headerrec.usg$t_ownername,
						headerrec.usg$t_format);
    sys$asctim (
    	&timlen, 
    	&TIME_DESC, 
    	&headerrec.usg$q_time, 
    	0);
    timbuf[timlen] = 0;
    fprintf(output_file,"Volume Serial # : %011d\tData Collected  : %s\n",
						headerrec.usg$l_serialnum,
						timbuf);
}
PutFile(FILE *output_file,struct usgdef1 filerec)
{
    char namebuf[100];		   
    unsigned int rights_id  = 0, attrib  = 0;
    int context = 0, status = 0;
    short namlen  = 0;
    char id_string[14];
    $DESCRIPTOR(nambuf_dsc,namebuf);
    
    nambuf_dsc.dsc$w_length = sizeof(namebuf)-1;

    if (filerec.usg$w_dir_len == 2)
    {
	/* Set lost files to no directory files uic			    */
	filerec.usg$l_fileowner = NODIR_FILES_UIC;
    }
    
    status = SYS$IDTOASC(filerec.usg$l_fileowner, &namlen, &nambuf_dsc,
                             &rights_id,&attrib,&context);

    switch (status)
    {
	case SS$_NORMAL: namebuf[namlen] = 0; break;
	case SS$_IVIDENT:
	case SS$_NOSUCHID:  {
				switch (filerec.usg$l_fileowner)
				{
				    case NODIR_FILES_UIC:
					    sprintf(namebuf,"<NO DIRECTORY>");
					    break;
				    case VMS_UIC:
					    sprintf(namebuf,"<VMS>");
					    break;
				    default:
					    sprintf(namebuf,"<NOSUCHID>");
					    break;
				}
			    }; break;
	case SS$_ACCVIO: 
	case SS$_INSFMEM:
	case RMS$_PRV:
	case SS$_NOIOCHAN:
	default: lib$signal(status); break; 
    }

    if (rights_id >= IS_GENERAL_ID)
    {
	sprintf(id_string,"%x",rights_id);
    }
    else
    {
	sprintf(id_string,"[%o,%o]",(filerec.usg$l_fileowner / 65536),
				    (filerec.usg$l_fileowner % 65536));
    }
    fprintf(output_file,"%-32s\t%-14s\t%d\t%d\t%-*.s\n",
					namebuf,
					id_string,
					filerec.usg$l_used,
					filerec.usg$l_allocated,
					filerec.usg$w_spec_len,
					filerec.usg$t_filespec);
    status = SYS$FINISH_RDB(&context);
}

int mail_send_message (
			struct dsc$descriptor_s *to_user,
			struct dsc$descriptor_s *subject,
			struct dsc$descriptor_s *file_name)
{

    int send_context = 0;

    char resultspec[NAM$C_MAXRSS],
	 c_to_user[NAM$C_MAXRSS],
	 c_subject[NAM$C_MAXRSS],
	 c_file_name[NAM$C_MAXRSS];
    long resultspeclen;
    int	 status = SS$_NORMAL,
	 file_len = 0,
	 subject_line_len = 0,
	 to_user_len =0;
    ITMLST
	nulllist[] = {
	    {0, MAIL$_NOSIGNAL , 0, 0},
	    {0,0,0,0} },
	address_itmlst[] = {
	    {sizeof(c_to_user), MAIL$_SEND_USERNAME , c_to_user, &to_user_len},
	    {0, MAIL$_NOSIGNAL , 0, 0},
	    {0,0,0,0}},
	bodypart_itmlst[] = {
	    {sizeof(c_file_name), MAIL$_SEND_FILENAME, c_file_name, &file_len},
	    {0, MAIL$_NOSIGNAL , 0, 0},
	    {0,0,0,0}},
	out_bodypart_itmlst[] = {
	    {sizeof(resultspec), MAIL$_SEND_RESULTSPEC, resultspec,
	    &resultspeclen},
	    {0, MAIL$_NOSIGNAL , 0, 0},
	    {0,0,0,0}},
	attribute_itmlst[] = {
	    {sizeof(c_to_user), MAIL$_SEND_TO_LINE, c_to_user, &to_user_len},
	    {sizeof(c_subject), MAIL$_SEND_SUBJECT,c_subject,&subject_line_len},
	    {0, MAIL$_NOSIGNAL , 0, 0},
	    {0,0,0,0}};
    
    status = mail$send_begin(&send_context,&nulllist,&nulllist);
    if (status != SS$_NORMAL)
	return (status);

    memset(c_to_user,NULL,sizeof(c_to_user));
    sprintf (c_to_user, "%s\0", to_user -> dsc$a_pointer);
    address_itmlst[0].buffer_length = strlen(c_to_user);
    address_itmlst[0].buffer_address = c_to_user;
    
    status = mail$send_add_address(&send_context,&address_itmlst,&nulllist);
    if (status != SS$_NORMAL)
	return (status);

 
    attribute_itmlst[0].buffer_length = strlen(c_to_user);
    attribute_itmlst[0].buffer_address = c_to_user;

    memset(c_subject,NULL,sizeof(c_subject));
    sprintf (c_subject, "%s\0", subject -> dsc$a_pointer);
    attribute_itmlst[1].buffer_length = strlen(c_subject);
    attribute_itmlst[1].buffer_address = c_subject;

    
    status =
mail$send_add_attribute(&send_context,&attribute_itmlst,&nulllist);
    if (status != SS$_NORMAL)
	return (status);
	
    sprintf (c_file_name, "%s\0", file_name -> dsc$a_pointer);
    bodypart_itmlst[0].buffer_length = strlen(c_file_name);
    bodypart_itmlst[0].buffer_address = c_file_name;
    
    status = mail$send_add_bodypart(&send_context,&bodypart_itmlst,
				     &out_bodypart_itmlst);
    if (status != SS$_NORMAL)
	return (status);	

    resultspec[resultspeclen] = '\0';
    status = mail$send_message(&send_context,&nulllist,
				     &nulllist);
    if (status != SS$_NORMAL)
	return (status);
	
    status = mail$send_end(&send_context,&nulllist,
				     &nulllist);
    if (status != SS$_NORMAL)
	return (status);

    return(status);

}

int main(void)
{
    struct list *place_in_list;
    FILE *usage_file;
    FILE *output_file;
    unsigned short retlen;
    unsigned long ret_status;
    int m_status;
    int first_title = TRUE;
    int display_totals = TRUE;
    int display_files = FALSE;
    int send_mail = FALSE;
    struct usgdef headerrec;
    struct usgdef1 filerec;
    char title1[6] = "Owner";
    char title2[3] = "Id";
    char title3[5] = "Used";
    char title4[6] = "Alloc";
    char title5[5] = "File";
    
    char output_file_name[NAM$C_MAXRSS];
    char usage_file_name[NAM$C_MAXRSS];
    char mailto_string[NAM$C_MAXRSS];
    char subject_string[NAM$C_MAXRSS];
    char result_time[24];

    $DESCRIPTOR(MAIL_DESC,"MAILTO");
    $DESCRIPTOR(USAGE_DESC,"USAGEFILE");
    $DESCRIPTOR(OUTPUT_DESC,"OUTPUT");
    $DESCRIPTOR(FILE_DESC,"FILES");
    $DESCRIPTOR(TOTALS_DESC,"TOTALS");

    $DESCRIPTOR(INPUT_FILE_DESC,usage_file_name);
    $DESCRIPTOR(OUTPUT_FILE_DESC,output_file_name);
    $DESCRIPTOR(MAILTO_STRING_DESC,mailto_string);
    $DESCRIPTOR(SUBJECT_STRING_DESC,subject_string);
    
    $DESCRIPTOR(time_desc,result_time);
    
    place_in_list = malloc(sizeof(struct list));
    place_in_list -> uic = 0;
    place_in_list -> running_total_used = 0;
    place_in_list -> running_total_alloc = 0;
    place_in_list -> next = NULL;
    place_in_list -> top  = place_in_list;

    /* Input file from ANA/DISK/USAGE */
    
    ret_status = cli$present( &USAGE_DESC );
    if (ret_status == CLI$_PRESENT)
	{
	    ret_status = cli$get_value(&USAGE_DESC,
				       &INPUT_FILE_DESC,
				       &retlen);
	    usage_file_name[retlen] = '\0';
	}
    else
	sprintf(usage_file_name,"USAGE.DAT");

    /* Output file for report */
    
    ret_status = cli$present( &OUTPUT_DESC );
    if (ret_status == CLI$_PRESENT)
	{
	    ret_status = cli$get_value(&OUTPUT_DESC,
				       &OUTPUT_FILE_DESC,
				       &retlen);
	    output_file_name[retlen] = '\0';
	}
    else
	sprintf(output_file_name,"SYS$OUTPUT");

    ret_status = cli$present( &MAIL_DESC );
    if (ret_status == CLI$_PRESENT)
	{
	    ret_status = cli$get_value(&MAIL_DESC,
				       &MAILTO_STRING_DESC,
				       &retlen);
	    mailto_string[retlen] = '\0';
	    send_mail = TRUE;
	}
    else
	send_mail = FALSE;
		
    switch (cli$present(&FILE_DESC))
    {
    	case CLI$_PRESENT: display_files = TRUE; break;
    	case CLI$_DEFAULTED:
	case CLI$_ABSENT:
    	case CLI$_NEGATED: display_files = FALSE; break;
	default: break;
    }

    switch (cli$present(&TOTALS_DESC))
    {
	case CLI$_ABSENT:
    	case CLI$_DEFAULTED:
    	case CLI$_PRESENT: display_totals = TRUE; break;
    	case CLI$_NEGATED: display_totals = FALSE; break;
	default: break;
    }
    if ((usage_file = fopen(usage_file_name, "rb")) == NULL)
    {
	perror(usage_file_name);
	return(1);
    }
    if ((output_file = fopen(output_file_name, "w+")) == NULL)
    {
	perror(output_file_name);
	return(1);
    }
    sys$asctim(&retlen, &time_desc, 0, 0);
    result_time[retlen] = 0;    
    fprintf(output_file,"Diskmon Disk Usage %s at %s\n",
							VERSION,
							result_time);
    fprintf(output_file,"\n");
    GetHeaderRec(usage_file,&headerrec);
    PutHeader(output_file,headerrec);
    sprintf(subject_string,"Diskmon Disk Usage %s on %.12s at %s",
							VERSION,
							headerrec.usg$t_volname,
							result_time);
    while(GetFileRec(usage_file,&filerec))
    {
	if (display_files)
	{
	    /* Display Files						    */
	    if (first_title)
	    {
		first_title = FALSE;
		fprintf(output_file,"\n");
		fprintf(output_file,"File List\n");
		fprintf(output_file,"---------\n");
		fprintf(output_file,"%-32s\t%-14s\t%-6s\t%-7s\t%-6s\n",
								title1,
								title2,
								title3,
								title4,
								title5);
	    }
	    PutFile(output_file,filerec);
	}
	/* Checking for Lost files as then have a directory length of 2 eg  */
	/* file spec is []file.type					    */
	if (filerec.usg$w_dir_len == 2)
	{
	    /* Set lost files to no directory files uic			    */
	    filerec.usg$l_fileowner = NODIR_FILES_UIC;
	}
	add_to_total(place_in_list,
		     filerec.usg$l_fileowner,
		     filerec.usg$l_used,
		     filerec.usg$l_allocated); 
    }
    fclose(usage_file);
    if (display_totals) PutTotals(output_file,place_in_list);
    fclose(output_file);
    if (send_mail)
    {
	if((m_status = mail_send_message(&MAILTO_STRING_DESC,
					 &SUBJECT_STRING_DESC,
					 &OUTPUT_FILE_DESC)) != SS$_NORMAL)
	{
	    lib$signal(m_status);
	}
    }
    return;
}


--
+-----------------------------------------------------------------------+
Martin Millson (Morgul)           | Easynet: vogon::millson
International Systems Engineering | Internet: millson@vogon.enet.dec.com 
Digital Equipment Corporation     | Phone: +44 734 203258
"Out of the Storm rode the black nine"
Disclaimer:
"This is me talking not Digital Equipment Corp."
+-----------------------------------------------------------------------+