Useful docker commands

Docker commands

Docker network commands


Monitoring docker

Docker image

High level summary of what’s active. For all containers

docker container ls

Gave (I’ve formatted it to make it easy to read – the data is a very long line)

CONTAINER ID :f6e04a6aabac   
IMAGE :otel/opentelemetry-collector
COMMAND :"/otelcol --config o…"
CREATED :8 minutes ago
STATUS : Up 8 minutes
PORTS: :0.0.0.0:4318->4318/tcp, [::]:4318->4318/tcp, 55679/tcp, 0.0.0.0:9999->4317/tcp, [::]:9999->4317/tcp
NAME :otelcollector

Issue a command in a container

To issue the ls command in the jaeger2 container.

docker exec jaeger2 ls

Create and run an image

docker run -p 4317:4317 -p 8888:8888 -p 9464:9464 –name otel otel/opentelemetry-collector:latest -v /home/colin/otel/otel.yaml:/etc/oteltol/config.yaml

  • This runs otel/opentelemetry-collector:latest (downloading it if necessary)
  • -p map port -p external_port:internal_port
  • -v map volume(file) -v external:internal the otel image uses a file /etc/oteltol/config.yaml. When the program running in the container accesses /etc/oteltol/config.yaml it uses the file /home/colin/otel/otel.yaml in Linux.

Display active images docker ps

Displays all images docker ps -a

colin@ColinNew:~/otel$ docker ps -a
CONTAINER ID IMAGE COMMAND ... NAMES
ee578b476ac1 prom/prometheus "/bin/prometheus --c…" ... prom
0f51508e07a2 otel/opentelemetry...:latest "/otelcol -v /home/c…" ... otel

You can refer to a container using the container id (ee578b476ac1) or the name(prom).

Docker inspect – show the configuration

docker inspect otel

gave

[
{
"Id": "2fb850a85e31125f936eb6994440757be33c509d2c80ef66864f55341219c6a0",
"Created": "2026-06-28T10:33:31.048547624Z",
"Path": "/otelcol",
"Args": [
"--config",
"/etc/otelcol/config.yaml"
],
"State": {
"Status": "exited",
...
"StartedAt": "2026-06-28T10:33:31.151675197Z",
"FinishedAt": "2026-06-28T10:33:35.449264219Z"
},
"Image": "sha256:140cdb56eeea12ebc33bb4f7109fd4eef90391933f8d85b33384fcfe1cf040c4",

Docker network

List active docker networks

docker network ls

gave

NETWORK ID     NAME                  DRIVER    SCOPE
b5bbab0b08f1 bridge bridge local
4e4dc158fa88 host host local
0cfcba5bb0ff none null local
b027e6a0f6ed otel-jaeger-network bridge local

Give details about one network

docker network inspect otel-jaeger-network 

gave

[
{
"Name": "otel-jaeger-network",
"Id": "b027e6a0f6edc5f290b25c10852cf6022cb3dc05ab47c5ec6175910354c1d8ce",
"Created": "2026-06-28T17:32:51.894552966+01:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv4": true,
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"IPRange": "",
"Gateway": "172.18.0.1"
}
]
},
...
"Containers": {
"a979647ba520ccc44ff600dd445b50606a91252fb0850318b823b7b51d3d2329": {
"Name": "otelcollector",
...
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
}
},
...

Which ports are in use within a container?

docker  port  otelcollector
4317/tcp -> 0.0.0.0:4317
4317/tcp -> [::]:4317
4318/tcp -> 0.0.0.0:4318
4318/tcp -> [::]:4318

What is my container doing ?

docker  stats   otelcollector

CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
68cf3ad47887 otel... 0.00% 16.24MiB / 27.05GiB 0.06% 85.3kB / 12.3kB 131kB / 0B 14

Images

What images do I have installed?

docker image ls

gave

IMAGE                           ID             DISK USAGE   CONTENT SIZE   EXTRA
cr.jaegertrac...jaeger:2.19.0 ede4864215be 192MB 56.6MB
jaegertracing/all-in-one:... ab6f1a1f0fb4 123MB 37.4MB
otel/opentelemet... 140cdb56eeea 240MB 48.5MB U
prom/prometheus:latest a75c5a35bc21 427MB 118MB

Details of an image

docker image inspect jaegertracing/all-in-one
[
{
"Id": "sha256:ab6f1a1f0fb49ea08bcd19f6b84f6081d0d44b364b6de148e1798eb5816bacac",
"RepoTags": [
"jaegertracing/all-in-one:1.76.0",
"jaegertracing/all-in-one:latest"
],
"RepoDigests": [
"jaegertracing/all-in-one@sha256:ab6f1a1f0fb49ea08bcd19f6b84f6081d0d44b364b6de148e1798eb5816bacac"
],
"Comment": "buildkit.dockerfile.v0",
"Created": "2025-12-03T17:07:31.304698052Z",
"Config": {
"User": "10001",
"ExposedPorts": {
"14250/tcp": {},
"14268/tcp": {},
"16686/tcp": {},
"4317/tcp": {},
"4318/tcp": {},
"9411/tcp": {}
},
...

One minute – docker

This is one of the “one minute MVS” series of topics to provide the core of what you need to know on a subject to get started.

The problem

I want to run a self contained application suites on my laptop. There are two versions, old and new. Both use the same IP ports, and configuration files (/etc/myapp/config.yaml). How do I do it?

One solution is to use docker.

Docker is container management.

You need to install docker on your machine. I used sudo apt install docker.

My first docker image

docker run --rm --name jaeger \
-p 16686:16686 \
-p 4317:4317 \
-p 4318:4318 \
-p 5778:5778 \
-p 9411:9411 \
cr.jaegertracing.io/jaegertracing/jaeger:2.19.0

When you run this command

  • the first time, it downloads a prebuilt image cr.jaegertracing.io/jaegertracing/jaeger from a docker site. The second time is uses the previously downloaded image
  • it will download version 2.19.0 (you can specify latest)
  • it is given a name jaeger
  • it also has a number which can be used in commands
  • it maps its ports -p external:internal. In my config file I have endpoint: “0.0.0.0:4317”. This maps to the 4317 on my laptop. I could have a second container and pass parameters -p 5317: 4317. The same config file can be used. To talk to the second container, I need to use port 5317.
  • when it shuts down, the image is removed (–rm)
  • you stop the image using docker stop jaeger. You can restart it with docker start jaeger.
  • you remove the image using docker rm jaeger.

Commands

See docker commands.

Using files within a docker image

The program may use a configuration file for example /etc/myprog/config. You can map this to a file outside of the container.

For example

docker run --rm  --name otelcollector \
--volume "$(pwd)/o1.yaml":/otel-config.yaml \
-v "/mnt/Colin/ssl/ssl2/tempcert.pem":"/server.pem" \
-v "/mnt/Colin/ssl/ssl2/tempcert.key.pem":"/server.key.pem" \
...
otel/opentelemetry-collector --config otel-config.yaml

This says when the application accesses /server.key.pem, this is mapped to file /mnt/Colin/ssl/ssl2/tempcert.key.pem on my laptop.

In my config file I have

receivers:
otlp:
protocols:
grpc:
endpoint: "0.0.0.0:4317"
tls:
cert_file: server.pem
key_file: server.key.pem

min_version: "1.3" # Enforces TLS 1.3 as the minimum requirement
max_version: "1.3" # Locks maximum version to TLS 1.3
cipher_suites: []
reload_interval: "1h"
http:

Docker to Docker

I have one docker image with OpenTelemetry (otel), and another with Jaeger, which is used to display information produces by the otel image. I found this article useful

Create a docker network (once)

docker network create otel-jaeger-network

This can be used

docker run --rm  --name jaeger \
...\
--network otel-jaeger-network \
jaegertracing/all-in-one:latest

and

docker run --rm  --name otelcollector \
--volume "$(pwd)/o1.yaml":/otel-config.yaml \
...
--network otel-jaeger-network \
otel/opentelemetry-collector --config otel-config.yaml

The otel config file has

endpoint: "0.0.0.0:4317"

The jaeger has default of localhost:4317 because these share the same network they are each end of a socket.

Docker to docker

The problem

I have one application running in one docker container, and another application in another docker container. How do I get them to talk to each other?

Docker to Docker

I have one docker image with OpenTelemetry (Otel), and another with Jaeger, which is used to display information produces by the Otel image. I found this article useful. You do not need to know what Otel and Jaeger are, you just need to know that data from an external site is sent to Otel, and Otel passes some transformed data to Jaeger.

You need to create a docker network, then have the docker images use this network.

Create a docker network (once)

docker network create otel-jaeger-network

Use it

This can be used

docker run --rm  --name jaeger \
...\
--network otel-jaeger-network \
jaegertracing/all-in-one:latest

and

docker run --rm  --name otelcollector \
--volume "$(pwd)/o1.yaml":/otel-config.yaml \
...
--network otel-jaeger-network \
otel/opentelemetry-collector --config otel-config.yaml


Sending data from z/OS to Otel

A program running on z/OS sends data to my laptop and the data goes to the Otel container.

I’ve configured the Otel container

docker run ... 
--publish 9999:4317

My z/OS code sends data to port 9999 on my laptop. The docker code maps port 9999 to port 4317 within the Otel container.

The Otel config file has

endpoint: "0.0.0.0:4317"

and so the Otel code can process the data sent from z/OS

In practice it would be easier to use port 4317 from z/OS and use

 --publish 4317:4317 

Sending data from Otel to Jaeger

My Otel configuration has

exporters:
otlphttp/jaeger:
endpoint: "http://jaeger2:4318"
tls:
insecure: true

The docker code extracts the value jaeger2 from the endpoint.

This ties up with the Jaeger container definition

docker run --rm  --name jaeger2 ...

The program in the Jaeger container is listening on port 4318, and so the data is sent from the Otel container to the Jaeger container.

Why have my JCL symbols stopped working?

I have some JCL which backs up datasets, and passes in today’s date.

//IBMBAKU  JOB 1,MSGCLASS=H,MSGLEVEL=(2,1)              
//** IBS1 JCLLIB ORDER=USER.Z25D.PROCLIB
// SET TODAY=D&YYMMDD
//SG2 EXEC PROC=BACKUP,DD=&TODAY,P=COLIN.PKIICSF.C

Where YYMMDD is a dynamic symbol containing today’s date.

This JCL invokes

//BACKUP   PROC P='USER.Z24C.PROCLIB',DD='UNKNOWN' 
//S1 EXEC PGM=IKJEFT01,REGION=0M,
// PARM='XMIT A.A DSN(''&P'') OUTDSN(''BACKUP.&DD..&P'')'
//SYSPRINT DD SYSOUT=*
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD *
// PEND

This passes in the string D260630, which can be used in the PARM statement.

The XMIT creates a data set BACKUP.D260630.COLIN.PKIICSF.C incorporating today’s date.

Nothing new here.

It was a month or so ago that I last used it – and it worked fine.

Today it did not work! The reason was I had changed to a different z/OS image without “standard” configuration.

The JES2 definitions for the job classes needs SYSSYM=ALLOW

I did this on the console

 $T JOBCLASS(a),SYSsym=allow

and my JCL started working again.

I changed my JES2 definitons in SYS1.PARMLIB(HASJES2) to have

/****************************************************************** 
* JOBCLASS Definitions
*******************************************************************/
JOBCLASS(A) COMMAND=DISPLAY,SYSSYM=ALLOW

so the definitions work after the next IPL.

Installing Data Gatherer on z/OS

OpenTelemetry is a technology for providing trace data as work flows between systems. It is often called OpenTel or OTel.

As work flows around a network, status(“where the work is”) information is sent to a central location, and tools at this central location can sew the data together and produce visualisations of the flow of data, and where the work was delayed.

The data is normally sent over TCP/IP.

Products/programs could emit their own data in the required format, and sent it over TCP/IP to the server. On z/OS data can be written to In-Memory SMF, and a data collector reads the SMF records and sends the data over TCP/IP to the central site.

IBM provides a Data Gatherer. This post is about configuring it and getting it working. It feels a bit rough around the edges, and I would have designed some of it differently.

Where is it?

The code has been forced into the RMF data collector. The data collector code is usually installed in the Unix directory /usr/lpp/grb.

Where is the documentation?

In the z/OS Data Gatherer User’s Guide (SC31-5703-70) – Chapter 3 are instructions on creating security profiles.

The first sentence in this chapter is:

Instructions for setting up the z/OS OpenTelemetry Emitter are available in the file system in your z/OS installation at /usr/lpp/grb/opentelemetry_emitter/dt/README.

These instructions are not clear, and sometimes wrong. (For example to disable TLS, you set a flag, and then define a certificate!) Thanks to Joern Thyssen from Rocket Software for his help in getting it working.

I created some JCL called MYDG and submitted it. You could use a started task. The userid running the data gatherer needs access to the SMF data. If the access to the SMF data is restricted you may want to use a started task with its own restricted userid. Instructions for this are in the Chapter 3 above.

My JCL

//IBMOT32  JOB  (OTEL),MSGLEVEL=(1,1),NOTIFY=&SYSUID
// EXPORT SYMLIST=*
// SET JARFILE='zos-otel-emitter-dt.jar'
// SET $OTLENDP='http://10.1.0.2:4317'
// SET $OTLPRT='grpc'
// SET REGSIZE='0M'
// SET $OTLEXPC='false'
// SET $TLSENBL='false'
// SET $MTLS='false'
// SET $TLSCERT=''
// SET $TLSCLKY=''
// SET $TLSCLCR=''
// SET $SMFRDBS=''
//*
// SET VERSION='21'
// SET JAVADIR='/usr/lpp/java/java21/current_64'
// SET APPHOME='/usr/lpp/grb/opentelemetry_emitter/dt'
// SET $INMRESL='IFASMF.MQOTEL'
// SET $SMFDUMP='false'
// SET $SMFDUMP='true' dump SMF record binary
// SET $SMFRDFL='0'
//JAVAJVM EXEC PGM=JVMLDM&VERSION,REGION=&REGSIZE PARM='/+I'
//STEPLIB DD DISP=SHR,DSN=JAVA.V21R0M0.SIEALNKE
...

Java 21 and higher

If you are using Java 21 or higher, some of the output from Java comes out by default in ASCII (and so is not easily readable). You need to specify

IJO="$IJO -Dfile.encoding=IBM-1047" 

Identifing the SMF data

This reads SMF data identified as IFASMF.MQOTEL, and sends it over http (not https) to http://10.1.0.2:4317.
MQ writes its OpenTel data to SMF records type 1158.

My SMFPRMxx in parmlib has

RECORDING(LOGSTREAM) 
...
INMEM(IFASMF.MQOTEL,RESSIZMAX(128M),TYPE(1158))

This gives a user specified name IFASMF.MQOTEL to records with type 1158. You protect the name IFA.IFASMF.MQOTEL with your security manager. (The Chapter 3 documentation is confusing).

The Data Gatherer accesses the data through the label IFASMF.MQOTEL.

Collecting data

Once Java has started (it takes about 15 seconds to start on my baby zD&T machine), it will listen for new records sent to the SMF resource (IFASMF.MQOTEL). It does not drain existing records.

You can send the data over TCP/IP or write it locally.

If you are sending the data over TCP/IP, once the first record has been read from SMF, the data collector starts a TCP/IP session to the remote collector. If the IP address is not active (or is misconfigured) it can take many seconds ( > 15 seconds for me) before the UnknownHostException is thrown.

Personally I would have connected at startup, so you know if you have a configuration error. It is not good when you start the server at midnight, but only find there is a problem at 0800 when the work starts. It would be better to report the error when the server is started, because it gives you more time to fix any problems.

If the connection is successful, there is no notification.

What is sent?

You can specify the option

 SET $SMFDUMP='true'  

and it dumps the data in //STDOUT

00000000 13 60 00 00 7E 7E 00 61 5A DC 01 26 17 7F E5 E2 .-..==..!...."VS
00000010 F0 F1 00 00 00 00 00 01 00 20 01 00 00 E2 E3 BE 01...........ST.
00000020 DD C1 37 4E 40 00 00 00 01 00 00 01 00 00 00 00 .A.+ ...........
00000030 00 00 00 00 04 86 00 00 00 00 00 40 00 00 00 02 .....f..... ....
00000040 00 01 0A 00 E2 D7 C1 D5 00 E2 E3 BE DD C0 C0 60 ....SPAN.ST..{{-
00000050 80 00 00 00 00 00 00 00 00 E2 E3 BE DD C0 CC 83 .........ST..{.c
00000060 80 00 00 00 00 00 00 00 F0 81 86 F7 F6 F5 F1 F9 ........0af76519
00000070 F1 F6 83 84 F4 F3 84 84 F8 F4 F4 F8 85 82 F2 F1 16cd43dd8448eb21
00000080 F1 83 F8 F0 F3 F1 F9 83 85 F2 85 F3 82 85 84 84 1c80319ce2e3bedd
00000090 83 F0 83 F0 F6 F0 F8 F0 85 F2 85 F3 82 85 84 84 c0c06080e2e3bedd
000000A0 83 F0 F1 86 F9 F7 83 F0 00 04 00 2F 00 18 0C 01 c01f97c0........
000000B0 A2 85 99 A5 89 83 85 4B 95 81 94 85 00 04 01 F4 service.name...4
000000C0 C3 E2 D8 F9 00 20 09 01 A2 97 81 95 4B 95 81 94 CSQ9....span.nam
000000D0 85 00 00 00 00 0B 01 F4 D4 D8 C7 C5 E3 40 C3 D6 e......4MQGET CO
000000E0 D3 C9 D5 00 00 28 18 01 94 85 A2 A2 81 87 89 95 LIN.....messagin
...

Once you have proved it is working – I suggest you set SMFDUMP=’false’.

TLS support

The Data Gatherer has support for TLS, but the backend I was using Jaeger does not have TLS support. The documentation says install the cassandra product; I could not install this on my Linux machine.

Debugging the JCL

I had various problems about configuration problems. I found specifying PARM=’/+I’

//JAVAJVM  EXEC PGM=JVMLDM&VERSION,REGION=&REGSIZE,PARM='/+I' 

showed what configuration parameters were used.

Debugging TLS

You can get a Java trace for TLS by specifying

IJO="$IJO  -Djavax.net.debug=all " 

Though you may not want to specify “all”. See here for more information on javax.net.debug

Data Gatherer Messages

Caused by: java.util.ServiceConfigurationError: TLS is enabled and ‘grb.otel.dt.tls.trusted.certs’ property must be set properly. at com.ibm.grb.service.startup.ConfigValidator.validateRunState(ConfigValidator.java:129)

A poor message. The problem was I had SET $OTLENDP=’http://10.1.0.2:4317′ instead of SET $OTLENDP=’https://10.1.0.2:4317′

Why doesn’t this valid C program compile?

I wanted to use some C code I found, and when I tried to compile the source, it kept complaining because of a syntax error.

... 
void printMD()
{
printf("ABC ");
int i;
i = 0;
printf("I is %i\n",i);
}
...

The original code was several thousand lines long. The (first) error message was

ERROR CCN3275 ./perfutic.c:4     Unexpected text 'int' encountered.                                        

There were two challanges

  1. Create the smallest program to isolate
  2. Find out why the code failed to comile

Isolate the problem

I put #ifdef temp…. #endif around blocks of code to remove irrelevant code. This worked, but I quickly got into a mess where I had many #ifdef…#endif and matching them up.

I saved a copy of the program, then used #ifdef..#endif to ignore blocks of code. If this made no change to the compile, then delete the block, and add more #ifdef…#endif. If it caused other error messages, remove the #ifdef..#endif statements, and try a smaller block of code.

Eventually I got down to the code above.

Why did the code fail to compile?

I spent half a day scratching my head, and when I came back next morning, I had a flash of inspiration.

Later versions of C are more flexible in some areas.

In early days of the C compiler you had to define all variables before you did any work. With later versions of the C compiler this restriction was relaxed, so you could call a function, the define a variable.

See intermingled declarations and code: variable declaration is no longer restricted to file scope or the start of a compound statement (block) in the ISO C99 specification

The makefile I was using was

cparms ="-Wc,SO,LIST(lst31),XREF,ILP32,DLL,SSCOM,SHOWINC,DEFINE(MVS=1)"  
cc -c -o $@ -I"//'MQCD94.SCSQC370'" -I'/usr/include' -I. $(cparms) $<

You control the level of C using the LANGLVL option.

The cc command produced (in the listing)

Language level. . . . . . . . : *COMMONC:NOTEXTAFTERENDIF 

With the c99 command, the code compiled, and the listing had

 Language level. . . . . . . . : *STDC99:NOTEXTAFTERENDIF 

With the original compile, I was using the options which did not allow variables to be defined after executable code.

Instead of using cc, I could have used cc with langlvl(EXTENDED).

Why doesn’t this valid C program compile?

The short answer is : I was using the wrong compiler options.

Whoosh goes my z/OS console

I was having problems IPLing z/OS. Once you can logon to TSO you can use SDSF to display the console. The problem is when the system fails before you can logon to TSO.

When z/OS is IPLed a few messages are displayed, then the console goes into wrap mode, and message roll past faster than I can read them.

You can change the roll mode using the command to not delete messages automatically

K S,DEL=N

You can then use

K

to clear the screen.

You can use K S,REF to display and overtype values. For example RTME=5 says roll every 5 seconds, RTME=1/4 says roll every quarter of a second.

How much free space do my ZFS have?

I was running into a problem where my file systems were running out of space. I knew I had some spare space – but where was it?

The command

zfsadm aggrinfo

produced output like

COLIN.ZFS.ZOWE.CONFIG (R/W COMP): 1943183 K free out of total 2975040      
COLIN.ZFS.ZOWE33.CONFIG (R/W COMP): 7327 K free out of total 421920
COLIN.OPENSSL.ZFS2 (R/W COMP): 2407159 K free out of total 7200000
ZFS.S0W1.SYSTEM (R/W COMP): 1263 K free out of total 1440
ZFS.Z31B.CNJ (R/O COMP): 80605 K free out of total 648000
ZFS.S0W1.WEB (R/W COMP): 9839 K free out of total 10080

You can sort it to give the largest space at the bottom.

  • -n means treat the value as a number
  • -k 4,4 means sort by the 4th field.
zfsadm aggrinfo |sort -n -k4,4

Gave

ZFS.Z31B.VERSION (R/W COMP): 33 K free out of total 3823200
ZFS.Z31B.ZEDC (R/O COMP): 367 K free out of total 1440
...
COLIN.OPENSSL.ZFS2 (R/W COMP): 2407159 K free out of total 7200000
COLIN.ZOPEN.ZFS (R/W COMP): 2446835 K free out of total 6144480

Formatting a spread sheet column so it spans multiple columns.

I have some data in two columns in Google sheets (also in Libre office Calc). How do I print it so it is two columns (of two columns) , down the first column, down the second column, and onto another page?

The easiest way for me was

  • Copy the column of data into the clip board.
  • Go to libre office writer
  • Paste the data
  • Top line of options, Format -> Page style ( or Shift+Alt+P)
  • Select the Columns option on the top line
  • Columns + (to make it two columns)
  • OK
  • Set a heading
    • Insert -> header and footer ( near the bottom)
    • Header – > all
  • Set a page number
    • Insert -> Page number ( 5th from the bottom)

Using an external function

My “simple” problem was to have one application, using functions from another application, and allow me to upgrade both, and continue working. I want to write some code for openssl to call my program. I’m changing my code frequently, and I do not want to have to recompile and rebind openssl every time I change my program. (The recompile takes about 3 hours on my baby machine).

It took me a couple of days to get working; I learned a lot.

The simple solution which does not really work

I have a suite of programs called mycode, and I want to use stuff from another package which I’ll call otherstuff.

I can simply bind mycode, and otherstuff together to resolve all of the functions entry points. It works, but if I upgrade mycode, or otherstuff, I will not pick up the latest versions, and the code which is in the final load module may be incompatible with the newer versions.

Use fetch!

In mycode I can use the C function fetch, to load a load module, and give me the entry point. I then call the functions using the entry point.

This works – after a fashion. The key word is load module. A load module exists in a PDS or a PDSE. A Program Object can be in Unix or in a PDS, so the fetch solution does not work with file files in Unix.

The code worked if the module was in a load module – which I didn’t want.

I could not use fetch to load a program object from Unix.

Putting the code in a load module

I used the binder to create a load module.

xlc  dummy.o-o "//'COLIN.LOAD(dummy)'"  ...

To get my program to run I had to use

EXPORT STEPLIB=COLIN.LOAD
./myprog

The fetch worked, and my program was called.

Use Dynamic Link Library

The Unix eqivilent to a load module is the DLL.

When you create a DLL, two parts are created.

  • The .x file which is used for C to find how to call a function (stub code)
  • The .so file which contains the executable code.

The .x file

The .x file contains data like

IMPORT DATA,'cpfopen','ascii_tab'
IMPORT CODE,'cpfopen','cpfopen1'
IMPORT CODE,'cpfopen','printHex'

Which says

  • there is an entry point cpfopen1 in program object cpfopen
  • there is an external data object called ascii_tab in program object cpfopen
  • there is an entry point printHex in program object cpfopen

This .x file contains information for C to create stub code to load the actual code.

My calling program has

#pragma linkage(cpfopen1 ,OS)
...
int myopen ;
myopen = cpfopen1("ABC","DEF");
...

This needs to be complied with option

-Wc,DLL  

If you do not use the DLL option it ignores the IMPORT statement, and reports cpfopen1 and printHex are not found.

I bound it using

xlc  -o fopen  myfile.o cpfopen1.x  V 

The executed code.

You need to compile your code and specify which function names you want to make visible to other programs(export).

You can use

  • the compiler option EXPORTALL or
  • #pragma export(function1) #pragma export(function2) within your code.

At bind time

l1="-Wl,DLL  " 
xlc cpfopen1.o -o cpfopen1 -V $l1 1>a

This creates

  • the executable with the name in the -o parameter (cpfopen1)
  • and cpfopen1.x using the name of the object in the -o parameter

With

l1="-Wl,DLL  " 
xlc cpfopen1.o -o cpfopen1.so -V $l1 1>a

the file cpfopen1.x has

IMPORT CODE,'cpfopen1.so','cpfopen1'

Doing it this way, and letting the C and the binder resolve the entry point is the easy way of doing it.

Advanced DLL

You can load a DLL yourself

void * hDLL; 
int (*fptr)(const char * filename, const char *mode);

hDLL = dlopen("cpfopen1", RTLD_LOCAL | RTLD_LAZY );
if (!hDLL) {
fprintf(stderr, "%s\n", dlerror());
exit(99);
}
dlerror(); /* Clear any existing error */

The find the entry point using dlsym. This uses the handle of the DLL object loaded above, and you pass the entry point name. (It might not exist).

Define a pointer to the function

int   (*fptr)(const char * filename, const char *mode); 

This defines a pointer to a function fptr, which returns an int, and has two char * parameters.

fptr = (int (*)(const char *, const char *)) dlsym(hDLL, "cpfopen1"); 

if ( fptr != 0)
{
myopen = (*fptr)("ABC","DEF");
printf("result %i\n",myopen);
}

It took me some time to get the definitions of the function parameters correct.

The called function is

#pragma linkage(cpfopen1, fetchable)
int cpfopen1(char * filename, char *mode)
{
printf(">>>>>>>>>>>>>>>In cpfopen1 %s %s\n",filename,mode);
return 7 ;
}

It was much easier to include the .x file at bind time, and let C run time sort it out.

Clean up

You should use dlclose(handle) to close the object and free its resources