29
Fri, Nov
0 New Articles

The Trigger Is the Safety: Part 2

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

In “The Trigger Is the Safety” (MC, November 1997), I introduced basic trigger concepts and their application. I also gave you four rules for trigger program coding:

1. Keep it simple, stupid (KISS). Don’t bog down your database with a lot of exceptional-case code.

2. Use activation group *CALLER in an ILE environment.
3. Put the trigger program in the same library as the file it is associ-ated with.
4. Consider language-specific features when choosing your trigger implementation. In this article, I will discuss advanced trigger capabilities and some specific considerations for your trigger implementation. I will show you how a trigger program can change data before it is applied to your database and disallow an update.

What happens when you create a duplicate of a file that has a trigger attached? The answer depends on how you created the duplicate. If you use Create Duplicate Object (CRTDUPOBJ) to create the file, the new file will still have the trigger program attached (the same trigger program, in the same library). However, if you use Copy File (CPYF) to create the file, the trigger attachment will not copy to the new file. If you add a trigger program to a file already in production, you will need to find out whether this will affect you. For instance, if you create a duplicate of a file into QTEMP using CRTDUPOBJ, your new file will also call the trigger program just as the original file did. This may or may not be what you want.

A better approach to creating “duplicate” files is to use DDS. You can create a new file to look just like an existing file with a short source member (see Figure 1). You specify the record format and the original file, and you give the key fields, if needed. There are several advantages to this technique. First, you don’t have an object on your system that does not have a source member associated with it. Many source control packages are

Dup It

happier when every object has a source member. (The same is true of many support teams!) Second, system analysis tools such as the Advanced Systems Concepts (ASC) APLUS or the Hawkeye Pathfinder can track the relationship of the original file to your “dup.” Third, even if you don’t use an automated tool to keep track of which duplicate files need to be re-created when the format changes, it is easier to find those objects when there is a source member relating the two files. Fourth, in order for the trigger program to be attached to this file, you would have to execute the Add Physical File Trigger (ADDPFTRG) command against that file. No implicit trigger attachments there!

Fill in the Blanks

In the first article, I presented an example trigger program that wrote audit records representing the updates performed on a file. I’m going to change the trigger program from the first article in two ways. The first change is to add update program, date, and time fields to both the master and audit files that the trigger program will populate. The second is to add a security feature to the file—we’ll allow only certain user profiles to update certain fields!

The first enhancement demonstrates a really powerful way to erect a firewall around your data. You can design your trigger program to correct errors before they get in your database. To enable this feature, you must say *YES on the keyword Allow Repeated Change—ALWREPCHG(*YES)—on the ADDPFTRG command. If this parameter is left at the default of *NO, then any change you make to the trigger buffer will be ignored by database management. Of course, this means something only for insert and update operations. All you have to do after that is code your trigger program to alter the data in the “after” image.

In this trigger program, (see Figure 3) I am updating three fields, regardless of their current value. These fields are intended to indicate the last date and time of an update and the name of the program that performed the update. These same fields will go into the audit record(s).

I retrieve the date and time via the Time op code. The program name, however, requires a bit of work. The best way I’ve found to determine the name of the program performing the update is to send a message to that program and then receive it. The name of the program is contained in the message “sender” information. For this purpose, I use the message-handling APIs, specifically Send Program Message (QMHSNDPM) and Receive Program Message (QMHRCVPM). I set the parameters to go one level back from the caller as the target of the send and receive. The caller is QDBPUT for a write, QDBUDR for anything else. I send the message as an *INFO message, and I receive it by message key. The name of the receiving program is returned by QMHRCVPM. Keep in mind that this means that the message send/receive will be called for each update to the file. These calls are not free! If you don’t need this information in your trigger program, don’t include this code. I recommend that you perform this function only for master files that aren’t changed all that much, rather than for high usage transaction files.

It is important to note the differences in how the database modules are called. High- level languages (HLLs) call the database modules directly. However, if you use DFU, the name of the calling program will not be the name of your DFU program. It will be the name of the module that DFU uses to perform its database functions (usually QDZXDBI). Also, if you use SQL, the name of the calling program will not be the one that performs the SQL function. SQL uses its own modules to interface with database management (QSQINS for inserts, QSQDELET for deletes, QSQUPDAT for updates).

You may wonder why I retrieve the program name every time the trigger is fired. The reason is that I haven’t been able to determine exactly what will cause the trigger

program to be shut down. Since I’m not turning on LR (so I don’t have to reinitialize my program, including opening and closing the files defined to the trigger program, for every record), I leave the program active. Closing the file that has the trigger program attached does not shut down the trigger program. Short of using Reclaim Resources (RCLRSC) for the default activation group or Reclaim Activation Group (RCLACTGRP) for a named activation group, I am not able to know that the trigger program is shut down. Therefore, I don’t know that the program calling it this time is the same as the program that called it last time. Of course, if your application can be sure of that, you’ll need to perform the message send/receive only once.

The situation is different for the name of the user profile. Unless you are using the API to change a job’s user profile (QWTSETP), the user profile needs to be checked only once. It will not change for the duration of the job.

Just Say No!

I use the other enhancement to our trigger program to demonstrate how a trigger can deny a database action based on whatever criteria I define. In my example, I’ve created a file (XMPUSRP) that contains a record for each user profile I want to allow to change the key field. If your profile isn’t in that file, you aren’t allowed to update that record, so you can’t change the vendor number. Of course, if your profile is in my file, go right ahead. Update all you want.

The technique is straightforward. Once your trigger program detects a violation, all you have to do is send an escape message to the trigger program’s caller.

Here’s how it works. In the Initialization subroutine (*INZSR), I perform a secure override to my database file. This will ensure that the trigger program opens the file I intend for it to open. (If an enterprising programmer redirected the trigger program to look at a different XMPUSRP file, he could update records he shouldn’t have authority to update.) I then retrieve the user profile from the system data structure job information. Using that value, I perform a Set Lower Limit (SETLL) against my file, XMPUSRP. If I find an entry for that key value, the Allow flag is set to Y. If not, however, the flag is set to
N. The CheckUpdate subroutine references that flag. The update is allowed if the field in question (Vendor Number) changes and the flag is Y, or if the field does not change. If the field has changed but the Allow flag is not Y, the trigger sends an escape message stating that the update was disallowed. Database management interprets this message as an error condition, and the record is not updated.

This error condition is signaled by an exception to the calling program. DFU and SQL handle this exception, sending a message to the job log. RPG receives message RPG1299. Of course, you can put an indicator on the I/O op code and check that, too. COBOL reports it as a file status 90.

It is important to note that any specific messages sent by your trigger program will be found in the job log. Both DFU and SQL report a generic trigger message, CPF502B (“Error occurred in trigger program”).

Show Me the Way

To implement this trigger program, perform the following steps. Create the physical files XMPMSTP (Figure 3), XMPAUDP (Figure 2), and XMPUSRP (Figure 5). Next, compile XMPTRGR2 (Figure 4). Now, make the program the trigger for the master file. Use these commands:

ADDPFTRG FILE(XMPMSTP) +

TRGTIME(*BEFORE) +

TRGEVENT(*INSERT) +

PGM(XMPTRGR2) +

ALWREPCHG(*YES)

ADDPFTRG FILE(XMPMSTP) +

TRGTIME(*BEFORE) +

TRGEVENT(*UPDATE) +

PGM(XMPTRGR2) +

ALWREPCHG(*YES)

ADDPFTRG FILE(XMPMSTP) +

TRGTIME(*BEFORE) +

TRGEVENT(*DELETE) +

PGM(XMPTRGR2)

Note that I did not say ALWREPCHG(*YES) on the delete. There is no need. I won’t be changing the record image before I delete it!

Go Forth and Firewall!

In these two articles, I have introduced you to trigger programs. You have seen how to create and implement a trigger program. You have seen examples of how triggers can enhance the integrity of your files. I hope you can see that triggers are powerful tools to help you prevent problems. I’m sure you’ll find even more creative ways to use triggers to put a firewall around your database.

A R XMPMSTF FORMAT(XMPMSTP)

A K VNDNBR ** File Name : XMPAUDP

* Created : 9/08/1997 Michael Polutta

* This is an audit file for the trigger example.

*

* Create this file with the following command:

*

* CRTPF FILE( yourlib/XMPAUDP) SRCFILE(srclib/QDDSSRC)

*

*A R XMPAUDF

A AUDDAT 8S 0 TEXT(‘AUDIT DATE’)

A COLHDG(‘DATE’)

A AUDTIM 6S 0 TEXT(‘AUDIT TIME’)

A COLHDG(‘TIME’)

A AUDPGM 10A TEXT(‘PROGRAM NAME’)

A COLHDG(‘PROGRAM’)

A AUDSEQ 1A TEXT(‘B=BEFORE, A=AFTER’)

A COLHDG(‘SEQ’)

A AUDNBR 6S 0 TEXT(‘VENDOR NUMBER’)

A COLHDG(‘VENDOR’ ‘NUMBER’)

A AUDNAM 25A TEXT(‘VENDOR NAME’)

A COLHDG(‘VENDOR NAME’)

A AUDAD1 25A TEXT(‘VENDOR ADDRESS - LINE 1’)

A COLHDG(‘ADDRESS’ ‘LINE 1’)

A AUDAD2 25A TEXT(‘VENDOR ADDRESS - LINE 2’)

A COLHDG(‘ADDRESS’ ‘LINE 2’)

A AUDCTY 25A TEXT(‘VENDOR CITY’)

A COLHDG(‘CITY’)

A AUDST 2A TEXT(‘VENDOR STATE’)

A COLHDG(‘STATE’)

A AUDZIP 9S 0 TEXT(‘VENDOR ZIP CODE’)

A COLHDG(‘ZIP CODE’)

A AUDCTN 25A TEXT(‘VENDOR CONTACT NAME’)

A COLHDG(‘VENDOR CONTACT NAME’)

A AUDPHN 10S 0 TEXT(‘VENDOR PHONE NBR.’)

A COLHDG(‘PHONE NUMBER’) ** File Name : XMPMSTP

* Created : 9/08/1997 Michael Polutta

Figure 1: XMPDUPP, the duplicate example

Figure 2: XMPAUDP, the audit file

* This is a master file for the trigger example.

*

* Create this file with the following command:

*

* CRTPF FILE( yourlib/XMPMSTP) SRCFILE(srclib/srcfile)

*

*A UNIQUE

A R XMPMSTF

A VNDNBR 6S 0 TEXT(‘VENDOR NUMBER’)

A COLHDG(‘VENDOR’ ‘NUMBER’)

A VNDNAM 25A TEXT(‘VENDOR NAME’)

A COLHDG(‘VENDOR NAME’)

A VNDAD1 25A TEXT(‘VENDOR ADDRESS - LINE 1’)

A COLHDG(‘ADDRESS’ ‘LINE 1’)

A VNDAD2 25A TEXT(‘VENDOR ADDRESS - LINE 2’)

A COLHDG(‘ADDRESS’ ‘LINE 2’)

A VNDCTY 25A TEXT(‘VENDOR CITY’)

A COLHDG(‘CITY’)

A VNDST 2A TEXT(‘VENDOR STATE’)

A COLHDG(‘STATE’)

A VNDZIP 9S 0 TEXT(‘VENDOR ZIP CODE’)

A COLHDG(‘ZIP CODE’)

A VNDCTN 25A TEXT(‘VENDOR CONTACT NAME’)

A COLHDG(‘VENDOR CONTACT NAME’)

A VNDPHN 10S 0 TEXT(‘VENDOR PHONE NBR.’)

A COLHDG(‘PHONE NUMBER’)

A VNDPGM 10A TEXT(‘PROGRAM NAME’)

A COLHDG(‘PROGRAM’)

A VNDDAT 8S 0 TEXT(‘LAST UPDATE DATE’)

A COLHDG(‘LAST UPDATE’ ‘DATE’)

A VNDTIM 8S 0 TEXT(‘LAST UPDATE TIME’)

A COLHDG(‘LAST UPDATE’ ‘TIME’)

A K VNDNBR ** Program Name : XMPTRGR2

* Created : 1/07/1998 Michael Polutta

* This is an example trigger that disallows some updates.

* The input parameters to this trigger program are:

* - Buffer : contains trigger information and before/after

* images of XMPMSTP.

* - BufLen : length of Buffer.

*

* Create this program with the following command:

*

* CRTBNDRPG PGM(yourlib/XMPTRGR2) SRCFILE(srclib/srcfile) +

* DFTACTGRP(*NO) ACTGRP(*CALLER)

*

*H DEBUG

*FXMPAUDP O E DISK

FXMPUSRP IF E K DISK usropn

*Dpsds SDS

D JobUser 254 263

*D Buffer DS 32767

* Physical file name

D FileName 1 10

* Physical file library

D LibraryName 11 20

* Member name

D MemberName 21 30

* Trigger event

D TrgEvent 31 31

* Trigger time

D TrgTime 32 32

* Commit lock level

D CommitLckLvl 33 33

* Reserved

D Filler1 34 36

* CCSID

D CCSID 37 40B 0

* Reserved

D Filler2 41 48

Figure 3: XMPMSTP, the master file

* Offset to the original record

D OldOff 49 52B 0

* length of the original record

D OldLen 53 56B 0

* Offset to the original record null byte map

D OldNullOffset 57 60B 0

* length of the null byte map

D OldNullLength 61 64B 0

* Offset to the new record

D NewOff 65 68B 0

* length of the new record

D NewLen 69 72B 0

* Offset to the new record null byte map

D NewNullOffset 73 76B 0

* length of the null byte map

D NewNullLength 77 80B 0

* Reserved

D Resv3 81 96

** Total Buffer length

D BufLen DS

D Leng 1 4B 0

** parms for QMHSNDPM

D SndMsgParms DS

D MsgF s 20 inz(‘ ‘)

D MsgDta s 64 inz

D MsgLen s 8B 0 inz

D MsgTyp s 10 inz

D MsgQ s 10 inz(‘ ‘)

D MsgStk s 8B 0 inz(1)

D MsgKey s 4

* parms for QMHRCVPM

* Variable length information

D RcvInfo DS 120

D RMBytesRtn 1 4B 0 inz(0)

* Bytes Returned

D RMBytesAvl 5 8B 0 inz(0)

* Bytes Available

D RMMsgSev 9 12B 0 inz(0)

* Message Severity

D RMMsgid 13 19

* Message Id

D RMMsgType 20 21

* Message Type

D RMMsgKey 22 25

* Message Key

D RMMsgFile 26 35

* Message File Name

D RMMsgfLib 36 45

* Message File Library

D RMMsgLib 46 55

* Message Library Used

D RMProgram 82 93

* Send Program Name

D RMRcvPgm 111 120

* Receive Program Name

* Fixed Rcv variables

D RcvLength s 8B 0 inz(120)

D RcvFmt s 8 inz(‘RCVM0200’)

D RcvMsgq s 10 inz(‘* ‘)

D RcvStackEnt s 8B 0 inz(0)

D RcvMsgType s 10 inz(‘*INFO ‘)

D RcvMsgKey s 4 inz(‘ ‘)

D RcvWait s 8B 0 inz(0)

D RcvAction s 10 inz(‘*REMOVE ‘)

* API Error parameter

D APIError DS

D ErrBot 1 4B 0 inz(32)

D ErrBin 5 8B 0

D ErrId 9 15

D Err1 16 16

D ErrDta 17 32

** Date and Time

D TimeDate DS

D TimeDate14 1 14 0

D CurrTime 1 6 0

D CurrDate 7 14 0

D CurrMonth 7 8 0

D CurrDay 9 10 0

D CurrYear 11 14 0

D CYMD DS

D CYMDDate 1 8 0

D CYMDYear 1 4 0

D CYMDMonth 5 6 0

D CYMDDay 7 8 0

** Map the fields from the DB file to take the before/after images apart.

D RcdFmt E DS EXTNAME(XMPMSTP)

** Work fields rt.

D Start s 5P 0

D Allow s 1

D SavePgm s like(AUDPGM)

D OldVendor s like(AUDNBR)

D NewVendor s like(AUDNBR)

*D OvrCmd s 56 inz(‘OVRDBF FILE(XMPUSRP) +

D TOFILE(*LIBL/XMPUSRP) +

D SECURE(*YES)’)

D CmdLength s 15P 5 inz(56)

*C *entry plist

C Buffer parm Buffer

C BufLen parm BufLen

** Get the name of the program firing the trigger

C exsr WhoDidIt

* Determine trigger event and take appropriate action

C select

*

C when TrgEvent = ‘1’

C exsr CheckInsert

*

C when TrgEvent = ‘2’

C exsr CheckDelete

*

C when TrgEvent = ‘3’

C exsr CheckUpdate

*

C endsl

*

C return

*C CheckInsert begsr

*

C exsr SetAudFields

C exsr GetNewFields

C exsr UpdateBuffer

*

C endsr

*C CheckDelete begsr

*

C exsr SetAudFields

C exsr GetOldFields

*

C endsr

*C CheckUpdate begsr

*

C exsr SetAudFields

C exsr GetOldFields

C eval OldVendor = AUDNBR

C exsr GetNewFields

C eval NewVendor = AUDNBR

* Check “allow” flag, process accordingly.

C if OldVendor NewVendor

C and Allow = ‘Y’

C or OldVendor = NewVendor

C exsr UpdateBuffer

C else

* Send message to disallow update operation.

C eval MsgQ = ‘* ‘

C eval MsgF = ‘QCPFMSG *LIBL ‘

C eval MsgId = ‘CPF9898’

C eval MsgTyp = ‘*ESCAPE ‘

C eval MsgDta = ‘Update disallowed’

C eval MsgLen = 17

C eval MsgStk = 2

C exsr SendMessage

C endif

*

C endsr

*C SetAudFields begsr

* Date and Time

C time TimeDate14

C eval CYMDYear = CurrYear

C eval CYMDMonth = CurrMonth

C eval CYMDDay = CurrDay

C eval AUDDAT = CYMDDate

C eval AUDTIM = CurrTime

*

C endsr

*C GetOldFields begsr

* “Before” image

C move ‘B’ AUDSEQ

*

* move original record to field layout

C eval Start = OldOff + 1

C eval RcdFmt = %subst(Buffer:Start:OldLen)

*

* Get fields from “old” record that we need

C exsr GetFieldData

C eval AUDPGM = SavePgm

*

C write XMPAUDF

*

C endsr

*C GetNewFields begsr

* “After” image

C move ‘A’ AUDSEQ

*

* move new record to field layout

C eval Start = NewOff + 1

C eval RcdFmt = %subst(Buffer:Start:NewLen)

*

* Get fields from “new” record that we need

C exsr GetFieldData

C eval AUDPGM = SavePgm

*

C write XMPAUDF

*

C endsr

*C GetFieldData begsr

* Extract fields from data structure

C eval AUDNBR = VNDNBR

C eval AUDNAM = VNDNAM

C eval AUDAD1 = VNDAD1

C eval AUDAD2 = VNDAD2

C eval AUDCTY = VNDCTY

C eval AUDST = VNDST

C eval AUDZIP = VNDZIP

C eval AUDCTN = VNDCTN

C eval AUDPHN = VNDPHN

C eval AUDPGM = VNDPGM

*

C endsr

*C UpdateBuffer begsr

* Update program name field in database “after” image

C eval VNDPGM = SavePgm

* Update date/time fields in database “after” image

C eval VNDDAT = CYMDDate

C eval VNDTIM = CurrTime

C eval %subst(Buffer:Start:NewLen) = RcdFmt

*

C endsr

*C WhoDidIt begsr

* Set program name

C if TrgEvent =’1’

C eval MsgQ = ‘QDBPUT ‘

C else

C eval MsgQ = ‘QDBUDR ‘

C endif

* Send the *INFO message

C eval MsgF = *blanks

C eval MsgId = *blanks

C eval MsgTyp = ‘*INFO ‘

C eval MsgDta = ‘Hi there!’

C eval MsgLen = 9

C exsr SendMessage

* Receive the message

C move MSGKEY RcvMsgKey

C call ‘QMHRCVPM’

C parm RcvInfo

C parm RcvLength

C parm RcvFmt

C parm RcvMsgq

C parm RcvStackEnt

C parm RcvMsgType

C parm RcvMsgKey

C parm RcvWait

C parm RcvAction

C parm APIError

*

C eval SavePgm = RMRcvPgm

*

C endsr

*C SendMessage begsr

*

C call ‘QMHSNDPM’

C parm MsgID 7

C parm MsgF

C parm MsgDta

C parm MsgLen

C parm MsgTyp

C parm MsgQ

C parm MsgStk

C parm MsgKey

C parm APIError

*

C endsr

*C *inzsr begsr

* Override XMPUSRP with SECURE(*YES) to avoid a security gap

C call ‘QCMDEXC’

C parm OvrCmd

C parm CmdLength

C open XMPUSRP

* Set the ALLOW flag

C eval Allow = ‘N’

C JobUser setll XMPUSRF 65

C if *in65 = *on

C eval Allow = ‘Y’

C endif

*

C close XMPUSRP

*

C endsr

*** File Name : XMPUSRP

* Created : 12/22/1997 Michael Polutta

* This is a reference file for the trigger example.

*

* Create this file with the following command:

*

* CRTPF FILE( yourlib/XMPUSRP) SRCFILE(srclib/QDDSSRC)

*

*A UNIQUE

A R XMPUSRF

A USRUSR 10A TEXT(‘USER PROFILE’)

A COLHDG(‘USER PROFILE’)

A K USRUSR

Figure 4: XMPTRGR2, the trigger program

Figure 5: XMPUSRP, the new authorized user file

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: