The IFS replication process is going along pretty well and we have a number of new features in test which should make the final implementation of a real time replication process pretty good. One of the problems we faced was how to ensure the object auditing values were correctly set for all objects below a directory, if you create a new object in the directory you need to make sure its auditing value is correctly set otherwise you will not see the changes to the object in the audit journal.
To ensure new objects are changed to have the correct setting we needed to set the *CRTOBJAUD flag for the directory. The CHGATR command is provided to set the attribute but had few shortcomings.
The initial programs we created simply took the path we were interested in and used the CHGATR command with SUBTREE(*ALL), while this did work it had an annoying side effect of sending messages for every non *DIR object in the path! We could have simply monitored for each of the messages and removed them from the users view but that seemed like a lot of work?
The process we use to set the initial directory mapping up uses a combination of opendir() and readdir() to display a list of all directories, options are provided to walk down the sub directories where you can set *INCLUDE or *EXCLUDE against the individual directories. Our main rule is that if a directory is set for *INCLUDE all sub directories below are automatically selected. An *EXCLUDE selection only excludes the selected directory not all sub directories. Setting the auditing flag on initial selection was fairly easy as the command CHGAUD command allows subtree processing with fairly minimal messaging being returned. However we also needed to set the *CRTOBJAUD flag for all subdirectories to ensure every new object created below the base path would be captured and replicated to the target system. The CHGATR command does support the SUBTREE parameter, but as we have mentioned above, it results in a message for every object which is not suitable for the attribute setting.
We had looked at the Qp0lProcessSubtree() API in the past and discounted it due to some problems in getting the API to function as we wanted it to, however we felt that this would be the correct solution in this instance so we set about creating a couple of functions which would allow us to carry out the task in hand.
There is a good sample in the UNIX type API’s section which shows how to use the API with a function as the exit point. This was a very good starting point which resulted in the following code.
First of all we had to call a function which would determine the right exit point to call, we had to either set the value to *CHANGE or *NONE for each directory we found. We created an initial function which was passed a pointer to the directory we were interested in plus the setting the directory and it’s sub directories had to be set to.
The functions are part of a bigger suite of functions so while they compile in our test environment you may have to make some changes to allow it to compile in yours. These are also non production functions at the moment which will be enhanced for better error control etc in the future.
Here are some of the important structures etc we used in the functions.
#include /* Object information */
#include /* Qlg structs */
#include /* ccsid conversion */
/* other headers not described */
#define PATH_TYPE_CHAR 0x00000000
#define PATH_TYPE_POINTER 0x00000001
#define PATH_TYPE_CHAR_2 0x00000002
#define PATH_TYPE_POINTER_2 0x00000003
typedef union pName_Type {
char pName_Char[2048];
char *pName_Ptr;
};
typedef _Packed struct pName_Struct_x {
Qlg_Path_Name_T qlgStruct;
union pName_Type Path;
} pName_Struct_t;
typedef _Packed struct Objtypes_List_x {
uint Number_Of_Objtypes;
char Objtype[2][11];
} Objtypes_List_t;
Here is the function which is called first from another function.
/**
* (function) Set_Dir_Atr
* set the *CRTOBJAUT Attribute on subdirectories
* @parms
* 1 directory to start from
* 2 attribute value
* returns 1 if the directory was set OK
*/
int Set_Dir_Atr(char *cwd, char *type) {
int rc = 0; /* return code */
char msg_dta[255]; /* msg data */
IFS_Path_t Path; /* path name struct */
Objtypes_List_t MyObj_types; /* obj types struct */
Qp0l_User_Function_t User_function; /* user function struct */
struct {
uint DataIn;
uint DataOut;
} CtlBlkAreaName;
/* set up the function call */
memset((void *)&User_function, 0x00, sizeof(Qp0l_User_Function_t));
User_function.Function_Type = QP0L_USER_FUNCTION_PTR;
User_function.Mltthdacn[0] = QP0L_MLTTHDACN_NOMSG;
if(memcmp(type,"*CHANGE",7) == 0)
User_function.Procedure = &Set_Dir_Atr_Chg;
else
User_function.Procedure = &Set_Dir_Atr_None;
/* set up the path name struct */
memset((void*)&Path, 0x00, sizeof(Path));
Path.Path_Dets.CCSID = 0;
Path.Path_Dets.Path_Type = 0;
Path.Path_Dets.Path_Length = strlen(cwd);
memcpy(Path.Path_Dets.Path_Name_Delimiter,"/ ",2);
memcpy(Path.Path_Name,cwd,strlen(cwd));
/* set up the object types */
MyObj_types.Number_Of_Objtypes = 2;
memcpy(&MyObj_types.Objtype[0],"*DIR ",11);
memcpy(&MyObj_types.Objtype[1],"*NOQSYS ",11);
if(rc = Qp0lProcessSubtree((Qlg_Path_Name_T *)&Path,
QP0L_SUBTREE_YES,
(Qp0l_Objtypes_List_t *)&MyObj_types,
QP0L_LOCAL_REMOTE_OBJ,
(Qp0l_IN_EXclusion_List_t *)NULL,
QP0L_PASS_WITH_ERRORID,
&User_function,
&CtlBlkAreaName) == 0) {
sprintf(msg_dta,"Process successful");
snd_msg("GEN0001",msg_dta,strlen(msg_dta));
}
else {
sprintf(msg_dta,"ERROR on Qp0lProcessSubtree(): error = %d\n", errno);
snd_msg("GEN0001",msg_dta,strlen(msg_dta));
return -1;
}
return 1;
}
You will notice we have passed in a selection list, we are only interested in *DIR types plus we want to ignore the QSYSLIB types when it is passed a path which is part of an iASP. We have also mapped it for all sub directories (QP0L_LOCAL_REMOTE_OBJ), we expect this to be changed to QP0L_LOCAL_OBJ in production as we are only interested in the local IFS.
Next we needed a function for each request which would set the attribute accordingly. This is the function for the *CHANGE request, the *NONE request is the same except for the value being set in the command. We are looking at the use of the Controlblock to pass in a value which would decide the setting, but that’s another day.
/**
* (function) Set_Dir_Atr_Chg
* set the *CRTOBJAUT Attribute on subdirectories within a directory
* @parms
*
* returns 0 if the directory was set OK
*/
void Set_Dir_Atr_Chg(uint *Sel_sts,
uint *Err_val,
uint *Ret_val,
Qlg_Path_Name_T *Obj_name,
void *Func_ctl_blk) {
int i = 0; /* counter */
int len = 0; /* counter */
int ret = 0; /* return value */
size_t insz; /* path len */
size_t outsz = 2048; /* converted outbuf size */
char outbuf[2048]; /* output buffer */
char *outbuf_ptr; /* ptr to output buffer */
iconv_t cd; /* convert struct */
size_t ret_iconv; /* returned value */
char cmd[255]; /* command string */
char msg_dta[255]; /* message data */
char *path_ptr; /* ptr to path string */
pName_Struct_t *pName; /* Path name struct */
QtqCode_T toCode = {37,0,0,0,0,0}; /* CCSID to struct */
QtqCode_T fromCode = {61952,0,0,1,0,0}; /* CCSID from struct */
if(*Sel_sts == QP0L_SELECT_OK) {
if(Obj_name != NULL) {
pName = (pName_Struct_t *)Obj_name;
if(Obj_name->Path_Type & PATH_TYPE_POINTER)
path_ptr = pName->Path.pName_Ptr;
else
path_ptr = (char *)pName->Path.pName_Char;
/* convert to US CCSID */
insz = pName->qlgStruct.Path_Length;
outbuf_ptr = (char *)outbuf;
memset(outbuf_ptr, 0x00, insz);
cd = QtqIconvOpen(&toCode,&fromCode);
if(cd.return_value == -1) {
*Ret_val = errno;
return;
}
ret_iconv = (iconv(cd,(char **)&(path_ptr),&insz,(char **)&(outbuf_ptr),
&outsz));
if(ret_iconv != 0) {
ret_iconv= iconv_close(cd);
*Ret_val = errno;
return;
}
ret_iconv = iconv_close(cd);
strcpy(cmd,"CHGATR OBJ('");
strcat(cmd,outbuf);
strcat(cmd,"') ATR(*CRTOBJAUD) VALUE(*CHANGE)");
ret = Issue_Cmd(cmd);
if(ret != 1) {
sprintf(msg_dta,"Failed to set *CHANGE - %s",outbuf);
snd_msg("GEN0001",msg_dta,strlen(msg_dta));
*Ret_val = -1;
}
}
}
*Ret_val = 0;
}
This function is called for each directory returned from the API, initial testing has shown no problems so far! We did have some problems with the Path Pointer which we have yet to determine the reason for, if we left out the conversion routines the path could not be correctly mapped? If you want to look at this and let us know what you find please be our guest.. It works for now so getting down to the nitty gritty of the issues is less important for us.. You should also consider the CCSID conversion as this is particular to our system and may not work on yours.
Thats it, we can now set the attribute correctly for the IFS directories without the masses of error messages we got when we simply called the CHGATR with SUBTREE(*ALL)! If you have any comments or want to add any changes to the code for others to see please feel free to comment..
Hope this helps others who like use struggled initially with the API.
Chris…