I have a job that runs under a sbs that i would like to end within a cl pgm. My issue is when i do endjob job(blahblah), it will pull up duplicate jobs from the outq, then i have to select the active job to end. Kinda defeats the purpose for the pgm. I would like to get away from entering wrkactjob job(blahblah) and placing an option 4 to end the active job if i could. I will note that the job number/user is different for each time the job is active. In short, can this be done?
Thanks.
Software/Hardware used:
as400 iseries
ASKED:
October 20, 2010 10:55 AM
UPDATED:
November 12, 2010 12:26 AM
You could modify jobs to log some kind of status info as noted in the answer box; but you’d be doing that for every future job that you want to access by program. I’d say it’s much better to start with learning how to get what you need without modifying any current programming.
There are two general ways to get it done — use some work management APIs or mess around with things like creating spooled files from WRKACTJOB or WRKSBSJOB and reading through the spooled files.
The APIs tend to be a lot cleaner in the long run, more accurate and more immune to upgrades. But they seem to scare a lot of people. The spooled files method seems to be easier because people tend to be already familiar with how they look, while the APIs are unfamiliar. (Once I did my first successful API project, I never considered going back to the spooled files.)
Your simplest API route would be through the List Job (QUSLJOB) API. A different API could be more direct, but QUSLJOB is a better starting point. To make QUSLJOB work, two additional APIs — Create User Space (QUSCRTUS) API and Retrieve User Space (QUSRTVUS) API — will also be needed.
The structure of your new procedure would go something like this:
There are a couple variations depending on what version of i5/OS you’re using. If you’re at V5R4 or higher, you could use the Retrieve Pointer to User Space (QUSPTRUS) API instead of QUSRTVUS; that lets you simply step through the *USRSPC by adding an offset to a pointer rather than retrieving each list entry into a CL variable. You could also use other work management APIs to access all details of the fully-qualified job name that you retrieved from the *USRSPC before ending the job; that could eliminate a potential problem from someone unexpectedly giving an unrelated job the same name as the one you’re interested in.
If you want to go with the APIs, there is plenty of help available. CL has done this kind of stuff for a couple decades, so it’s not quite out of the question.
Tom
Lotofbad -
We have dozens of jobs like this that run in a special “work” SBS. We control these jobs by a data area of the same name as the CL. We check this data area for an End Job Flag. This also works for programs (RPG, Cobal, etc). This data area is not a single byte DtaAra. We store the current job info, last completed job, run time, etc …
We have a generic program that manipulates several data areas. This allows us to smoothly control the end of a CL or program.
MIS
Here’s a sample CL program that will end a named job if that job is currently active. It uses a CL variable with a constant value for the job-name, but it could be a parm variable or it could be retrieved from a data area or…? It is written so that it will only end the job if it is active in a specific subsystem — QPGMR in this case. That test could be skipped or the variable holding the target subsystem name could be changed to have a different value or…? It also loops through to catch every active job with that name. It could exit when it finds the first one or it could display a message asking if the current one is the one to end or…?
The example job that it ends is named DUMMYJOB and the subsystem that it targets is QPGMR. You can test it by submitting multiple jobs with that name, some to QPGMR and some to other subsystems. You can submit other jobs to QPGMR at the same time with a different job-name. (I have QPGMR set to run four concurrent jobs.) You can use something like CMD(DLYJOB 600) as the command for the submitted test job.
pgm /* Identify a job-name and a subsystem to work over... */ dcl &SCHNAME *char 10 value( 'DUMMYJOB' ) dcl &SCHSBS *char 10 value( 'QPGMR' ) dcl &SchJob *char 26 /* API User Space general error code variable... */ dcl &a_err *char 128 value( x'0000008000000000' ) /* Identify a *USRSPC name... */ dcl &usrspc *char 10 value( 'LSTACTJOBS' ) dcl &usrspclib *char 10 value( 'QTEMP' ) dcl &qusrspc *char 20 /* General fields from *usrspc header... */ dcl &offslst *int dcl &nbrlste *int dcl &sizlste *int /* Various work fields... */ dcl &Sbs *char 10 dcl &jobname *char 10 dcl &jobusr *char 10 dcl &jobnbr *char 6 dcl &qjob *char 26 /* Various receiver variables... */ dcl &us_hdr *char 150 dcl &JOBL0100F *char 56 dcl &JOBI0200F *char 72 /*---------------------------------------------------------------------------*/ /* Global MONMSG... */ /*---------------------------------------------------------------------------*/ monmsg ( cpf0000 mch0000 ) exec( goto Std_Err ) /* */ /* Create *usrspc for the SBS info APIs... */ /* */ chgvar &qusrspc ( &usrspc *cat &usrspclib ) call QUSCRTUS ( + &qusrspc + 'ACTJOB ' + x'00004000' + X'00' + '*ALL ' + 'List active jobs ' + '*YES ' + x'0000000000000000' + ) /* Set the qualified search-job name... */ chgvar &SchJob ( &SCHNAME *cat '*ALL *ALL ' ) /* */ /* List the active jobs into our *usrspc... */ /* */ call QUSLJOB ( + &qusrspc + 'JOBL0100' + &SchJob + '*ACTIVE ' + x'0000000000000000' + '*' + x'00000000' + x'00000000' + ) /* Retrieve the *usrspc header... */ call QUSRTVUS ( + &qusrspc + x'00000001' + x'00000096' + &us_hdr + ) /* */ /* Get the offset to the list within the space, the number */ /* of list entries and size of each entry from the header. */ /* */ chgvar &offslst %bin( &us_hdr 125 4 ) chgvar &nbrlste %bin( &us_hdr 133 4 ) chgvar &sizlste %bin( &us_hdr 137 4 ) /* If no entries, then get out of here... */ if ( &nbrlste *eq 0 ) do sndpgmmsg msgid( CPF9897 ) msgf( QCPFMSG ) + msgdta( 'No active jobs found.' ) goto End_ActJob enddo /* Set the offset to the list within the space... */ chgvar &offslst ( &offslst + 1 ) dowhile ( &nbrlste *gt 0 ) /* Retrieve one listed job entry from the list... */ call QUSRTVUS ( + &qusrspc + &offslst + &sizlste + &JOBL0100F + ) /* Get the qualified job name from the list entry... */ chgvar &qjob %sst( &JOBL0100F 1 26 ) /* Get details of that job in order to find what subsystem it's in... */ call QUSRJOBI ( + &JOBI0200F + x'00000048' + 'JOBI0200' + &qjob + ' ' + ) /* Get the subsystem name from the receiver variable... */ chgvar &Sbs %sst( &JOBI0200F 63 10 ) if ( &Sbs *eq &SCHSBS ) do chgvar &jobname %sst( &JOBI0200F 9 10 ) chgvar &jobusr %sst( &JOBI0200F 19 10 ) chgvar &jobnbr %sst( &JOBI0200F 29 6 ) endjob job( &jobnbr/&jobusr/&jobname ) enddo else + sndpgmmsg msgid( CPF9897 ) msgf( QCPFMSG ) + msgdta( 'Found' *bcat &Sbs ) + topgmq( *EXT ) msgtype( *STATUS ) /* Bump our offset up by the size of a list entry... */ chgvar &offslst ( &offslst + &sizlste ) /* Decrement our loop counter and loop back if more... */ chgvar &nbrlste ( &nbrlste - 1 ) enddo End_Run: dltusrspc &usrspclib/&usrspc /* Cleanup */ return Std_Err: /* Move any *DIAG messages up the stack... */ Qsys/call QSYS/QMHMOVPM ( + ' ' + '*DIAG ' + x'00000001' + '* ' + x'00000001' + x'00000000' + ) Qsys/monmsg ( CPF0000 MCH0000 ) /* Resend any *ESCAPE messages up the stack... */ Qsys/call QSYS/QMHRSNEM ( + ' ' + x'00000000' + ) Qsys/monmsg ( CPF0000 MCH0000 ) return endpgmOnce you have the basics of creating a space, putting a list into it and stepping through the list, the code can be used over and over with different list APIs and with different formats.
The code is written for V5R3. It can be modified for earlier releases by replacing any *INT variables with 4-byte *CHAR variables and changing out the DOWHILE for an IF/GOTO loop.
It can be modernized to V5R4 and later by using various data structures instead of substringing and by using pointer logic to eliminate using the QUSRTVUS API.
You should be able to compare anything in the code with the related API documentation in order to understand any piece of it. Running an actual example under debug is as good as you’ll get for learning.
Tom
if ( &nbrlste *eq 0 ) do sndpgmmsg msgid( CPF9897 ) msgf( QCPFMSG ) + msgdta( ‘No active jobs found.’ ) goto End_ActJob enddoMinor correction — that GOTO should be:
goto End_RunI did a little tidying up and failed to realize that I changed executeable code in addition to the comments that I changed.
Be aware that this site’s handling of copy/pasted program code will mess up characters such as apostrophes. Nothing I can do about that. You’ll just have to change each leading and trailing quote to an apostrophe. There are one or two additional Unicode things that it messes up, and you’ll want to fix those. (For example, if you type three periods as an ellipsis, it will replace them with a single ellipsis Unicode character. Messing with “code” shouldn’t happen with an editor, especially within a {code} block. But, that’s how it goes.)
Tom
Thank’s a lot, I adapted it for V4R5 & V5R1, working very well, doing the job quickly and easy to understand. Very good work !