21
Sat, Dec
3 New Articles

The API Corner: The Case of the Missing Stream Files

APIs
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

The QjoRetrieveJournalEntries API offers flexibility in terms of tracking IFS usage.

 

Earlier this month, a question was posed over on MIDRANGE-L related to a user having trouble with IFS objects mysteriously disappearing. The question was if there was a way "to journal or otherwise monitor an IFS directory, that would tell us the who, what, when, where, and why of those deletions?" The system can't help very much with the "why" part of the question, but with journaling the system can certainly provide information related to who, what, when, and where. And with that information, hopefully the "who" can help to explain "why."

 

Today's article will introduce you to the Retrieve Journal Entries (QjoRetrieveJournalEntries) API which, for V5R4, is documented here. The API itself has been available since V4R4. In addition to using this API, you can also access file removal information by using facilities such as the system audit journal, displaying the IFS journal information to an outfile, etc. But as this is the "API Corner," I'm sure you can understand why I've chosen to use the API.

 

To set the scenario, let's say you have IFS directory /MyPlayDir, which is not currently being journaled. To start journaling, we will create the journal receiver IFS1, create the journal IFSJRN, and then start journaling of the MyPlayDir directory, along with any subdirectories of MyPlayDir. The commands you might use to accomplish these actions are these:

 

CRTJRNRCV JRNRCV(IFS1)

 

CRTJRN JRN(IFSJRN) JRNRCV(IFS1)

 

STRJRN OBJ(('/MyPlayDir')) JRN('/qsys.lib/vining.lib/ifsjrn.jrn') +

      SUBTREE(*ALL)

 

Having started journaling of the MyPlayDir directory, what we need now is a program that will tell us, for all files removed from MyPlayDir (and subdirectories of MyPlayDir), information such what's shown below using the RPG DSPLY operation code.

 

DSPLY  Job QPADEV000F/VINING/095249 removed *STMF 

DSPLY  MyMissingFile.txt                          

DSPLY  at 2011-09-04-11.11.14.734704              

DSPLY  using PGM123                           

DSPLY  Path was /MyPlayDir/MyImbeddedDir           

 

These results show that job QPADEV000F/VINING/095249, while running program PGM123, removed the stream file MyMissingFile.txt from subdirectory MyImbeddedDir of /MyPlayDir shortly after 11:00 on September 4. As I mentioned, we don't really know "why" MyMissingFile.txt was removed, but we certainly now know quite a bit about the removal itself.

 

The complete program generating the previous messages, Display File Removal (DSPFRMV), is provided below. While we won't be able to review the entire program this month for space reasons, you will have a working program that you can look at if you are so inclined.

 

h dftactgrp(*no) bnddir('QC2LE')                                      

                                                                       

dRtvJrnE          pr                  extproc('QjoRetrieveJournalEntries')

d RcvVarPtr                       *   value                           

d LenRcvVar                     10i 0 const                           

d Journal                       20a   const                           

d Format                         8a   const                           

d JrnEtoRtv                  65535a   const options(*omit)            

d QUSEC                               likeds(QUSEC) options(*omit)                  

                                                                      

dGetPath          pr              *   extproc('Qp0lGetPathFromFileID')

d RcvVar                         1                                    

d LenRcvVar                     10u 0 value                           

d FileID                        16a   const                           

                                                                      

dGetErrno         pr              *   extproc('__errno')              

                                                         

dPathText         s           1000                       

dRcvVarPtr        s               *                      

dLenRcvVar        s             10i 0 inz(10000000)      

dObject           s            640                       

dPath             s          65535                       

dWait             s              1                       

dX                s             10i 0                    

dSavSeqNoPtr      s               *                      

dLstSeqNoChr      s             20                       

dNullPtr          s               *                      

dErrnoPtr         s               *                      

dErrno            s             10i 0 based(ErrnoPtr)    

                                                         

dRcvVarHdr        ds                  likeds(QJO0100H)   

d                                     based(RcvVarPtr)   

                                                          

dEntHdrPtr        s               *                                

dEntHdr           ds                  qualified based(EntHdrPtr)   

d Hdr                                 likeds(QJO00JEH)             

                                                                    

dEntSpcDtaPtr     s               *                                

dEntSpcDta        ds                  qualified based(EntSpcDtaPtr)

d Hdr                                 likeds(QJOJEESD)             

                                                                    

dESD_Ptr          s               *                                

dESD_8            ds                  qualified based(ESD_Ptr)     

d ObjFID                        16                                  

d ParentFID                     16                                 

d NameDsp                       10u 0                              

d ObjJID                        10                                 

d ObjType                        7                                  

                                                                   

dObjNameDtaPtr    s               *                                

dObjNameDta       ds                  based(ObjNameDtaPtr)         

d ObjNameLen                    10u 0                              

d ObjNameCCSID                  10i 0                              

d ObjNameCntryID                 2a                                

d ObjNameLangID                  3a                                

d                                3a                                

d ObjName                      640c   ccsid(1200)                  

                                                                   

dJrnEtoRtv        ds         65535    qualified                     

d Hdr                                 likeds(QJOJEJIR)             

                                                                   

dJrnEtoRtvKeyHdrPtr...                                             

d                 s               *                                 

dJrnEtoRtvKeyHdr  ds                  likeds(QJOEFVLR)             

d                                     based(JrnEtoRtvKeyHdrPtr)    

                                                                   

dJrnEtoRtvKeyHdrDtaPtr...                                          

d                 s               *                                 

                                                                    

dGetByStrSeqNo    ds                  qualified                      

d                                     based(JrnEtoRtvKeyHdrDtaPtr)  

dStrSeqNoChr                    20                                  

dStrSeqNoNbr                    20s 0 overlay(StrSeqNoChr :1)       

                                                                     

dGetByJrnType     ds                  qualified                     

d                                     based(JrnEtoRtvKeyHdrDtaPtr)  

d Hdr                                 likeds(QJOJEDK8)              

d JrnTypes                      10    dim(300)                      

                                                                    

dJournal          ds                                                

d JrnName                       10    inz('IFSJRN')                  

d JrnLib                        10    inz('*LIBL')                  

                                                                    

 /copy qsysinc/qrpglesrc,qjournal                                   

 /copy qsysinc/qrpglesrc,qusec                                   

                                                                

 /free                                                          

                                                                

  QUSBPrv = 0;                                                   

                                                                

  JrnEtoRtv.Hdr.QjoNVLR00 = 2;                                  

  JrnEtoRtvKeyHdrPtr = %addr(JrnEtoRtv) + %size(JrnEtoRtv.Hdr); 

                                                                 

  // Starting sequence number                                   

  JrnEtoRtvKeyHdr.QjoLVLR00 = 32;                               

  JrnEtoRtvKeyHdr.QjoK01 = 2;                                   

  JrnEtoRtvKeyHdr.QjoLOD00 = 20;                                

                                                                

  JrnEtoRtvKeyHdrDtaPtr = JrnEtoRtvKeyHdrPtr +                  

                            %size(JrnEtoRtvKeyHdr);             

  GetByStrSeqNo.StrSeqNoChr = '*FIRST';                         

  SavSeqNoPtr = JrnEtoRtvKeyHdrDtaPtr;                       

                                                             

  // Journal entry type B4                                   

  JrnEtoRtvKeyHdrPtr += JrnEtoRtvKeyHdr.QjoLVLR00;           

  JrnEtoRtvKeyHdr.QjoK01 = 8;                                

  JrnEtoRtvKeyHdr.QjoLOD00 = 14;

  if %rem(JrnEtoRtvKeyHdr.QjoLOD00 :4) = 0;                

     JrnEtoRtvKeyHdr.QjoLVLR00 = %size(JrnEtoRtvKeyHdr) +  

                                 JrnEtoRtvKeyHdr.QjoLOD00; 

  else;                                                    

     JrnEtoRtvKeyHdr.QjoLVLR00 =                           

        %size(JrnEtoRtvKeyHdr) + JrnEtoRtvKeyHdr.QjoLOD00 +

        (4 - %rem((%size(JrnEtoRtvKeyHdr) +                

                   JrnEtoRtvKeyHdr.QjoLOD00) :4));         

  endif;

                                                             

  JrnEtoRtvKeyHdrDtaPtr = JrnEtoRtvKeyHdrPtr +                

                            %size(JrnEtoRtvKeyHdr);          

  GetByJrnType.Hdr.QjoNbrIA00 = 1;                           

  GetByJrnType.JrnTypes(1) = 'B4';                           

                                                              

  RcvVarPtr = %alloc(LenRcvVar);                             

                                                             

  dou RcvVarHdr.QjoCH = '0';                                 

      RtvJrnE(RcvVarPtr :LenRcvVar :Journal :'RJNE0100'      

              :JrnEtoRtv :QUSEC);                                    

                                                                     

      EntHdrPtr = RcvVarPtr + RcvVarHdr.QjoOfJE;                     

                                                                      

      for X = 1 to RcvVarHdr.QjoNbrER;                               

          EntSpcDtaPtr = EntHdrPtr + EntHdr.Hdr.QjoDESD;             

          ESD_Ptr = %addr(EntSpcDta.Hdr.QJOESD);                     

                                                                      

          ObjNameDtaPtr = ESD_Ptr + ESD_8.NameDsp;                   

          if ObjNameCCSID = 1200;                                    

             Object = %char(%subst(ObjName :1 :%div(ObjNameLen :2)));

          endif;                                                     

                                                                     

          dsply ('Job ' + %trimr(EntHdr.Hdr.QjoJN02) + '/' +         

                          %trimr(EntHdr.Hdr.QjoUN) + '/' +           

                          %trimr(EntHdr.Hdr.QjoJNbr) +               

                 ' removed ' + %trimr(ESD_8.ObjType));               

          dsply (%subst(Object :1 :52));                             

          dsply ('at ' + EntHdr.Hdr.QjoTS);                         

          dsply ('using ' + EntHdr.Hdr.QjoPgmN);                    

                                                                    

          if GetPath(Path :%size(Path) :ESD_8.ParentFID) = NullPtr; 

             ErrnoPtr = GetErrno();                                 

          else;                                                     

             PathText = %str(%addr(Path));                          

             dsply ('Path was ' + %subst(PathText :1 :42)) ' ' Wait;

          endif;                                                    

                                                                    

          LstSeqNoChr = EntHdr.Hdr.QjoSNbr;                         

          EntHdrPtr += EntHdr.Hdr.QjoDNJH;                          

      endfor;                                                       

                                                                    

      if RcvVarHdr.QjoCH = '1';                                     

         select;                                                    

            when RcvVarHdr.QjoNbrER = 0;                              

                 // RcvVar not large enough to return even one entry...

                                                                      

                 dsply 'Unable to access journal entries' ' ' Wait;   

                 leave;                                               

                                                                       

            when X > RcvVarHdr.QjoNbrER;                              

                 // Get next set of journal entries                   

                                                                       

                 JrnEtoRtvKeyHdrDtaPtr = SavSeqNoPtr;                 

                 GetByStrSeqNo.StrSeqNoNbr =                          

                   (%dec(LstSeqNoChr :20 :0) + 1);                    

                 iter;                                                 

            other;                                                    

         endsl;                                                       

      endif;                                                          

  enddo;                                                              

                         

  dealloc RcvVarPtr;     

  *inlr = *on;           

  return;                

                         

 /end-free               

 

Assuming that your source member name is DSPFRMV, you can compile and run the sample program with the following commands.

 

CRTBNDRPG PGM(DSPFRMV)

CALL DSPFRMV

 

As coded, the sample program will DSPLY all deletions journaled to the IFSJRN journal.

 

The program starts by formatting a call to the QjoRetrieveJournalEntries API. For a retrieve type API, QjoRetrieveJournalEntries is fairly standard in terms of the parameters passed to it. The parameter list is a receiver variable to receive the journal entries, the length of the receiver variable, the qualified name of the journal to be used, the format to be used in returning the journal entries, an omissible parameter defining those journal entries to be retrieved, and an omissible error code parameter.

 

The fifth parameter, Journal entries to retrieve, provides an extremely flexible method to describe what journal entries are to be returned to our program. By default, all journal entries are returned, but, as we're interested today solely in file removals, we will use this parameter to restrict the returned entries to actions related to removing links from the MyPlayDir (and subdirectories of MyPlayDir) directory.

 

The Journal entries to retrieve parameter uses keyed variable-length records to define those journal entries to return. The first element of the parameter is a header record that defines how many of these variable-length records we will be using on the call to the API. A definition of this header record is provided in the QSYSINC/QRPGLESRC member QJOURNAL and is used by the DSPFRMV program (with LIKEDS(QJOJEJIR)) to define the data structure JrnEtoRtv (Journal entries to retrieve) with the header record Hdr. The IBM-provided structure, and our use of the structure, is this:

 

DQJOJEJIR         DS                                                   

D*                                             Qjo JE Jrn Info Retrieve

D QJONVLR00               1      4B 0                                 

D*                                             Num Var Len Rcrds      

 

dJrnEtoRtv        ds         65535    qualified                    

d Hdr                                 likeds(QJOJEJIR)             

 

The data structure JrnEtoRtv is arbitrarily defined with a length of 65535, more than sufficient to contain the variable-length records we will be using in our program. Today, we'll use two variable-length records, and DSPFRMV sets the variable JrnEtoRtv.Hdr.QjoNVLR00 to a value of 2.

 

JrnEtoRtv.Hdr.QjoNVLR00 = 2;                                  

 

Each variable-length record within the Journal entries to retrieve parameter uses a standard record layout with the fixed-length portion of each record defined by the data structure QJOEFVLR within QSYSINC/QRPGLERC QJOURNAL. This common record layout defines the length of the current variable-length record (used by the API to access the "next" variable length record), the key of the variable-length record (used to identify the format of the data associated with this variable-length record), and the length of the data associated with the key. The IBM-provided structure, and DSPFRMV's use of the structure to define the based structure JrnEtoRtvKeyHdr, is this:

 

DQJOEFVLR         DS                                                 

D*                                             Qjo JE Fmt Var Len Rcrd

D QJOLVLR00               1      4B 0                                

D*                                             Len Var Len Rcrd      

D QJOK01                  5      8B 0                                

D*                                             Key                   

D QJOLOD00                9     12B 0                                

D*                                             Len Of Data           

D*QJODATA01              13     13                                    

D*                                             Data                  

 

dJrnEtoRtvKeyHdrPtr...                                             

d                 s               *                                

dJrnEtoRtvKeyHdr  ds                  likeds(QJOEFVLR)             

d                                     based(JrnEtoRtvKeyHdrPtr)    

 

The JrnEtoRtvKeyHdr data structure is based so that we can use this common definition, by way of the pointer JrnEtoRtvKeyHdrPtr, to populate the Journal entries to retrieve parameter with appropriate variable-length records describing those journal entries to return. The IBM-provided structure defines a commented QJODATA01 variable to indicate where the data associated with the variable-length record starts. The actual layout of the associated data is not provided in structure QJOEFVLR as it is dependent on the key value we use.

 

To initialize the first variable-length record of JrnEtoRtv, DSPFRMV uses the following:

 

  JrnEtoRtvKeyHdrPtr = %addr(JrnEtoRtv) + %size(JrnEtoRtv.Hdr); 

                                                                

  // Starting sequence number                                   

  JrnEtoRtvKeyHdr.QjoLVLR00 = 32;                               

  JrnEtoRtvKeyHdr.QjoK01 = 2;                                   

  JrnEtoRtvKeyHdr.QjoLOD00 = 20;                                

                                                                

DSPFRMV first sets the pointer JrnEtoRtvKeyHdrPtr to the first byte of JrnEtoRtv following the Hdr record to address where the first variable-length record will begin. The program then initializes the length of the variable-length record to 32 (QjoLVLR00), the key of the variable-length record to 2 (QjoK01), and the length of the data associated with key 2 to 20 (QjoLOD00). In explaining why these values were chosen, we will start with the key value of 2.

 

The QjoRetrieveJournalEntries API supports a wide variety of key values to control the type of journal entries to be returned. The list of key values available to you can be found in the API documentation. The key value of 2, Starting sequence number, enables you to specify the first journal sequence number that is to be considered for retrieval. You can specify a journal sequence number or the special value *FIRST. *FIRST indicates that all entries should be considered for inclusion based on the specified journal receiver range (which can be controlled with the variable-length record associated with a key value of 1 and defaults to the current journal receiver if not specified). In our case, we're using the special value of *FIRST for the starting sequence number.

 

I should point out that we do not really need to specify this particular variable-length record at this point in the program. We could have just used one variable-length record (the one we'll be initializing next) for our initial call to the API. The API default, when no key 2 variable-length record is specified, is to start with the first entry available, which is what we're asking for. We may, however, need to use a Starting sequence number key later in the program, so I chose to simply initialize the variable-length record now as opposed to later (and save the location of the starting sequence number using pointer variable SavSeqNoPtr). The reason for possibly needing the Starting sequence number key later is that our receiver variable may, or may not, be large enough for all removal-related journal entries that are available to be returned. If the allocated receiver variable length does prove to be insufficient in size, then we will be using key 2, when calling the API a subsequent time, to resume with the journal entry following the last entry returned on the previous API call.

 

The value of 20 for QjoLOD00, Length of data, was chosen for the simple reason that, per the API documentation, 20 bytes is the required size of the data associated with key 2.

 

The value of 32 for QjoLVLR00 was determined using the length of the JrnEtoRtvKeyHdr (12 bytes) plus the length of the associated data (20 bytes). This simple addition may not be sufficient in all cases, though. The API documents that each variable-length record must be aligned on a four-byte boundary and, in the case of key 2, the length of the associated data (20) is already a multiple of four. For this reason, we can simply add the length of the associated data to the size of the header (JrnEtoRtvKeyHdr) and use this value (32) as the length of the variable-length record (which the API then uses to access the next variable-length record). In other cases—for instance, key 8, Journal entry types—the length of the associated data is variable in length. If the length of the associated data does not happen to be a multiple of four, then we need to provide for padding of the associated data to ensure that the next variable-length record is properly aligned. To accomplish this, we can calculate a valid value for QjoLVLR00 using approaches such as this one:

 

  if %rem(JrnEtoRtvKeyHdr.QjoLOD00 :4) = 0;                

     JrnEtoRtvKeyHdr.QjoLVLR00 = %size(JrnEtoRtvKeyHdr) +  

                                 JrnEtoRtvKeyHdr.QjoLOD00; 

  else;                                                    

     JrnEtoRtvKeyHdr.QjoLVLR00 =                           

        %size(JrnEtoRtvKeyHdr) + JrnEtoRtvKeyHdr.QjoLOD00 +

        (4 - %rem((%size(JrnEtoRtvKeyHdr) +                

                   JrnEtoRtvKeyHdr.QjoLOD00) :4));         

  endif;

 

This approach, calculating a valid length, could of course be used rather than hard-coding a value of 32 as was done in DSPFRMV.

 

Having set the appropriate values for the JrnEtoRtvKeyHdr subfields, DSPFRMV then sets the value of the key associated data using these statements:

 

  JrnEtoRtvKeyHdrDtaPtr = JrnEtoRtvKeyHdrPtr +                  

                            %size(JrnEtoRtvKeyHdr);             

  GetByStrSeqNo.StrSeqNoChr = '*FIRST';                         

 

The pointer JrnEtoRtvKeyHdrDtaPtr is set to the address where the first byte of the associated data value is to be stored by adding the size of the variable-length header to the starting address of the header. As JrnEtoRtvKeyHdrDtaPtr is the basing pointer for data structure GetBGyStrSeqNo, the special value '*FIRST' is then written to GetByStrSeqNo.StrSeqNoChr.

 

We're now ready to initialize the header of the second variable-length record. The approach taken to set this header is similar to how the header was created for the first variable-length record though this time using a calculated value for QjoLOD00.

 

  JrnEtoRtvKeyHdrPtr += JrnEtoRtvKeyHdr.QjoLVLR00;           

  JrnEtoRtvKeyHdr.QjoK01 = 8;                                

  JrnEtoRtvKeyHdr.QjoLOD00 = 14;                             

  if %rem(JrnEtoRtvKeyHdr.QjoLOD00 :4) = 0;                

     JrnEtoRtvKeyHdr.QjoLVLR00 = %size(JrnEtoRtvKeyHdr) +  

                                 JrnEtoRtvKeyHdr.QjoLOD00; 

  else;                                                    

     JrnEtoRtvKeyHdr.QjoLVLR00 =                           

        %size(JrnEtoRtvKeyHdr) + JrnEtoRtvKeyHdr.QjoLOD00 +

        (4 - %rem((%size(JrnEtoRtvKeyHdr) +                

                   JrnEtoRtvKeyHdr.QjoLOD00) :4));         

  endif;                                                    

 

The program…

 

  1. Increments the pointer variable JrnEtoRtvKeyHdrPtr to address the start of the next variable-length record by adding the length of the current variable-length record to the current value of the JrnEtoRtvKeyHdrPtr pointer
  2. Identifies the key value being used as 8, key value 8 being to filter by journal entry type
  3. Sets the length of the key associated data to 14
  4. Sets the length of the new variable length record to the calculated value of 28 (which is adding two bytes of padding to the end of the associated data in order to align the next variable-length record on a 4-byte boundary)

 

In the case of key 8, the associated data is a data structure comprised of an integer value indicating how many journal entry types are being specified followed by that number of journal entry types, where each entry type is 10 bytes in length. The IBM-provided definition for this associated data, the use of this definition by DSPFRMV (allowing up to 300 entries to be specified), and the setting of the associated data is as follows:

 

DQJOJEDK8         DS                                               

D*                                             Qjo JE Data Key 8   

D QJONBRIA00              1      4B 0                              

D*                                             Num In Array        

 

dGetByJrnType     ds                  qualified                     

d                                     based(JrnEtoRtvKeyHdrDtaPtr)  

d Hdr                                 likeds(QJOJEDK8)              

d JrnTypes                      10    dim(300)                      

 

  JrnEtoRtvKeyHdrDtaPtr = JrnEtoRtvKeyHdrPtr +               

                            %size(JrnEtoRtvKeyHdr);          

  GetByJrnType.Hdr.QjoNbrIA00 = 1;                            

  GetByJrnType.JrnTypes(1) = 'B4';                           

 

To set the key associated data value for key 8, DSPFRMV:

 

  1. Increments the pointer variable JrEtoRtvKeyHdrDtaPtr to address the first byte of the associated data
  2. Sets the number of journal entry types provided to 1
  3. Sets the first element of the JrnTypes array to the value 'B4', with B4 representing journal entries for removing a link from a parent directory

 

Having set all necessary values for the Journal entries to retrieve parameter DSPFRMV allocates a receiver variable 10,000,000 bytes in size (a totally arbitrary number, but a number sufficiently large to allow the API to return at least one journal entry) and calls the QjoRetrieveJournalEntries API.

 

Next month, we'll look at how the DSPFRMV program processes the returned journal entries. As mentioned earlier, the actual processing logic we'll review next month can be found in the program source provided above, so feel free to "skip ahead."

 

As usual, if you have any API questions, send them to me at This email address is being protected from spambots. You need JavaScript enabled to view it.. I'll see what I can do about answering your burning questions in future columns.

 as/400, os/400, iseries, system i, i5/os, ibm i, power systems, 6.1, 7.1, V7, V6R1

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$

Book Reviews

Resource Center

  • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

  • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

  • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

  • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

  • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

  • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

  • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

  • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

  • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

  • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

  • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

  • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: