Sometimes we know the name and user of a job we might want to end, but we won’t know the job number until the job is submitted. There does not seem to be an IBM i command to end jobs when the job numbers are not known, and I have a use for one, so I built one.
To create the command, there were two options. Use an API to list the jobs and end them, or use existing commands to find the jobs, convert a spool file to a flat file, and parse the flat file to examine the list and end the desired jobs. I tend to prefer commands to API’s. so I went with the second option.
While I had a particular use in mind for this thing, I did decide to go ahead and make it a little more flexible, so it might be of use to more people. I have to warn you that I did not give the thing a full QA, so it could have a bug or two in it. I hope it is all OK, but if you see a bug, please add a comment to the post. Also, if you use it, test it first, to make sure it does exactly what you expect it to do.
For your convenience, text files for the source have been posted here:
There is a command definition, a CL program, and an RPG ILE program. The RPG program uses the RPG cycle, a program defined input file, a procedure prototype, and a line of free formatted code, so you may find it a bit stylistically eclectic. On the other hand, it is so simple a child with a basic understanding of RPG could understand it, which is something we probably could not say if it was written with APIs, so there’s that. Anyway, let’s take a look.
Here is the command, prompted:
The parameters are mostly familiar from the WRKUSRJOB and ENDJOB commands. The user is a user name, or * for current user. Job Type is batch, interactive, or both. The job name can be specified or left *ALL for all jobs that meet the other selection criteria. I did add one new option to the ENDJOB option. In addition to *CNTRLD and *IMMED, there is *ABN which will run ENDJOBABN instead of ENDJOB.
Hopefully the comments in the code explain well enough what each object does.
Here is the source for the ENDUSRJOB command:
/* The ENDUSRJOB command ends jobs with the selected option. The */ /* jobs to end are selected based on User, Job Type, and Job Name. */ /* The WRKUSR job command is used to select jobs by User and Job Type. */ /* The jobs selected by WRKUSRJOB are then matched to the given Job */ /* Name to make the final selection. Selected jobs are ended using */ /* the ENDJOB command with the specified Option. */ /* The ENDUSRJOB command is processed by CL program ENDUSRJOBC: */ /* CRTCMD CMD(ENDUSRJOB) PGM(ENDUSRJOBC) */ /* This program is a demonstration. Please test this program */ /* thoroughly before using it in any production setting. It is */ /* provided as is. Rod Flohr, 2014-09-11 */ CMD PROMPT('End User Jobs') PARM KWD(USER) TYPE(*NAME) LEN(10) DFT(*) + SPCVAL((*)) PROMPT('User whose jobs to end') PARM KWD(JOBTYPE) TYPE(*CHAR) LEN(12) RSTD(*YES) + DFT(*BATCH) VALUES(*BATCH *INTERACTIVE + *ALL) PROMPT('Job type') PARM KWD(JOBNAME) TYPE(*NAME) LEN(10) DFT(*ALL) + SPCVAL((*ALL)) PROMPT('Job name') PARM KWD(OPTION) TYPE(*CHAR) LEN(7) RSTD(*YES) + DFT(*CNTRLD) VALUES(*CNTRLD *IMMED *ABN) + PROMPT('How to end')
Here is the source for the ENDUSRJOBC program:
/* The ENDUSRJOBC program is the command processor for the ENDUSRJOB */ /* command. The ENDUSRJOB command ends jobs based on selection parms. */ /* Jobs to end are selected based on User, Job Type, and Job Name. */ /* The ENDUSRJOBC program uses the WRKUSRJOB command to pre-select */ /* jobs by User and Job Type. The spool file ouput is converted to a */ /* flat physical file for final processing by the ENDUSRJOBR program. */ /* This program is a demonstration. Please test this program */ /* thoroughly before using it in any production setting. It is */ /* provided as is. Rod Flohr, 2014-09-11 */ PGM PARM(&USER &JOBTYPE &JOBNAME &OPTION) DCL VAR(&USER) TYPE(*CHAR) LEN(10) DCL VAR(&JOBTYPE) TYPE(*CHAR) LEN(12) DCL VAR(&JOBNAME) TYPE(*CHAR) LEN(10) DCL VAR(&OPTION) TYPE(*CHAR) LEN(7) /* Find current user if passed in user is '*'. */ IF COND(&USER = '*') THEN(RTVJOBA USER(&USER)) /* Pre-select jobs by User and Job Type. Output to spool file. */ WRKUSRJOB USER(&USER) STATUS(*ACTIVE) OUTPUT(*PRINT) + JOBTYPE(&JOBTYPE) /* Set up the flat physical for processing. */ DLTF FILE(QTEMP/DSPSBJ) MONMSG MSGID(CPF0000) CRTPF FILE(QTEMP/DSPSBJ) RCDLEN(80) CPYSPLF FILE(QPDSPSBJ) TOFILE(QTEMP/DSPSBJ) + SPLNBR(*LAST) OVRDBF FILE(DSPSBJ) TOFILE(QTEMP/DSPSBJ) /* Call ENDUSRJOBR to finalize the selection by Job Name and */ /* end the selected jobs. */ CALL PGM(ENDUSRJOBR) PARM(&JOBNAME &OPTION) /* Clean up. */ DLTF FILE(QTEMP/DSPSBJ) ENDPGM
Here is the source for the ENDUSRJOBR program:
* ENDUSRJOBR is called by ENDUSRJOBC, the command processor for the * ENDUSRJOB command. The ENDUSRJOB command ends jobs based on selection parms. * Jobs to end are selected based on User, Job Type, and Job Name. * ENDUSRJOBC pre-selects active jobs by User and Job Type, using WRKUSRJOB * with OUTPUT(*PRINT). The spool file is copied to a flat file in QTEMP. * The flat file is processed in this program to make the final selection for * Job Name and end the selected jobs. * This program is a demonstration. Please test this program * thoroughly before using it in any production setting. It is * provided as is. Rod Flohr, 2014-09-11 * Process the flat file DSPSBJ using the RPG cycle. FDSPSBJ IPE F 80 DISK DJOBID S 28 DCMD S 200A * Prototype for QCMDEXC D QCMDEXC PR EXTPGM('QCMDEXC') D CMD 200A OPTIONS(*VARSIZE) CONST D CMDLEN 15P 5 CONST * Input specs for flat file DSPSBJ IDSPSBJ AA I 1 80 RECORD I 4 13 JOBNAME I 17 26 USER I 30 35 JOBNUM C *ENTRY PLIST C PARM JOBNAMEIN 10 C PARM OPTION 7 * Only process lines with valid job numbers (skip headings, etc.) C IF %CHECK('1234567890':JOBNUM) = 0 Valid JOBNUM * Compare for selected Job Name, if not '*ALL' C IF JOBNAMEIN = JOBNAME OR JOBNAMEIN = '*ALL' JOBNAME Match * Format the ENDJOB command C EVAL CMD = *BLANKS C EVAL JOBID = JOBNUM + '/' + %TRIM(USER) + '/' + C %TRIM(JOBNAME) * If OPTION is *ABN, use ENDJOBABN, else use ENDJOB C IF OPTION = '*ABN' OPTION *ABN C EVAL CMD = 'ENDJOBABN JOB(' + %TRIM(JOBID) + C ')' C ELSE OPTION *ABN C EVAL CMD = 'ENDJOB JOB(' + %TRIM(JOBID) + C ') OPTION(' + OPTION + ')' C ENDIF OPTION *ABN * Call the ENDJOB command, ignoring any errors. /FREE CALLP(E) QCMDEXC (%TRIM(CMD) : %LEN(%TRIM(CMD))); /END-FREE C ENDIF JOBNAME Match C ENDIF Valid JOBNUM
Probably the simplest way to pick up the source is to download the ENDUSRJOB zip file linked to above. Create empty source members for each object. Then go into Navigator, find your source members in the IFS under QSYS.LIB, right click each source member and click Edit, and then copy and paste the text for that member from the downloaded text file, and save the file. After pasting in the text, go back to SEU or RDi and verify the source is lined up OK and compile everything.
For an example of how this might be useful, check out this post:
Can’t restart Apache on the IBM i because of zombie ZENDSVR6 jobs in QHTTPSVR
Thanks very much!
This saved me a lot of work