Archive

Archive for the ‘REXX’ Category

Using the SDSF REXX interface – Part 2

October 12, 2013 Leave a comment

Here’s another REXX exec using the SDSF REXX interface. This one will issue a command to the local system.

/**REXX**************************************************************/   
/*                                                                  */   
/* ISSUCMD - Issue a command.                                       */   
/*                                                                  */   
/* Input args:                                                      */   
/*                                                                  */   
/*  Delay   - # secs to wait for command completion                 */   
/*  Command - Command to issue (do not need leading /)              */   
/*                                                                  */   
/* Output: return code from the command.                            */   
/*                                                                  */   
/********************************************************************/   

parse arg delay, command                                                 

Address 'TSO'                                                           

IsfRC = isfcalls( "ON" )                                                 

saved_delay=ISFDELAY            /* save curr delay */                   
ISFDELAY=delay                  /* Set delay time  */                   

address SDSF "ISFEXEC '/"command"'"                                     
saverc=rc                                                               

ISFDELAY=saved_delay            /* restore delay   */                   

IsfRC = isfcalls( "OFF" )                                               

return saverc

Using the above command to start a started task on the local system:

/* REXX */
rc=ISSUCMD(2,"S CICS1")
return 0
Categories: Mainframe, REXX Tags: ,

Using the SDSF REXX interface – Part 1

September 24, 2013 Leave a comment

I thought I’d post some of the REXX execs I have written that use the SDSF REXX interface. This first one determines if a job is executing, either on the specified lpar if passed in or anywhere in the sysplex if not (subject to shared JES spool limits). It returns a return code and, if executing, the lpar the job (or stc) is executing on separated by a space which you can then parse out.

/**REXX**************************************************************/   
/*                                                                  */      
/* JOBSTAT - Determine if a job is executing or not                 */   
/*                                                                  */   
/* Input args:                                                      */   
/*                                                                  */   
/*  Jobname - Required.                                             */   
/*  sysname - Optional. If specified will only look for job on that */   
/*                      system.                                     */   
/*                                                                  */   
/* Output:                                                          */   
/*  0 - Job is not executing (on requested system if specified)     */   
/*  1 - Job is executing + sysname of system job is running on      */   
/*                                                                  */   
/********************************************************************/   

parse arg jobn , sysname                                                 
upper jobn                                                               
upper sysname                                                           

Address 'TSO'                                                           

IsfRC = isfcalls( "ON" )                                                 

saved_delay=ISFDELAY                                                     
ISFDELAY=0                                                               

executing=0                                                             
executing_on=""                                                         

address SDSF "ISFEXEC ST "jobn                                           

if sysname <> "" then do                                                 
   do i = 1 to jname.0                                                   
      if queue.i = "EXECUTION" & actsys.i = sysname then do             
         executing=1                                                     
         executing_on=actsys.i                                           
         end                               
      end                                 
   end                                     
else do                                   
   do i = 1 to jname.0                     
      if queue.i = "EXECUTION" then do     
         executing=1                       
         executing_on=actsys.i             
         end                               
      end                                 
   end                                     

ISFDELAY=saved_delay                       

IsfRC = isfcalls( "OFF" )                 

return executing" "executing_on

Using the above exec to determine if a job is executing on the local lpar:

/**REXX**************************************************************/   
/*                                                                  */  
/* Call jobstat to see if job is running in the local system        */
/*                                                                  */   
/********************************************************************/  

sysname=MVSVAR('SYSNAME')   /* local sysname */

parse value jobstat("MYJOB",sysname) with executing lpar 
if executing then do                               
   /* do 'executing' stuff here */ 
   say 'Job is executing'         
   end                                             
else do                                           
   /* Do 'Not executing' stuff here */  
   say 'Job is not executing'                                 
   end 

return 0

Using the above exec to determine if a job is executing in the sysplex and on which lpar:

/**REXX**************************************************************/   
/*                                                                  */  
/* Call jobstat to see if job is running anywhere in the sysplex    */
/*                                                                  */   
/********************************************************************/  

parse value jobstat("MYJOB") with executing lpar 
if executing then do                               
   /* do 'executing' stuff here */ 
   say 'Job is executing on '||lpar         
   end                                             
else do                                           
   /* Do 'Not executing' stuff here */  
   say 'Job is not executing'                                 
   end  

return 0
Categories: Mainframe, REXX Tags: ,

Anonymous functions in REXX

June 19, 2013 Leave a comment

In this blog post https://www.ibm.com/developerworks/community/blogs/MartinPacker/entry/dragging_rexx_into_the_21st_century?lang=en Martin Packer was experimenting with modernizing REXX on the mainframe. One topic he mentioned but did not resolve was the use of anonymous functions in REXX.

An anonymous function is a block of code that is not associated with an identifier like a function name. It’s just a block of code you can pass to something else (another function) to run. It’s used a lot in things like jQuery to implement ‘callbacks’ from the invoked function back into your own code.

So I got to thinking about this and did a little experimenting. The following is ‘close’ in that I can pass a block of code into a called function and have it execute there. It could implement some sort of ‘callback’ by for example returning data to the original caller on the stack or via an external data set but I did not go that far. I just wanted to see how hard it would be to run some code passed in by the caller. So here’s my solution:

This first program is the caller of some function that will use the anonymous function code block I pass to it. I define the anonymous function in a comment block in the program (makes it easier to write anything longer than a couple of lines) and load it into a variable using sourceline. Notice that each line of the anonymous function has to end with a semicolon since all the lines get concatenated together into one big string.

/* rexx */                                                             

func=loadfunc('MYANONFUNC')                                                                                                               
rc=z67('parm for z67',func,'This is a message')                        
return 0                                                               

/*-------------------------------------------------------------*/      
loadfunc: procedure                                                    
parse arg reqdname                                                     

code=''                                                                

indata=0                                                               
do i = 1 to sourceline()                                               
   line = sourceline(i)                                                
   select                                                              
      when substr(line,1,11)='/*FUNCTION ' then do                      
         parse var line . name .                                       
         if name=reqdname then do                                      
            indata=1                                                   
            end                                                        
         end                                                           
      when substr(line,1,2)='*/' & indata=1 then leave                 
      otherwise do                                                     
         if indata then code = code||line                              
         end                                                           
      end /* select */                                                 
   end /* do i=1 to sourceline() */                                    

return code   
/*-------------------------------------------------------------*/      

/*FUNCTION MYANONFUNC                                                      
trace "I";                                                             
parse arg msg;          
say msg;   
trace "O";              
return 0;                
*/

The following code is the called function (called z67 in this case), It accepts a parameter for its own use (myparm) which is just displays to demo the ability, the function string itself (func) and any data (funcdata) to be passed to the anonymous function from the caller.

/* rexx */                           
parse arg myparm, func, funcdata     
say "myparm is ("myparm")"           
rc=x(funcdata)                       
return rc                            

x: procedure expose func             
interpret func                       
return 0

The code above parses out the three arguments passed in, displays the first one and then calls an internal function (x) passing it the function data for the anonymous function (funcdata). That allows the passed code to parse any arguments passed to it by the original caller. The ‘x’ routine gets to the function code itself by exposing the ‘func’ variable and just executes it using interpret.

Notice that the anonymous function code can also accept arguments from the original caller and do things like turn on trace. It would be quite easy to modify this code so that a variable number of parameters could be passed to the invoked passed function.

Categories: Coding, Mainframe, REXX Tags:

Dynamic ISPF Panels – Getting the Input Data

April 23, 2013 Leave a comment

In the previous article I looked at the general process of building the ISPF screen dynamically, including the creation of input fields. Now it’s time to get the users input from those fields into the application. Again I am using REXX as the driving code.

In a typical static ISPF panel, fields are referred are named and the data in them can be set and retrieved by variable name within the application.

With a dynamic ISPF panel there are no variables that you can refer to, only the variable being used to refer to the data string that is the panel definition. when the user enters some data into the input fields on your screen and presses enter, ISPF rebuilds the entire data string and updates the dynamic screen variable, complete with text fields you output, attributes and input data from the user. You have to manually extract the input data from that string and place the data into your own variables.

The process of extracting the input data from the screen variable is a lot simpler if you only ever use one attribute character to mark the start of an input field and one attribute character to mark the end of an input field.

In the previous article I had the following hexadecimal attribute definitions in my skeleton panel:

01 TYPE(DATAOUT) INTENS(LOW)                      
02 TYPE(DATAOUT) INTENS(HIGH)                     
03 TYPE(DATAIN)  INTENS(LOW) COLOR(GREEN) PAD(_)  
04 TYPE(DATAOUT) INTENS(LOW) COLOR(RED)           
05 TYPE(DATAOUT) INTENS(LOW) COLOR(YELLOW)

And I built each input line using the following code:

dol='01'x                                                       
din='03'x                                                       
doy='05'x                                                       
dynvar=doy||SetSize('Enter something..............',24)||din||, 
            SetSize('here',8)||dol

The effect of the above code is that the start of each input field is marked by a ’03’x attribute byte and the end of the input field is marked by an ’01’x attribute byte.

The following piece of code extracts the input fields from the screen variable and places them into stem variables, then sets the total number of input fields in the dot zero stem variable:

input.=''
i=0                                               
do while pos('03'x,dynvar) <> 0                   
   i=i+1                                          
   parse var dynvar . '03'x data '01'x dynvar     
   input.i=data                                   
   end                                            

input.0=i

Hopefully it is now obvious why I chose to use hexadecimal values for attribute characters. If I had used something the user could enter, as the end of field attribute byte then if the user entered that character, the above code would only extract data up to that character, in effect cutting the input data short.

If you use multiple differing attribute bytes to delimit the start or end of the input fields then the above code becomes more complex since you have to allow for the multiple differing start and end of field markers.

This technique works fine if the input fields are always in the same order on the screen. Remember, there is nothing in the input data from the screen that really defines what each field contains unless you look at the text that occurred before it, so you have to know that the first input field always contains data for a specific item, the second input field always contains data for the next specific item and so on.

If your application can build the screen with input fields that appear in differing order for different situations then YOU have to keep track of the order of the input fields on the screen and extract the data into the appropriate variables for your application code to process.

One way is certainly to look at the prompt that occurred before the input field and use that to determine what the data is and which variable it should go into in the application and I am sure there are many other ways to achieve the same thing. As all the best books say, this is left as an exercise for the reader!

Categories: ISPF, Mainframe, REXX

Dynamic ISPF Panels

April 19, 2013 3 comments

Typically, ISPF panels are very static in nature, at least as far as the content is concerned. Input and output fields are ‘named’ and can be set and contents retrieved by the underlying application through the use of variables and variable services. In addition the panel can contain a wide variety of processing statements to validate the input, thus relieving the application of much of that work.

In all my years working with mainframes I have never worked or had the need to use any other type of ISPF panel – until now!

I was looking at adding an ISPF based ‘wizard’ to some code and wanted to make is a ‘consumable’ as possible.

Well, the reality is that I could have used the typical ISPF static panel but I decided I wanted to try to mimic the more modern dynamic web page applications where the content of the page changes depending upon the options you select.

What I found was that you can define a ‘dynamic’ area in an ISPF panel and actually build the contents of the screen in the application. While I found some general guidelines on how to do this I did not find any specific examples of what you might call ‘real world’ code and so, as usual, I had to invent it all myself!

This article is the result of my experimentation. There are some things still missing like cursor positions for one and handling input and output fields that change order (you’ll see why this is important later on) but should you feel the need to have a go with creating dynamic ISPF panels, hopefully this will help get you started. They are pretty cool!

By the way, since this was simply an exercise my example code is all done in REXX to keep things simple and easy to change.

Panels and execs are all in the the standard ISPF locations. nothing fancy there.

The Panel Definition

Even though you can build pretty much the entire panel in the application, you still need a basic panel definition in the ISPPLIB concatenation. This is mine:

DynamicISPFPanel

The dynamic area is defined by the ‘$’ symbol in the panel. It extends the full width of the panel (80 chars) and is referred to by the name DYNVAR. The ‘$’ are NOT treated as attribute characters and do NOT take up space on the panel, they are in fact part of the dynamic area.

I also defined several hexadecimal attribute character strings x’01’ through x’05’. I used hexadecimal characters so that there is no way the user can enter them into the input areas. This is important because YOU have to find them in the screen var (DYNVAR in my case) in your code and if you sued a character the user could enter you might mistake user input for attribute characters.

Cursor positions is currently rudimentary and fixed. I position the cursor at column 27 in the dynamic screen variable DYNVAR, which is the location of my first input field.  Ifthe location of the first input field could move I would have to make that positioning more dynamic by using a variable and setting the offset in the dynamic var in the application code. For now, this works though!

The Dynamic area

In my case, the dynamic area is 80 characters long. The way it is built is that the first 80 characters of whatever is in the DYNVAR variable are used to build the first line of the dynamic area. Then the next 80 characters are used for the second line, the next 80 characters for the third line and so on.

So, if I do the following in the application exec (My test panel is called Z991):

dynvar='This is a test.'
address ISPEXEC 'DISPLAY PANEL(Z991)'

I would get this:

DynamicISPFPanel-001

Notice the cursor is where I positioned it in the panel definition in spite of the fact there is nothing there yet!

In order to get something to display on the next line I have to pad that text out to 80 characters and then add the next line of text. So I’d need driving code something like this:

dynvar=fit2line('This is a test.')||'This s line 2.'
address ISPEXEC 'DISPLAY PANEL(Z991)'

Which gives me this:

DynamicISPFPanel-002

In case you are wondering what ‘fit2line’ is, it’s a little function I wrote that just pads out whatever is passed to it to a multiple of the line length (80 characters in my case). It looks like this:

Fit2Line: procedure             

parse arg in                    
ll=80      /* must = line length */
l=length(in) // ll              
out=in||copies(' ',ll-l)        
return out

If I wanted to add a third line, the dynvar assignment would be something like this:

dynvar=fit2line('This is a test.')||fit2line('This s line 2.')||'This is line 3.'

Input Fields

So now lets add an input field at the cursor location.

Since DYNVAR starts in column 1 (remember the ‘$’ is NOT an attribute, just a marker for the dynamic area) and the cursor is at column 27 on line 1 of the area, we need to make the data up to the cursor position 26 characters long.

However, Since the cursor is at position 27 and this is going to be an input field, we need an attribute character just before it. It would also be nice if we had an attribute character before the text as well.

If you go back to the panel definition, you will see that it has the following attributes defined:

01 TYPE(DATAOUT) INTENS(LOW)                      
02 TYPE(DATAOUT) INTENS(HIGH)                     
03 TYPE(DATAIN)  INTENS(LOW) COLOR(GREEN) PAD(_)  
04 TYPE(DATAOUT) INTENS(LOW) COLOR(RED)           
05 TYPE(DATAOUT) INTENS(LOW) COLOR(YELLOW)

Each 2 digit value represents a single hexadecimal attribute byte.

The input attribute is hex ’03’ and there are a variety of output attributes. I’m going to make the text yellow so I am going to use the hex ’05’ value.

My code now looks like this:

din='03'x                                                     
doy='05'x                                                     
dynvar=doy||SetSize('Enter something..............',24)||din

I defined the attributes as variables to make them easier to recognize.

SetSize is a simple routine to set that length of a string to a specified size. It looks like this:

setsize: Procedure                        
parse arg in , l                          
if length(in) < l then return left(in,l)  
return left(in||copies(' ',l),l)

This is what the resultant panel looks like:

DynamicISPFPanel-003

Slight problem! We did not set the location of an attribute to indicate the end of the input field/start of the next field. Hence it was continued all the way to the end of the dynamic area!

Remember that everything follows on sequentially so we need to set a string to the required length of the input field and then add in a closing attribute byte. The input field string can be spaces or it can contain some initial data. So now my code looks like this:

dol='01'x                                                       
din='03'x                                                       
doy='05'x                                                       
dynvar=doy||SetSize('Enter something..............',24)||din||, 
            SetSize('here',8)||dol

I’ve added a ‘dataout low’ attribute (see panel definition) and set the input field size to 8 characters. I’ve also initialized the input field data to the word ‘here’. Now my screen looks like this:

DynamicISPFPanel-004

If I wanted to add another input field on the third line so that there is a blank line between the input lines then my code becomes something like this:

dol='01'x                                                         
din='03'x                                                         
doy='05'x                                                         
dynvar=doy||SetSize('Enter something..............',24)||din||,   
            SetSize('here',8)||dol                                
dynvar=fit2line(dynvar)||fit2line(' ')||,                         
       doy||SetSize('Enter something else.........',24)||din||,   
            SetSize('here',8)||dol

Notice that after setting the first line up I had to use the fit2line function to expand it to fill the whole line. Then I used the fit2line function with a single space to cause it to add a blank line and finally I added the new line to the dynamic variable. My screen now looks like this:

DynamicISPFPanel-005

Of course the data I have displayed in the input fields could have come from variables instead of hard coded literals, as could the prompts.

In the next part of this article I shall look at the steps needed to extract the input data from the screen into the application.

Categories: ISPF, Mainframe, REXX

Sending e-mail from REXX

July 20, 2012 Leave a comment

The basic SMTP protocol used for email is actually pretty simple and you can implement it quite easy using any socket interface.

I found the original version of this exec on the internet here although I have tweaked it a little. Original author is acknowledged.

Obviously you could modify it some to accept different parameters and maybe the bulk of the message data from the stack but it makes a  great starting point:

/* REXX EMAIL */
/* from http://ibmmainframes.com/about12103.html */

smtp_server      = 'mail.server.url'                 /* mail server address                   */
smtp_from        = 'someone@someplace.com'          /* Who this is from                      */
smtp_to          = 'someoneelse@somewhereelse.com'     /* Who it is to on the smtp_server       */
smtp_replyto     = 'someone@someplace.com'           /* Who to send any replies to.           */
                                                    /* Does not have to be same as smtp_form */

crlf = x2c('0d25')

/* SMTP Initialization */
str = Socket('initialize', Date(B))
Parse Var str sockrc subtaskid maxdesc tcpipuser
str = Socket('Socket', 'af_inet', 'stream', 'tcp')
Parse Var str sockrc sockid
str = Socket('SetSockOpt', sockid, 'sol_socket', 'SO_ASCII', 'on')
server_info = 'AF_INET 25 ' || smtp_server
str = Socket('Connect', sockid, server_info)
str = Socket('Recv', sockid, 10000)
say str
Parse Var str sockrc data_length smtp_response
msg= 'HELO ' || smtp_server || crlf
str = Socket('Send', sockid, msg)
str = Socket('Recv', sockid, 10000)
say str

/* Mail Message */
msg= 'MAIL FROM:<' || smtp_from || '>' || crlf
str = Socket('Send', sockid, msg)
str = Socket('Recv', sockid, 10000)
say str
/* Repeat RCPT TO section to send to multiple users */
msg= 'RCPT TO:<' || smtp_to || '>' || crlf
str = Socket('Send', sockid, msg)
str = Socket('Recv', sockid, 10000)
say str
msg= 'DATA' || crlf
str = Socket('Send', sockid, msg)
str = Socket('Recv', sockid, 10000)
say str
the_subject = 'This is a Test #1'
msg = 'To:' smtp_to || crlf ,
   || 'Reply-To:' smtp_replyto || crlf ,
   || 'Subject:' the_subject || crlf ,
   || 'X-Mailer: REXX Exec on ZOS' || crlf
str = Socket('Send', sockid, msg)
str = Socket('Send', sockid, crlf)
msg = 'This is a test (#1) from ' || Userid() || crlf
str = Socket('Send', sockid, msg)
msg = crlf || '.' || crlf
str = Socket('Send', sockid, msg)
str = Socket('Recv', sockid, 10000)
say str
/* End of Mail Message */

/* SMTP Termination */
msg= 'QUIT' || crlf
str = Socket('Send', sockid, msg)
str = socket('Close', sockid)
str = socket('Terminate', subtaskid)
Say 'Email sent to ' smtp_to
Exit

The only downside to this implementation as it stands is that you have to know the receiver’s mail server name. (shown as mail.server.url in the example). There is a mechanism (described in RFC 1035) for talking to a DNS server to obtain the MX (Mail Exchange) records for a domain so that you can find the url of the mail server(s) for a domain name (like IBM.com) but I’ve not looked into that yet. Something for the future maybe.

Categories: Mainframe, REXX

The case of the wrong case…

September 18, 2011 Leave a comment

This has been a bit of a detective story so I thought I’d mention it here in case it helps anyone.

As part of my efforts to move my web based ISPF interface to the stand alone IBM HTTP server I needed to run the REXX execs under USS. Now to be honest I rarely ever touch USS on z/OS but that’s where I needed to be so that’s where I am.

I typically write my REXX execs in lower case so I might end up with one exec invoking another with something like this:

data=myfunc(parm1,parm2,etc)

Where myfunc is another REXX exec in the standard search order (SYSPROC and SYSEXEC typically). This works fine because even though the function name is written in lower case, REXX converts it to upper case and that matches the member name in the library just fine. Everything works great.

BUT! Move over to USS land and things are not so simple. REXX in USS land is case sensitive. Using the example above, I had created the ‘myfunc’ exec file  in USS with a lower case file name and was surprised when the calling exec could not find it.

Eventually (after two days) I found that by default, REXX makes function names coded like this into UPPER CASE before searching for them (I knew this, I had just forgotten about it) so REXX was searching for a file called ‘MYFUNC’ whilst the file I had created was called ‘myfunc’. Not the same animal in a case sensitive environment.

I could make all my exec file names upper case to address this, but in the event you need to call a lower case function name you can code it like this:

data='myfunc'(parm1,parm2,etc)

And amazingly it will now find the lower case exec file.