22
Sun, Dec
3 New Articles

The CL Corner: What IFS Files Have Not Been Used for Three or More Days?

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

Access and manipulate various forms of time to automatically remove files from the IFS or save them.

 

In last month's article, "When Was an IFS File Last Used or Changed?", we saw how a CL program could determine the date and time an IFS file was last opened or modified. Today, we'll take that one step further: finding out which files have not been opened in the last X number of days, where X is a user-specified value. This is the type of information that may help you decide what files are ready to be archived and/or deleted on your system. An example of how to automate the removal of such files is provided at the end of this article.

 

We'll start by creating the command Use Dir Program (USEDIRPGM), which will prompt the user for the directory to analyze and the number of days to use for the analysis. In past articles, we've used the message file USERMSGF to hold messages related to past programs and commands. If you do not currently have this message file on your system, create it now with this command:

 

CRTMSGF MSGF(USERMSGF)

 

With the USERMSGF message file in place, add three messages using the following commands:

 

ADDMSGD MSGID(DIR0001) MSGF(USERMSGF) MSG('Use Dir Program')

ADDMSGD MSGID(DIR0002) MSGF(USERMSGF) MSG('Directory')

ADDMSGD MSGID(DIR0003) MSGF(USERMSGF) MSG('Days')

 

This is the source for the USERMSGF command:

 

CMD        PROMPT(DIR0001)                               

PARM       KWD(DIR) TYPE(*PNAME) LEN(1024) DFT(*CURDIR) +

             SPCVAL((*CURDIR '.')) PROMPT(DIR0002)      

PARM       KWD(DAYS) TYPE(*UINT4) DFT(3) PROMPT(DIR0003) 

 

The command defines two parameters, both of which are optional. The first parameter, DIR, is the name of the directory to be analyzed. The default is your current working directory. You can specify a directory path up to 1024 bytes in length when not using the default directory. The second parameter, DAYS, permits you to specify the number of days, from the current date, that an object within the directory must not have been opened in order to be reported to the user. The default is three (3) days with the minimum supported value being  zero (0) days. You can create the USEDIRPGM command from the above source (assuming the source is in member USEDIRPGM of QCMDSRC) with the command

 

CRTCMD CMD(USEDIRPGM) PGM(DIR3) PMTFILE(USERMSGF)   

 

The command processing program (CPP) for the USEDIRPGM command is program DIR3. The source for the DIR3 program follows, with the changes from last month's DIR2 program shown in bold. In addition to the highlighted changed and added lines, the monitor for MCH3601 used last month has been removed.

 

Pgm        Parm(&Dir_In &Days)                             

Dcl        Var(&Dir_In)     Type(*Char) Len(1024)          

Dcl        Var(&Days)       Type(*UInt)                    

                                                            

Dcl        Var(&InlPath)    Type(*Char) Len(1025)          

Dcl        Var(&Dir_Ptr)    Type(*Ptr)                     

                                                           

Dcl        Var(&DirEnt_Ptr) Type(*Ptr)                     

Dcl        Var(&DirEnt)     Type(*Char) Len(696) +         

             Stg(*Based) BasPtr(&DirEnt_Ptr)               

Dcl        Var(&LenOfName)  Type(*UInt) Stg(*Defined) +    

             DefVar(&DirEnt 53)                             

Dcl        Var(&Name)       Type(*Char) Len(640) +         

             Stg(*Defined) DefVar(&DirEnt 57)              

                                                           

Dcl        Var(&FileInfo)   Type(*Char) Len(128)            

Dcl        Var(&LstAccess)  Type(*Int) +                              

             Stg(*Defined) DefVar(&FileInfo 25)       /* Last open   */

Dcl        Var(&LstDtaChg)  Type(*Int) +                              

             Stg(*Defined) DefVar(&FileInfo 29)       /* Last changed*/

Dcl        Var(&ObjTyp)     Type(*Char) Len(10) +                     

             Stg(*Defined) DefVar(&FileInfo 49)       /* Object type */

                                                                       

Dcl        Var(&DatTim_Ptr) Type(*Ptr)                                

Dcl        Var(&DatTim)     Type(*Char) Len(24) +                     

             Stg(*Based) BasPtr(&DatTim_Ptr)                          

                                                                       

Dcl        Var(&SecsInDay)  Type(*Int)  Value(86400)                  

Dcl        Var(&TimeFilter) Type(*Int)                                

                                                                      

Dcl        Var(&Path)       Type(*Char) Len(10000)                    

Dcl        Var(&MsgTxt)     Type(*Char) Len(300)                      

Dcl        Var(&Status)     Type(*Int)                                

Dcl        Var(&Null)       Type(*Char) Len(1) +               

             Value(x'00')                                      

Dcl        Var(&Null_Ptr)   Type(*Ptr)                         

                                                               

ChgVar     Var(&InlPath) Value(&Dir_In)                         

ChgVar     Var(&Path) Value(&InlPath *TCat &Null)              

CallPrc    Prc('opendir') Parm(&Path) RtnVal(&Dir_Ptr)         

If         Cond(&Dir_Ptr = &Null_Ptr) Then(Do)                 

           SndPgmMsg Msg('Directory not found') +              

             ToPgmQ(*Ext)                                      

           Return                                              

           EndDo                                               

                                                                

CallPrc    Prc('time') Parm((*Omit)) RtnVal(&TimeFilter)       

ChgVar     Var(&TimeFilter) +                                  

             Value(&TimeFilter - (&Days * &SecsInDay))         

                                                                

CallPrc    Prc('readdir') +                                         

             Parm((&Dir_Ptr *ByVal)) RtnVal(&DirEnt_Ptr)            

                                                                    

DoWhile    Cond(&DirEnt_Ptr *NE &Null_Ptr)                          

                                                                    

           If Cond(%sst(&Name 1 1) *NE '.') Then(Do)                

              ChgVar Var(&Path) Value(&InlPath *TCat '/' *TCat +    

                         %sst(&Name 1 &LenOfName) *TCat &Null)      

              CallPrc Prc('stat') +                                 

                Parm(&Path &FileInfo) RtnVal(&Status)               

                                                                     

              If Cond(&Status = 0) Then(Do)                         

                                                                    

                 If Cond(&LstAccess < &TimeFilter) Then(Do)         

                    ChgVar Var(&MsgTxt) +                           

                      Value(&ObjTyp *Cat %sst(&Name 1 &LenOfName))  

                    SndPgmMsg Msg(&MsgTxt) ToPgmQ(*Ext)             

                                                                

                    CallPrc Prc('ctime') +                     

                      Parm(&LstDtaChg) RtnVal(&DatTim_Ptr)     

                    ChgVar Var(&MsgTxt) +                      

                      Value('Last change: ' *Cat &DatTim)      

                    SndPgmMsg Msg(&MsgTxt) ToPgmQ(*Ext)        

                                                               

                    CallPrc Prc('ctime') +                     

                      Parm(&LstAccess) RtnVal(&DatTim_Ptr)     

                    ChgVar Var(&MsgTxt) +                      

                      Value('Last access: ' *Cat &DatTim)      

                    SndPgmMsg Msg(&MsgTxt) ToPgmQ(*Ext)        

                    EndDo                                       

                 EndDo                                         

                                                               

              Else Cmd(Do)                                     

                   ChgVar Var(&MsgTxt) Value('** ERROR **' +   

                     *Cat %sst(&Name 1 &LenOfName))           

                   SndPgmMsg Msg(&MsgTxt) ToPgmQ(*Ext)        

                   EndDo                                      

                                                               

              EndDo                                           

                                                              

           CallPrc Prc('readdir') +                           

             Parm((&Dir_Ptr *ByVal)) RtnVal(&DirEnt_Ptr)      

           EndDo                                              

                                                              

CallPrc    Prc('closedir') Parm((&Dir_Ptr *ByVal))            

                                                               

EndPgm                                                        

 

As you can see, there are not very many changes needed in order to start aging the files within the directory. Two of the changes—extending the length of both &Dir_In and &InlPath—are not even necessary for the purposes of this sample program. But as we will not be calling the DIR3 program directly from the command line, we no longer have to worry about the default of 32 bytes for character string lengths. These two length changes were made in an attempt to make the command more generally useful in terms of handling directory naming.

 

As with the previous DIR2 program, you can create the DIR3 program with either one or two steps, depending on the release level of the i operating system you are running on. If you are on V6R1 or later, you can use this single command:

 

CRTBNDCL PGM(DIR3)

 

If you are on V5R4, you will need to use the following two-step process (as the QC2LE binding directory is not implicitly used by CRTBNDCL if the system release level is prior to V6R1):

 

CRTCLMOD MODULE(DIR3)

CRTPGM PGM(DIR3) BNDDIR(QC2LE)

 

Before looking at the DIR3 program, let's quickly review the previous DIR2 program. DIR2 displayed messages listing all user files, within a specified directory, along with the date and time each file was last modified and last accessed. The dates and times displayed were formatted similar to Mon Apr 11 03:15:41 2011. With DIR3, we want the program to display this file information only if the amount of time since the last file open is greater than the number of days specified by the DAYS parameter.

 

One approach to filter the files to be shown would be to retrieve the current system date and time using a command such as RTVSYSVAL QDATETIME(&DATETIME), subtract three days, and then compare this result with the dates and times found for each file. This solution, however, is complicated by needing to keep in mind the number of days per month, leap days, etc. when subtracting three days. You might recall, though, that the ctime API was used to format dates in DIR2 only because the stat API, which actually retrieves the IFS file dates and times, returns time values as a number of seconds since Jan 1 1970. This format, a simple duration measured in seconds since 1970, is not too meaningful when shown to an end user but is ideal for the type of filtering we want to do in the DIR3 program. Keeping this background material in mind, let's look at the DIR3 program.

 

In addition to adding the DAYS parameter to the PGM statement and declaring the attributes of the &DAYS variable, two new variables are also declared in the program. The first, &SecsInDay, is defined as an integer with an initial value of 86400. 86400 is the number of seconds in a day (ignoring leap seconds and Daylight Saving Time considerations) and is determined by multiplying 60 seconds in a minute by 60 minutes in an hour by 24 hours in a day. The second variable, &TimeFilter, is defined as an integer and will be used to hold the result of subtracting (&Days * &SecsInDay) from the current day.

 

After verifying with the opendir API that the user-specified directory exists, DIR3 calls the Determine Current Time (time) API. The time API, documented here, returns the current time as an integer value representing the number of seconds from Jan 1 1970 to "now." This time value is stored in the variable &TimeFilter. The program now subtracts the product of &DAYS multiplied by &SecsInDay from &TimeFilter, storing the result in &TimeFilter. If &DAYS were specified as 3 when running the USEDIRPGM command, &TimeFilter would now represent the time and date three days prior to the current date and time.

 

All that's left now is to check to see if the last access time is less than the value of &TimeFilter. This is done with the addition of this line of code:

 

If Cond(&LstAccess < &TimeFilter) Then(Do)         

 

With these few changes, the program now provides a list of those files that have not been opened within the last X days or, to be a bit more accurate, those files that have not been opened within the last X 24-hour periods. What would it take, though, to support days rather than 24-hour periods? If today was a Friday, many people might interpret a three-day period as indicating files not opened since Monday (with the three-day period, meaning not opened on the previous Tuesday, Wednesday, or Thursday).

The localtime and mktime APIs

There are several ways to accomplish this, with the solution shown in this article being based on two additional APIs. The first API we'll use is named localtime and is documented here. The localtime API has one input—a 4-byte integer value representing the number of seconds since January 1 1970—and one output—a pointer to a structure where the input time value has been converted to a structure containing, among other things, the date and time in hours, minutes, seconds, and so on. The second API we'll be using is named mktime and is documented here. The mktime API has one input—a structure such as that returned by the localtime API—and one output—a 4-byte integer value where the input time value has been converted to the number of seconds since January 1 1970. We will use these APIs to…

 

  • reformat the current date and time value that is returned by the time API
  • set the returned structure to the start of the current day by changing the returned hour, minute, and second values to 0 while leaving the day portion the same
  • reformat this modified structure back to a 4-byte integer value representing the start of the current day
  • use this start of current day integer value, rather than the current date and time value, to set the &TimeFilter variable value

 

Here is the updated DIR3 program with the changes from the earlier version shown in bold.

 

Pgm        Parm(&Dir_In &Days)                             

Dcl        Var(&Dir_In)     Type(*Char) Len(1024)          

Dcl        VAR(&Days)       Type(*UInt)                    

                                                           

Dcl        Var(&InlPath)    Type(*Char) Len(1025)          

Dcl        Var(&Dir_Ptr)    Type(*Ptr)                     

                                                            

Dcl        Var(&DirEnt_Ptr) Type(*Ptr)                     

Dcl        Var(&DirEnt)     Type(*Char) Len(696) +         

             Stg(*Based) BasPtr(&DirEnt_Ptr)               

Dcl        Var(&LenOfName)  Type(*UInt) Stg(*Defined) +    

             DefVar(&DirEnt 53)                            

Dcl        Var(&Name)       Type(*Char) Len(640) +         

             Stg(*Defined) DefVar(&DirEnt 57)              

                                                            

Dcl        Var(&FileInfo)   Type(*Char) Len(128)           

Dcl        Var(&LstAccess)  Type(*Int) +                              

             Stg(*Defined) DefVar(&FileInfo 25)       /* Last open   */

Dcl        Var(&LstDtaChg)  Type(*Int) +                              

             Stg(*Defined) DefVar(&FileInfo 29)       /* Last changed*/

Dcl        Var(&ObjTyp)     Type(*Char) Len(10) +                     

             Stg(*Defined) DefVar(&FileInfo 49)       /* Object type */

                                                                       

Dcl        Var(&DatTim_Ptr) Type(*Ptr)                                

Dcl        Var(&DatTim)     Type(*Char) Len(24) +                     

             Stg(*Based) BasPtr(&DatTim_Ptr)                           

                                                                      

Dcl        Var(&SecsInDay)  Type(*Int)  Value(86400)                  

Dcl        Var(&TimeFilter) Type(*Int)                                

                                                                       

Dcl        Var(&Time_Ptr)   Type(*Ptr)                                

Dcl        Var(&Time)       Type(*Char) Len(36) +                     

             Stg(*Based)   BasPtr(&Time_Ptr)                           

Dcl        Var(&SecsAftMin) Type(*Int) +           

             Stg(*Defined) DefVar(&Time 1)         

Dcl        Var(&MinsAftHr)  Type(*Int) +           

             Stg(*Defined) DefVar(&Time 5)         

Dcl        Var(&HrsAftMid)  Type(*Int) +           

             Stg(*Defined) DefVar(&Time 9)         

Dcl        Var(&DayOfMth)   Type(*Int) +           

             Stg(*Defined) DefVar(&Time 13)        

Dcl        Var(&MthsAftJan) Type(*Int) +           

             Stg(*Defined) DefVar(&Time 17)        

Dcl        Var(&YrsAft1900) Type(*Int) +           

             Stg(*Defined) DefVar(&Time 21)        

Dcl        Var(&DaysAftSun) Type(*Int) +           

             Stg(*Defined) DefVar(&Time 25)        

Dcl        Var(&DaysAftJn1) Type(*Int) +           

             Stg(*Defined) DefVar(&Time 29)        

Dcl        Var(&DST_Flag)   Type(*Int) +           

             Stg(*Defined) DefVar(&Time 33)               

                                                           

Dcl        Var(&Path)       Type(*Char) Len(10000)        

Dcl        Var(&MsgTxt)     Type(*Char) Len(300)          

Dcl        Var(&Status)     Type(*Int)                    

Dcl        Var(&Null)       Type(*Char) Len(1) +          

             Value(x'00')                                 

Dcl        Var(&Null_Ptr)   Type(*Ptr)                    

                                                          

ChgVar     Var(&InlPath) Value(&Dir_In)                   

ChgVar     Var(&Path) Value(&InlPath *TCat &Null)         

CallPrc    Prc('opendir') Parm(&Path) RtnVal(&Dir_Ptr)    

If         Cond(&Dir_Ptr = &Null_Ptr) Then(Do)            

           SndPgmMsg Msg('Directory not found') +         

             ToPgmQ(*Ext)                                  

           Return                                         

           EndDo                                             

                                                             

CallPrc    Prc('time') Parm((*Omit)) RtnVal(&TimeFilter)     

CallPrc    Prc('localtime') Parm(&TimeFilter) +              

             RtnVal(&Time_Ptr)                               

ChgVar     Var(&SecsAftMin) Value(0)                         

ChgVar     Var(&MinsAftHr)  Value(0)                          

ChgVar     Var(&HrsAftMid)  Value(0)                         

CallPrc    Prc('mktime') Parm(&Time) RtnVal(&TimeFilter)     

ChgVar     Var(&TimeFilter) +                                

             Value(&TimeFilter - (&Days * &SecsInDay))       

                                                             

CallPrc    Prc('readdir') +                                  

             Parm((&Dir_Ptr *ByVal)) RtnVal(&DirEnt_Ptr)     

                                                              

DoWhile    Cond(&DirEnt_Ptr *NE &Null_Ptr)                   

                                                             

           If Cond(%sst(&Name 1 1) *NE '.') Then(Do)                

              ChgVar Var(&Path) Value(&InlPath *TCat '/' *TCat +    

                         %sst(&Name 1 &LenOfName) *TCat &Null)      

              CallPrc Prc('stat') +                                 

                Parm(&Path &FileInfo) RtnVal(&Status)               

                                                                     

              If Cond(&Status = 0) Then(Do)                         

                                                                    

                 If Cond(&LstAccess < &TimeFilter) Then(Do)         

                    ChgVar Var(&MsgTxt) +                           

                      Value(&ObjTyp *Cat %sst(&Name 1 &LenOfName))  

                    SndPgmMsg Msg(&MsgTxt) ToPgmQ(*Ext)             

                                                                     

                    CallPrc Prc('ctime') +                          

                      Parm(&LstDtaChg) RtnVal(&DatTim_Ptr)          

                    ChgVar Var(&MsgTxt) +                           

                      Value('Last change: ' *Cat &DatTim)           

                    SndPgmMsg Msg(&MsgTxt) ToPgmQ(*Ext)           

                                                                  

                    CallPrc Prc('ctime') +                        

                      Parm(&LstAccess) RtnVal(&DatTim_Ptr)        

                    ChgVar Var(&MsgTxt) +                         

                      Value('Last access: ' *Cat &DatTim)         

                    SndPgmMsg Msg(&MsgTxt) ToPgmQ(*Ext)            

                    EndDo                                         

                 EndDo                                            

                                                                  

              Else Cmd(Do)                                         

                   ChgVar Var(&MsgTxt) Value('** ERROR **' +      

                     *Cat %sst(&Name 1 &LenOfName))               

                   SndPgmMsg Msg(&MsgTxt) ToPgmQ(*Ext)            

                   EndDo                                           

                                                                  

              EndDo                                               

                                                             

           CallPrc Prc('readdir') +                          

             Parm((&Dir_Ptr *ByVal)) RtnVal(&DirEnt_Ptr)     

           EndDo                                             

                                                             

CallPrc    Prc('closedir') Parm((&Dir_Ptr *ByVal))           

                                                             

EndPgm                                                       

 

The changes are, as you can see, quite minimal. The APIs introduced (time, ctime, localtime, and mktime) can provide for very flexible date and time support for CL applications. As you have seen, adding and subtracting time durations such as days or hours can be done in a very straightforward manner—without the developer needing to worry about how many days are in a month, how many days are in a year, etc. In the current example program, we've used these APIs to assist us in aging IFS files. But I suspect you have other applications where flexible date manipulation, such as we've seen here, would help simplify the CL program.

 

A few notes though about the structure returned by the localtime API. The hour, minute, and second fields represent the number of hours, minutes, and seconds of the specified time. So a value of 0 for seconds means it is the start of the minute returned, a value of 0 for minutes means it is the start of the hour returned, and a value of 0 for hour means it is the start of day for the day returned. Seconds, therefore, are typically in the range of 0 to 59, minutes 0 to 59, and hours 0 to 23. As a piece of trivia, seconds can actually be in the range of 0 to 61, allowing for up to two leap seconds. The day of month is the usual 1 through 31, but month reverts to being the month of the year, so month can be in the range of 0 to 11. The year field is the calendar year less 1900, so the year 1901 is represented by the value 1.

Automate Removal or Save of IFS Files

You might be wondering how close we are to being able to automate the removal and/or saving of IFS files based on the number of days since the file was last used. Essentially, we're already there. The most recent version of the DIR3 program is only displaying the files that have not been opened in X days. To remove those files, you could add the following two statements after (or replacing) the SNDPGMMSG commands that are displaying the file attributes.

 

ChgVar Var(&Path) Value(&InlPath *TCat + 

     '/' *TCat +                         

     %sst(&Name 1 &LenOfName))           

RmvLnk ObjLnk(&Path)                      

 

The CHGVAR command is basically removing the trailing null byte (&Null) from the &Path variable that was previously formatted for use by the stat API. The stat API uses a trailing null byte to determine the length of the path parameter. The Remove Link (RMVLNK) command, like most CL commands, expects the path parameter to be ended by a blank, not a null byte. After reformatting the &Path variable without the null byte, the RMVLNK command then removes the specified file.

 

If you are going to implement additional processing such as the RMVLNK command show above, you may want to take a few items into consideration. These additional items might include the following:

 

  • Changing the USEDIRPGM command to require the DIR parameter as opposed to allowing the parameter to default to *CURDIR. You may not want a user entering the command name USEDIRPGM and pressing Enter, rather than F4 to prompt, to run the RMVLNK commands when the user's current directory is not set correctly.
  • Checking the &ObjTyp value for a value such as '*STMF' prior to running the RMVLNK command (as RMVLNK does not support object types such as *DIR).
  • Using a MONMSG following the RMVLNK in the event you don't have the proper authority to remove the file.

More CL Questions?

Wondering how to accomplish a function in CL? Send your CL-related questions to me at This email address is being protected from spambots. You need JavaScript enabled to view it.. I'll try to answer 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: