/******************************************************************/ /* OS/400 Servers - Sample File Server Exit Program */ /* Exit Point Name: QIBM_QPWFS_FILE_SERV */ /* */ /* Description: The following C program shows how to */ /* process the file server exit program */ /* parameters. It can be used as a shell */ /* for developing a file server exit program */ /* tailored for your operating environment. */ /* The program includes an example of */ /* converting the unicode file name */ /* (CCSID 61952) to US EBCDIC (CCSID 037). */ /* */ /* Input: Data Area QGPL/FILEEXIT: The first */ /* character is read (if the data area) */ /* to determine type of output. */ /* */ /* Output: Output varies according to the first */ /* character in the data area: */ /* 'Y': Exit point information is printed */ /* to a flat file: QGPL/FILEEXIT */ /* (In a production environment, a user */ /* defined journal entry should be used */ /* instead as it offers much better */ /* performance.) */ /* other: Return w/o logging information. */ /* */ /* References: SC41-3740 OS/400 Server Concepts and */ /* Administration, Appendix A. */ /* SC41-4801 System API Reference R360 */ /* National Language Support APIs */ /* Chapter 47: CDRA apis */ /* Chapter 46: NL Conversion */ /* NOTE: R310 manual did not document the */ /* CDRCVRT/QTQCVRT api. */ /* SC09-1390 Character Data Representation */ /* Architecture Level 2. */ /* TO USE THE SAMPLE: 1) Create the data area FILEEXIT and */ /* place a 'Y' in the first character */ /* to enable the debug log */ /* CRTDTAARA DTAARA(QGPL/FILEEXIT) */ /* TYPE(*CHAR) LEN(1) */ /* CHGDTAARA DTAARA(QPGL/FILEEXIT (1 1)) */ /* VALUE('Y') */ /* 2) Create the log file */ /* CRTPF FILE(QGPL/FILEEXIT) RCDLEN(132) */ /* 3) Copy the file to a source file and */ /* compile (requires ILE C) */ /* CRTBNDC PGM(QGPL/FILEEXIT) */ /* 4) Register the exit program */ /* ADDEXITPGM EXITPNT(QIBM_QPWFS_FILE_SERV)*/ /* FORMAT(PWFS0100) PGMNBR(1) */ /* PGM(QGPL/FILEEXIT) */ /* (WRKREGINF if easier... ) */ /* 5) Register the exit program for DOS */ /* CHGNETA PCSACC(*REGFAC) */ /* 10/96 DXD 3.7 */ /******************************************************************/ /******************************************************************/ /* Includes for api declaration and useful structures */ /******************************************************************/ #include #include #include #include /* QSYS file io */ /* include for file server exit. Member EPWFSEP in files H, */ /* QRPGSRC, QRPGLESRC, QCBLSRC and QCBLLESRC in library QSYSINC */ /* NOTE: R310 & R360 are missing some includes. Contact support */ /* for an updated copy. */ #include "QSYSINC/H/EPWFSEP" /* File Server exit structures */ /* QTQxxxx api is functionally equivalent to CDRxxxx. */ /* Function declare for QTQ apis can by found in QSYSINC */ #include "QSYSINC/H/QTQCVRT" /* NL conversion api include */ /* Stat can ONLY be called via ILE C programs. It returns a */ /* structure that we will use to determine the TYPE of object */ /* being accessed. Other languages will need to do a DSPLNK to */ /* a spool file, cpysplf, then open and parse the file. */ #include "QSYSINC/SYS/STAT" /* Unix style IFS api include */ #include "QSYSINC/H/QWCRDTAA" /* Retrieve Data Area */ /* Defines for constants */ #define MAX_FILENAME_LEN 132 /* 132 record length for log */ /******************************************************************/ /* Declare global variables and functions */ /******************************************************************/ /* function declares */ short int ConvertIt(char *, int, char *, int); /* conversion */ short int PrintObjType(char *); /* wrapper for stat() */ char DebugLogOn(void); /* output debug log? */ void xRwrite(_RFILE *, void *, int); /* wrapper for _Rwrite */ /* globals */ Qpwfs_Exit_Parms_t *fsPtr; /* ptr to file exit fmt */ char ebcdic_file_name[MAX_FILENAME_LEN]; /* FILE *qprint; /* output spool file */ char qprint[MAX_FILENAME_LEN +1]; /* log buffer */ _RFILE *ptrFile; /* output log file */ short return_code; /* global return code */ /******************************************************************/ /* Main program */ /* Check if output log file should be printed. */ /* Parse data passed via the exit program call. */ /* Convert file name parameter from UNICODE to EBCDIC */ /* Determine what type of object is being accessed. */ /* Print the information to a log file. */ /******************************************************************/ main (int argc, char * argv[]) { /* initialize the return variable to ok */ strncpy(argv[1], "1",1); /* check whether or not to write the debug log */ if (DebugLogOn() != 'Y') return; /* declare and open debug log */ if ((ptrFile =_Ropen("QGPL/FILEEXIT", "ar"))==NULL) return; /* error opening log file */ /* Set pointer to argv to make more readable. Argv contains */ /* the File Server exit point "format" defined in the */ /* the OS/400 Host Server Concepts manual, Appendix A. */ fsPtr = (Qpwfs_Exit_Parms_t *) argv[2]; /* print input parameters to debug log */ sprintf (qprint, "File Exit Called with:"); xRwrite(ptrFile, &qprint, strlen(qprint)); sprintf (qprint, "\n User : %s", fsPtr->User_Profile); xRwrite(ptrFile, &qprint, strlen(qprint)); sprintf (qprint, "\n Server : %s", fsPtr->Server_ID); xRwrite(ptrFile, &qprint, strlen(qprint)); switch (fsPtr->Function_ID) { case 0: sprintf (qprint, "\n Function: %d Change file attribute request", fsPtr->Function_ID); xRwrite(ptrFile, &qprint, strlen(qprint)); break; case 1: sprintf (qprint, "\n Function: %d Create stream file or create directory req", fsPtr->Function_ID); xRwrite(ptrFile, &qprint, strlen(qprint)); break; case 2: sprintf (qprint, "\n Function: %d Delete File or delete directory request", fsPtr->Function_ID); xRwrite(ptrFile, &qprint, strlen(qprint)); break; case 3: sprintf (qprint, "\n Function: %d List file attribute request (dir cmd)", fsPtr->Function_ID); xRwrite(ptrFile, &qprint, strlen(qprint)); break; case 4: sprintf (qprint, "\n Function: %d Move request", fsPtr->Function_ID); xRwrite(ptrFile, &qprint, strlen(qprint)); break; case 5: sprintf (qprint, "\n Function: %d Open stream request", fsPtr->Function_ID); xRwrite(ptrFile, &qprint, strlen(qprint)); break; case 6: sprintf (qprint, "\n Function: %d Rename request", fsPtr->Function_ID); xRwrite(ptrFile, &qprint, strlen(qprint)); break; case 7: sprintf (qprint, "\n Function: %d Allocate conversation request", fsPtr->Function_ID); xRwrite(ptrFile, &qprint, strlen(qprint)); break; default: sprintf (qprint, "\n Function: %d ERROR - INVALID FUNCTION ID", fsPtr->Function_ID); xRwrite(ptrFile, &qprint, strlen(qprint)); break; } /* if an open (X0005), then log the type of open */ if (fsPtr->Function_ID == QPWFS_OPEN) { sprintf (qprint, "\n Opened with the following type of file access:"); xRwrite(ptrFile, &qprint, strlen(qprint)); sprintf (qprint, "\n Read Access :%c", fsPtr->RD_Open); xRwrite(ptrFile, &qprint, strlen(qprint)); sprintf (qprint, "\n Write Access :%c", fsPtr->WR_Open); xRwrite(ptrFile, &qprint, strlen(qprint)); sprintf (qprint, "\n Read/WriteAccess :%c", fsPtr->RD_WR_Open); xRwrite(ptrFile, &qprint, strlen(qprint)); sprintf (qprint, "\n Delete Allowed :%c", fsPtr->Delete_Allowed); xRwrite(ptrFile, &qprint, strlen(qprint)); } sprintf (qprint, "\n Name Len: %d", fsPtr->Filename_Len); xRwrite(ptrFile, &qprint, strlen(qprint)); /* convert the name to EBCDIC */ if (fsPtr->Filename_Len > 0) /* something to convert */ { return_code = ConvertIt( (char *) argv[2] + 40, /* unicode file name */ fsPtr->Filename_Len, /* len of unicode file name */ ebcdic_file_name, sizeof(ebcdic_file_name)); if (return_code == 0) /* successfull conversion */ { sprintf (qprint, "\n Converted File Name Successfully. File name:"); xRwrite(ptrFile, &qprint, strlen(qprint)); sprintf (qprint, "\n%s", ebcdic_file_name); xRwrite(ptrFile, &qprint, strlen(qprint)); } else /* conversion error */ { sprintf (qprint, "\n Conversion Failed, RC: %d",return_code); xRwrite(ptrFile, &qprint, strlen(qprint)); } } /* end conversion if */ /* Some system administrators want to know the type of object */ /* being accessed (Ex: file or directory?) */ /* See the PrintObjType function below for details. */ if (fsPtr->Filename_Len > 0) /* valid name */ return_code = PrintObjType(ebcdic_file_name); /* PUT CODE TO REJECT A PARTICULAR REQUEST HERE. */ /* This example would reject the user profile GUEST if it */ /* attempts to delete or rename a file or directory. */ /* If (! (strncmp(fsPtr->User_Profile, "GUEST ", 10)) ) */ /* { */ /* If ( (fsPtr->Function_ID == 2) || */ /* (fsPtr->Function_ID == 6)) */ /* strncpy(argv[1], "0", 1); /* reject request */ /* } */ /* fclose(qprint); */ _Rclose (ptrFile); /* close the log file */ return; } /* end main program */ /******************************************************************/ /* functions */ /******************************************************************/ /******************************************************************/ /* Function: ConvertIt */ /* Purpose: Convert the UNICODE file name to EBCDIC */ /* There are three methods that could be used to */ /* convert the file name. */ /* - The iconv() National Language Conversion api */ /* (Callable only via ILE C) */ /* - The Character Data Representation Architecture */ /* (CDRA) API CDRCVRT. */ /* - Service Program equivalent APIs (QTQCVRT) */ /* These are the easiest APIs to work with. */ /* They can be called by all languages on the as/400 */ /* (including CL). They are equivalent to the */ /* CDRA APIs so refer to the CDRA documentation. */ /* This sample program uses the QTQCVRT api. */ /* Input: S1 - pointer to string to be converted */ /* nL1 - Length of string to be converted */ /* S2 - pointer to buffer for converted string. */ /* nL2 - Length of output buffer for converted string. */ /* Return: The API returns a pointer to a 12 byte */ /* value: */ /* 2 byte Primary Return Code; */ /* 2 byte Secondary Return Code; */ /* 8 bytes reserved. */ /* Our function will only return the Primary Return */ /* code (see references for details on the QTQCVRT */ /* api). */ /* 0 - success */ /* > 0 - error */ /* Reference: */ /* SC41-4801 System API Reference R360 */ /* National Language Support APIs */ /* Chapter 47: CDRA apis */ /* Chapter 46: NL Conversion */ /* NOTE: R310 manual did not document the */ /* CDRCVRT/QTQCVRT api. */ /* SC09-1390 Character Data Representation */ /* Architecture Level 2. */ /******************************************************************/ short int ConvertIt(char * S1, int nL1, char * S2, int nL2) { struct FBstruct { /* structure for Feedback parm */ short primary_rc; /* 2 byte primary return code */ short secondary_rc; /* 2 byte secondary return code */ char misc[8]; /* 8 bytes undefined */ }; _Packed struct FBstruct FB; /* Make sure no byte alignment */ int ebcdic = 37; /* US English CCSID */ int unicode = 61952; /* ISO IEC 10646 character set */ int nST1 = 0; /* not a null terminated string */ int nST2 = 1; /* null terminated string */ int nGCCASN = 0; /* default table */ int nL3; /* size of converted string */ int nL4; /* reserved */ char szTemp[5]; /* First, we'll make sure the name fits into our maximum */ /* file name length since we could be passed a length up to */ /* 16 MB! Unicode takes 2 bytes per ascii character so in our */ /* case we'll make it fit the 132 character wide spool file. */ if (nL1 > (2 * MAX_FILENAME_LEN)) nL1 = 2 * MAX_FILENAME_LEN; /* 2 bytes per char */ /* convert the file name to EBCDIC */ QTQCVRT (&unicode, /* Input CCSID */ &nST1, /* Input string not null terminated */ S1, /* Input string to be xlated */ &nL1, /* L1 - length of S1, input string */ &ebcdic, /* Output CCSID */ &nST2, /* 1 for null terminated C-string */ &nGCCASN, /* GCCASN - 0 default table */ &nL2, /* L2 - length of output buffer */ S2, /* S2 */ &nL3, /* L3 - length of converted data */ &nL4, /* L4 - always returns 0 */ &FB); /* FB: Output / feedback */ sprintf (qprint, "\n"); xRwrite(ptrFile, &qprint, strlen(qprint)); sprintf (qprint, "\n\nCalled QTQCVRT API"); xRwrite(ptrFile, &qprint, strlen(qprint)); sprintf (qprint, "\n------------------"); xRwrite(ptrFile, &qprint, strlen(qprint)); sprintf (qprint, "\n EBCDIC File Name Length: %d", nL3); xRwrite(ptrFile, &qprint, strlen(qprint)); sprintf (qprint, "\n Primary return code: %d", FB.primary_rc); xRwrite(ptrFile, &qprint, strlen(qprint)); sprintf (qprint, "\n Secondary rc: %d", FB.secondary_rc); xRwrite(ptrFile, &qprint, strlen(qprint)); return FB.primary_rc; } /******************************************************************/ /* Function: PrintObjType */ /* Purpose: Determine whether the "file name" passed to the */ /* exit program is a file or directory. The stat() */ /* api (See Reference below) is used to retrieve this */ /* information. (We will also retrive the file system */ /* being accessed). */ /* Input: path - fully qualified path passed by exit point. */ /* Return: return code of the call to stat() */ /* 0 - success */ /* Reference: System API Reference */ /* CH 70 Integrated File System APIs. */ /* NOTES: This api can only be called via ILE C compiler. */ /* To retrieve this information from other languages: */ /* Run the command: DSPLNK OUTPUT(*PRINT) */ /* Retrieve the information from the spool file */ /* (or use CPYSPLF to dump to flat file) */ /******************************************************************/ short int PrintObjType(char * path) { struct stat info; int rc; if ((rc = stat(path, &info)) == 0) { /* print some of the information */ sprintf (qprint, "\n"); xRwrite(ptrFile, &qprint, strlen(qprint)); sprintf (qprint, "\n\nstat() called successfully"); xRwrite(ptrFile, &qprint, strlen(qprint)); sprintf (qprint, "\n------------------------"); xRwrite(ptrFile, &qprint, strlen(qprint)); sprintf (qprint, "\n File System ID: %d", (int) info.st_dev); xRwrite(ptrFile, &qprint, strlen(qprint)); sprintf (qprint, "\n Object Type: %s", (char *) info.st_objtype); xRwrite(ptrFile, &qprint, strlen(qprint)); } else /* stat call failed */ { sprintf (qprint, "\n", rc); xRwrite(ptrFile, &qprint, strlen(qprint)); sprintf (qprint, "\n\nstat() call failed. RC: %d", rc); xRwrite(ptrFile, &qprint, strlen(qprint)); } return (short int) rc; } /******************************************************************/ /* Function: DebugLogOn */ /* Purpose: Check whether or not to write the debug log */ /* to a spool file. A data area is used to enable */ /* this feature. The first character of the data */ /* area is read in. If "Y", the debug log is written. */ /* The data used is QGPL/FILEEXIT. */ /* Input: NONE */ /* Return: Y - Write the debug log. */ /* N - Return without writing the log */ /* Reference: System API Reference */ /******************************************************************/ char DebugLogOn() { struct ErrorCodeStruct { /* error code structure */ int bytes_provided; /* bytes provided */ int bytes_available; /* bytes of error info */ char exception_ID[7]; /* msg id */ char reserved; /* reserved */ }; _Packed struct ErrorCodeStruct errorcode = {sizeof(errorcode), 0, /* initialize to 0 bytes */ "NOERROR"}; /* initialize to NOERROR */ struct QWCRDTAA_Output /* structure for data area data */ { int Bytes_Available; /* Total bytes of information */ int Bytes_Returned; /* Amount actually returned */ char Type_Value_Returned[10]; /* *CHAR, *DEC, or *LGL */ char Library_Name[10]; int Length_Value_Returned; int Number_Decimal_Positions; char Value; /* Only using 1 byte of data */ }; _Packed struct QWCRDTAA_Output RetrieveDataAreaOutput; /* format is 10 character data area name and then 10 character */ /* Library name. */ char DataAreaName[] = "FILEEXIT QGPL "; /* Retrieve 1 byte of data from data area */ QWCRDTAA (&RetrieveDataAreaOutput, /* Receiver variable */ sizeof(RetrieveDataAreaOutput), /* size of Rcv var */ DataAreaName, /* data area name */ 1, /* Starting position */ 1, /* Length of data */ &errorcode); /* Error code */ return RetrieveDataAreaOutput.Value; } /******************************************************************/ /* Function: xRwrite */ /* Purpose: Record I/O does not recognize null terminated */ /* strings so initialize output string with spaces. */ /* Input: same as _Rwrite */ /* Return: void */ /******************************************************************/ void xRwrite(_RFILE * ptrTmpFile, void * temp, int len) { int i; if (strlen( (char *) temp) > 0) for (i=strlen((char *) temp); i < MAX_FILENAME_LEN; i++) *(((char *) temp) + i) = ' '; _Rwrite(ptrTmpFile, temp, MAX_FILENAME_LEN); return; }