Wednesday, August 22, 2018

Using Environmental Variables

environment values are more than just widening the strdbg window

This all started with what was going to be a very short post about using an Environment Variable to set the width of your debug screen. But I became curious and by the time I finished playing with them this grew into how to create your own Environment Variables, and use them.

Let me start with the question: What is an Environment Variable?

Environment Variables come in two types:

  1. System – these Environment Variables are stored in the global environment space and are available to all jobs running on this IBM i system, and are persistent even when the system is IPL-ed.
  2. Job – the variables are outside the program's space, and are job specific, when the job ends they are automatically deleted.

Manipulation of Environment Variables is by CL commands and APIs.

From my playing I have concluded that Environmental Variables should be created and changed at the job level. Leave the system ones alone, changes to them will affect every job on the system, including all future jobs. With the job ones if anything goes amiss you can just signoff to end the job.

Let me start with this example of using an existing Environment Variable...

 

Using an Environment Variable to set the width of the debug screen

Totally free RPG finally liberated us from the restrictions placed on us by punch cards. I can now start my code in the first column of the source member and keep on going until the end of the member. If I am using SEU I can change the width of my editor display to accommodate this. But when using debug, STRDBG, I am locked into the 80 column width.


Click on image for larger version

I have found that I am often having to use the F20 to move to the right, and the F19 to move back to the left again more and more. It would be nice if there was a way I could expand the debug view to 132 columns.

There is an Environment Variable to change the default for me. All I need to do is to type in:

ADDENVVAR ENVVAR(ILE_DEBUGGER_1) VALUE(ALLOW_WIDE_SCREEN) LEVEL(*JOB)

ENVVAR(ILE_DEBUGGER_1): the value of this parameter must be entered in upper case.

VALUE(ALLOW_WIDE_SCREEN): the value must also be entered in upper case only.

LEVEL(*JOB): As I mentioned above I am only adding and changing Environmental Values at the job level.

Now when I enter debug I get to see 132 columns!


Click on image for larger version

This is great! I have changed the initial program that is executed when I signon to add this command to it, and I will always see debug in the wide view format.

If I had decided that the wide view was not for me I can remove it my using the following:

RMVENVVAR ENVVAR('ILE_DEBUGGER_1') LEVEL(*JOB)

 

Using Environment Variables

After that quick example let me explain what else I have found. There are four commands that can be used with Environment Variables:

  1. ADDENVVAR add
  2. CHGENVVAR change
  3. RMVENVVAR remove/delete
  4. WRKENVVAR work with

Let's start with the WRKENVVAR command. If I just type that in the command its default is to display the job variables.

WRKENVVAR

On my server and job I am shown:

                      Work with Environment Vars (*JOB)

Type options, press Enter.
  1=Add   2=Change   4=Remove   5=Display details   6=Print

Opt  Name                        Value
 _                           
 _   LANG                        '/QSYS.LIB/EN_US.LOCALE            ' >
 _   ILE_DEBUGGER_1              'ALLOW_WIDE_SCREEN'

I added the ILE_DEBUGGER_1, the other comes from me wanting US English to be my default language.

I can also use the SQL View ENVIRONMENT_VARIABLE_INFO to see the same information. Personally I prefer this as it will display the system and job variables.

01  SELECT VAR_TYPE AS TYPE, 
02         CAST(VAR_BNAME AS CHAR(50)) AS NAME,
03         VAR_BVALUE AS VALUE,
04         VAR_CCSID AS CCSID
05    FROM QSYS2.ENVIRONMENT_VARIABLE_INFO

I have used the short names for the columns as the long names are, well, very long. This is only a subset of the available columns. If you want to know more about this View there is a link to IBM's documentation at the bottom of this post.

The results look like:

TYPE    NAME             VALUE                    CCSID
SYS     CLASSPATH        1                           37
JOB     LANG             /QSYS.LIB/EN_US.LOCALE     285
JOB     ILE_DEBUGGER_1   ALLOW_WIDE_SCREEN           37

The columns I have selected are:

  • VAR_TYPE/TYPE the type of environment variable either SYS for system or JOB for job.
  • VAR_BNAME/NAME the name of the variable. I reduced its size to 50 so that the column width is a manageable size.
  • VAR_BVALUE/VALUE value contained in the variable.
  • VAR_CCSID/CCSID the CCSID of the variable.

I can easily create my own Environment Variables:

ADDENVVAR ENVVAR('SIMON_TEST') VALUE('ABC123') LEVEL(*JOB) REPLACE(*YES)

And I can see it with the WRKENVVAR command:

Opt  Name                        Value
 _                           
 _   LANG                        '/QSYS.LIB/EN_US.LOCALE            ' >
 _   ILE_DEBUGGER_1              'ALLOW_WIDE_SCREEN'
 _   SIMON_TEST                  'ABC123'

I can change the value of the variable I just created by using the change command:

CHGENVVAR ENVVAR('SIMON_TEST') VALUE('Something else')


Opt  Name                        Value
 _                           
 _   LANG                        '/QSYS.LIB/EN_US.LOCALE            ' >
 _   ILE_DEBUGGER_1              'ALLOW_WIDE_SCREEN'
 _   SIMON_TEST                  'Something else'

The real question is how I can use these in RPG, and of course there is an example program.

01  **free
02  ctl-opt option(*nodebugio:*srcstmt) dftactgrp(*no) ;

03  dcl-pr getenv pointer extproc('getenv');
04    *n pointer value options(*string:*trim)  ;
05  end-pr;

06  dcl-pr putenv int(10) extproc('putenv');
07    *n pointer value options(*string:*trim) ;
08  end-pr;

09  dcl-s EnvVarValue char(500) ;

10  EnvVarValue = %str(getenv('SIMON_TEST')) ;

11  EnvVarValue = 'Updated in RPG program' ;
12  putenv('SIMON_TEST=' + EnvVarValue) ;

13  EnvVarValue = 'Created in RPG program' ;
14  putenv('SIMON_TEST_1=' + EnvVarValue) ;
 
15  if (putenv('SIMON_TEST_1=' + 'Test again') <> 0) ;
16    dsply ('Some error happened') ;
17  endif ;

18  *inlr = *on ;

Line 1: Yes, I am programming in totally free RPG.

Line 2: My favorite control options, and I need to use the DFTACTGRP(*NO) as I am going to be calling external procedures.

Lines 3 – 5: This is the procedure definition for getenv, which I will use to get the value of a Environment Variable. Line 3 shows that it returns a pointer type variable, and as it is an external procedure I need to define it in the EXTPROC keyword. Line 4 shows us that a pointer used to pass data to this procedure, and with the VALUE and OPTIONS(*STRING:*TRIM) the passed character parameter is passed as a C type string type variable, which is null terminated.

Lines 6 – 8: Another external procedure definition for putenv. This returns a 10 long integer number. The passed pointed is defined as the same as the one in the procedure before.

Line 9: This variable will contain the string I will be retrieving and updating the Environment Variables with.

Line 10: This shows how simple it is to retrieve a Environment Variable. In this example I want the value for SIMON_TEST. The %STR built in function will get the C type string from the pointer.

A quick look at debug, with the wide screen, and I can see the value of the variable.

EVAL EnvVarValue
ENVVARVALUE =
      ....5...10...15...20...25
 1   'Something else

Lines 11 and 12: I am changing the value of EnvVarValue and then using it to update the Environment Variable but using the putenv. Notice simple the parameters used by putenv are, it is just: Environment Variable name = some value.

Lines 13 and 14: What happens if I try to update an Environment Value that does not exist, I will have to see after this program ends.

Lines 15 – 17: The IF statement, line 15, will cause the following code to be executed if the putenv ends abnormally. When putenv ends normally it returns zero, therefore, if the returned value is not zero something went wrong.

After the program ended I use the WRKENVVAR command to see if my Environment Variable had changed.

Opt  Name                        Value
 _                           
 _   LANG                        '/QSYS.LIB/EN_US.LOCALE            ' >
 _   ILE_DEBUGGER_1              'ALLOW_WIDE_SCREEN'
 _   SIMON_TEST                  'Updated in RPG program'
 _   SIMON_TEST_1                'Test again'

My surprise was that the putenv had created the variable SIMON_TEST_1!

If I would have preferred to use SQL to retrieve values from the variables I would have used the following statement:

    exec sql SELECT CAST(VAR_BVALUE AS CHAR(500)) INTO :EnvVarValue
               FROM QSYS2.ENVIRONMENT_VARIABLE_INFO
              WHERE CAST(VAR_BNAME AS CHAR(20)) = 'SIMON_TEST' ;

The value from SIMON_TEST would be place in the variable EnvVarValue.

How could I use Environment Variables in the "real world"?

If I have a standard set of values that are needed by a number of programs in a job I could load them all into an Environment Variable, and then get variable in each program.

I could put data into an Environment Value and then "pass it" to a submitted job. If the CPYENVVAR is yes the submitting job will pass all of the Environment Variables to the submitted job.

SBMJOB CMD(CALL PGM(SOME_PGM)) 
       JOB(SOME_JOB)
       CPYENVVAR(*YES)

At present I am just very pleased I can view debug in wide screen.

 

You can learn more about this from the IBM website:

 

This article was written for IBM i 7.3, and should work for earlier releases too.

11 comments:

  1. This is one of the best posts you've ever written. I've checked both V5R4 and V4R5 and the commands are available for both.

    Note that you have to have *JOBCTL special authority to use these commands for system environment variables. Any user can use them for job environment variables.

    (Corrected: Please delete my previous comment.)

    ReplyDelete
  2. Awesome maybe it could be useful for Production/Test environment variables?

    ReplyDelete
  3. Environment variables are very helpful in affecting the behavior of QSHell and other programs outside the traditional RPG and CL world.

    ReplyDelete
  4. Cool! I wasn't aware of QSYS2.ENVIRONMENT_VARIABLE_INFO.

    ReplyDelete
  5. This will be nice to use while debugging on green screen, something I still have to do very often.

    ReplyDelete
  6. Excelent Simon thanks for sharing 👍

    ReplyDelete
  7. There are A LOT of things that can be controlled by environment variables. If you do a search on ADDENVVAR in the Knowledge Center you will find a few of them documented. In addition, there are others that are only documented by IBM Support (a search there will also find some), and there are many that are undocumented.

    In addition, IBM also controls behavior with data areas, and like environment variables, the documentation on them may or may not exist.

    I wish there was a complete list of these hidden "knobs" somewhere.

    Here are a few of my favorite uses of environment variables that I've written about:
    https://techchannel.com/SMB/12/2019/maximum-spooled-files
    https://dawnmayi.com/2014/02/24/sending-a-spooled-file-via-tcp-when-working-with-spooled-files/
    https://dawnmayi.com/2010/08/02/debug-heap-usage-problems/

    ReplyDelete
  8. I love this! I use environment variables in web development and this gives me some food for thought!

    ReplyDelete
  9. Yep env variables a lot in the open source/PASE/Linux world. Similar to data area in some sense, but not exactly :-)

    ReplyDelete
  10. We use an environment variable to flag when somebody has temporarily profile swapped to augment their own user’s special authorities (e.g. implementing a change to the system) so we can force a joblog on sign off to see what they did (using the change command exit point). Very useful feature indeed.

    ReplyDelete
  11. One I found recently is QIBM_SRVRMODE_SBS.
    If set to *YES for LEVEL(*SYS) or LEVEL(*JOB) it results
    in CLI jobs that run in SQL SERVER MODE to spawn their QSQSRVR prestarted jobs in the same subsystem instead of the default QSYSWRK.
    This enables us to keep sets of REST-API server-jobs for multiple (test-)environments grouped in their respective subsystems.

    ReplyDelete

To prevent "comment spam" all comments are moderated.
Learn about this website's comments policy here.

Some people have reported that they cannot post a comment using certain computers and browsers. If this is you feel free to use the Contact Form to send me the comment and I will post it for you, please include the title of the post so I know which one to post the comment to.