A couple of years ago I had a client with a problem with hanging jobs. Interactive jobs would freeze. They had their system values set so that only one interactive session was allowed, meaning each hanging job resulted in a help desk call. The solution was to place a call (in the initial program) to a program that killed all existing interactive sessions. This required management buy-in for a policy allowing only one session (this had been the de facto policy anyway)
The program uses the following APIs:
QUSCRTUS - Create a User Space
QUSPRTUS - Retrieve User Space pointer
QUSRJOBI - Retrieve Job Attributes
QUSLJOB - List Jobs
The program is pretty simple - create a user space, retrieve the address of that user space, retrieve the device name, and list all the user's active jobs. The QUSLJOB API return value is a dynamic data structure contained in a user space (why not the heap, I don't know). Information is retrieved from the header portion, and pointer math (comibined with pointer-based data structures) is used to loop through the data portion. All interactive jobs with a device name different from the current job are cancelled. Placing a call to this program in every user's initial program reduced calls to the help desk by about 30%.
It would have made more sense to figure out why the jobs were hanging, but management of the AS/400 had recently been outsourced, and the politics were such that isn't wasn't possible to admit the service bureau wasn't perfect - it was easier to pay a contractor to bandaid the problem.
This is not a perfect solution - boundary conditions are not treated as rigorously as they should be; with about 100 bytes/active job, the 16000 bytes allocated for the user space will prove to be insufficient for any use with more than 160 active jobs. That said, it should be adequate for most people's purposes.