Examples of Accessing Shared Executable Libraries

Example 1: Updating a Character String Argument

This example uses the tmpnam routine in the shared library supplied by Solaris, libc.so, which is installed in the /usr/lib/sparcv9 directory. The tmpnam routine generates a unique filename that can be used safely as a temporary filename. The temporary filename is typically placed in the /var/tmp directory.
Here is the C prototype for this routine:
char * tmpnam(char *s);
The attribute table for this prototype would be the following:
routine tmpnam minarg=1 maxarg=1 returns=char255. module=libc;
arg 1 char output byaddr format=$cstr255;
The SAS source code would be the following:
x 'if [ ! -L ./libc ] ; then ln -s /usr/lib/sparcv9/libc.so.1 ./libc ; fi' ;
x 'setenv LD_LIBRARY_PATH .:/usr/lib/sparcv9:/usr/lib:/lib';

data _null_;
   length tempname $255 tname $255;
   retain tempname tname “ “;
   tname = modulec ('tmpnam', tempname);
   put tempname = ;
   put tname = ;
run;
The SAS log output would be the following:
Updating a Character String Argument
tempname=/var/tmp/aaaKraydG
tname=/var/tmp/aaaKraydG
The POSIX standard for the maximum number of characters in a pathname is defined in /usr/include/limits.h to be 255 characters, so this example uses 254 as the length of the generated filename (tempname) with one character reserved for the null terminator. The $CSTR255. informat ensures that the null terminator and all subsequent characters are replaced by trailing blanks when control returns to the DATA step.

Example 2: Passing Arguments by Value

This example calls the access routine that is supplied by most UNIX vendors. This particular access routine is in the Hewlett-Packard shared library, libc.sl, which is installed under the /usr/lib/pa20_64 directory.
Here is the C prototype for this routine:
int access(char *path, int amode);
The access routine checks the file that is referenced by the accessibility path according to the bit pattern contained in amode. You can use the following integer values for amode that correspond to the type of permission for which you are testing:
4   Read access
2   Write access
1   Execute (search) access
0   Check existence of file
A return value of 0 indicates a successful completion and the requested access is permitted. A return value of –1 indicates a failure and the requested access is not permitted.
Because the amode argument is a pass-by-value, this example includes the BYVALUE specification for arg 2 in the attribute table. If both arguments were a pass-by-value, one could use the CALLSEQ=BYVALUE attribute in the ROUTINE statement and it would not be necessary to specify the BYVALUE option in arg 2.
The attribute table would be the following:
routine access minarg=2 maxarg=2 returns=short module=libc;
arg 1 char input byaddr format=$cstr200.;
arg 2 num input byvalue format=ib4.;
The SAS source code would be the following:
x 'if [ ! -L ./libc ] ; then ln -s /usr/lib/pa20_64/libc.so ; fi' ;
x 'setenv LD_LIBRARY_PATH .:/usr/lib/pa20_64:/usr/lib:/lib' ;

data _null_;
  length path $200.;
  path='/dev';

  /* A non-root user is testing for write permission in the /dev directory */
  rc = modulen("*ie",'access',path,2);
  put rc = ;
run;
The SAS log output would be the following:
Log Output If Request Access Is Permitted
rc=-1
If you changed the SAS source code to check for a Write permission in the user's $HOME directory, the output would be different:
data _null_;
   length homedir $200.;
   homedir=sysget('HOME');
   
   /* A user is testing for write permissions in their $HOME directory */
   rc = modulen(“*ie”,'access',homedir,2);
   put rc = ;
run;
In this case, the SAS log output would be the following:
Log Output for Successful Completion (Access Permitted)
rc=0

Example 3: Using PEEKCLONG to Access a Returned Pointer

This example uses the strcat routine, which is part of the Red Hat Linux shared library libc-2.2.3.so. This library is typically installed in the /lib/i686 directory. This routine concatenates two strings and returns a pointer to the newly concatenated string.
Here is the C prototype for this routine:
char *strcat(char, *dest, const char *src);
The proper SASCBTBL attribute table would be the following:
routine strcat minarg=2 maxarg=2 returns=ulong module=libc;
arg 1 char input format=$cstr200.;
arg 2 char input format=$cstr200.;
The following example shows the SAS code:
filenamesascbtbl './sascbtbl.txt';

data _null_;
   file sascbtbl;
   put "routine strcat minarg=2 maxarg=2 returns=ulong module=libc;";
   put "arg 1 char input format=$cstr200.;";
   put "arg 2 char input format=$cstr200.;";
run;

data _null_;
   length string1 string2 newstring $200;
   length chptr $20;
   string1='This is string one and';
   string2=' this is string two.';
   chptr=modulec('strcat', string1, string2);
   newstring=peekclong(chptr,200);
   put newstring=;
run;
SAS writes the following output to the log:
Log Output for Using PEEKCLONG to Access a Returned Pointer
newstring=This is string one and this is string two.
The PEEKCLONG function was used here because the Red Hat Linux shared library /lib/i686/libc-2.2.3.so is a 32-bit library. The following output demonstrates this:
$pwd
/lib/i686

$file ./libc-2.2.3.so
libc-2.2.3.so:  ELF 32-bit LSB shared object, Intel 80386, version 1, not stripped
For more information about the PEEKLONG and PEEKCLONG functions, see PEEKLONG Function: UNIX and PEEKCLONG Function in SAS Functions and CALL Routines: Reference.

Example 4: Using Structures

Grouping SAS Variables as Structure Arguments describes how to use the FDSTART attribute to pass several arguments as one structure argument to a shared library routine. The passing of several arguments as one structure is another example of using structures with another routine in an external shared library.
The statvfs routine that is available under most UNIX operating systems retrieves file system information. This example uses the statvfs routine that is in the Solaris libc.so.1 shared library and typically installed in the /usr/lib/sparcv9 directory.
Here is the C prototype for this routine:
int statvfs(const char *path, struct statvfs *buf);
The statvfs routine will return a 0 if the routine completes successfully and –1 if there is a failure.
The statvfs structure is defined with the following members:
unsigned long f_bsize;          /* preferred file system block size */
unsigned long f_frsize;         /* fundamental file system block */
unsigned long f_blocks;         /* total number of blocks on file system in units */
unsigned long f_bfree;          /* total number of free blocks */
unsigned long f_bavail;         /* number of free blocks available to non-superuser */
unsigned long f_files;          /* total number of file nodes (inodes) */
unsigned long f_ffree;          /* total number of free file nodes */
unsigned long f_favail;         /* number of inodes available to non-superuser */
unsigned long f_fsid;           /* file system id (dev for now) */
char          f_basetype[16];   /* target fs type name, null-terminated */
unsigned long f_flag;           /* bit mask of flags */
unsigned long g f_namemax;      /* maximum filename length */
char          f_fstr[32];       /* file system specific string */ 
The SASCBTBL attribute table would be the following:
routine statvfs
   minarg=14
   maxarg=14
   returns=short
   module=libc;
arg 1  char input  byaddr         format=$char256.;
arg 2  num  output byaddr fdstart format=pib8.;
arg 3  num  output                format=pib8.;
arg 4  num  output                format=pib8.;
arg 5  num  output                format=pib8.;
arg 6  num  output                format=pib8.;
arg 7  num  output                format=pib8.;
arg 8  num  output                format=pib8.;
arg 9  num  output                format=pib8.;
arg 10 num  output                format=pib8.;
arg 11 char output                format=$cstr16.;
arg 12 num  output                format=pib8.;
arg 13 num  output                format=pib8.;
arg 14 char output                format=$cstr32.;
The SAS source code to call the statvfs routine from within the DATA step would be the following:
x 'if [ ! -L ./libc ]; then ln -s /usr/lib/sparcv9/libc.so.1 ./libc ; fi' ;
x 'setenv LD_LIBRARY_PATH .:/usr/lib/sparcv9:/usr/lib:/lib';

data _null_;
   length f_basetype $16. f_fstr $32.;
   retain f_bsize f_frsize f_blocks f_bfree f_bavail f_files f_ffree f_favail
          f_fsid f_flag f_namemax 0;
   retain f_basetype f_fstr ' ';
   rc=modulen ('statvfs' , '/tmp', f_bsize, f_frsize, f_blocks, f_bfree, f_bavail,
               f_files, f_ffree, f_favail, f_fsid, f_basetype, f_flag,
               f_namemax, f_fstr);
   put rc = ;
   put f_bsize = ;
   put f_frsize = ;
   put f_blocks = ;
   put f_bfree = ;
   put f_bavail = ;
   put f_files = ;
   put f_ffree = ;
   put f_favail = ;
   put f_fsid = ;
   put f_basetype = ;
   put f_flag = ;
   put f_namemax = ;
   /* Determining the total bytes available in the file system and then dividing the
    total number of bytes by the number of bytes in a gigabyte */
   gigsfree = ((f_bavail * f_bsize)/1073741824);
   put 'The total amount of space available in /tmp is 'gigsfree 4.2' Gigabytes.';
run;
The SAS log output would be the following:
Log Output for Using Structures
rc=0
f_bsize=8192
f_frsize=8192
f_blocks=196608
f_bfree=173020
f_bavail=173020
f_files=884732
f_ffree=877184
f_favail=877184
f_fsid=2
f_basetype=tmpfs
f_flag=4
f_namemax=255

The total amount of space available in /tmp is 1.32 Gigabytes.

Example 5: Invoking a Shared Library Routine

This example shows how to pass a matrix as an argument within PROC IML. The example creates a 4x5 matrix. Each cell is set to 10x+y+3, where x is the row number and y is the column number. For example, the cell at row 1 column 2 is set to (10*1)+2+3, or 15.
The example invokes several routines from the theoretical TRYMOD shared library. It uses the changd routine to add 100x+10y to each element, where x is the C row number (0 through 3) and y is the C column number (0 through 4). The first argument to changd specifies the extra amount to sum. The changdx routine works just like changd, except that it expects a transposed matrix. The changi routine works like changd except that it expects a matrix of integers. The changix routine works like changdx except that integers are expected.
Note: A maximum of three arguments can be sent when invoking a shared library routine from PROC IML.
In this example, all four matrices x1, x2, y1, and y2 should become set to the same values after their respective MODULEIN calls. Here are the attribute table entries:
routine changd module=trymod returns=long;
arg 1 input num format=rb8. byvalue;
arg 2 update num format=rb8.;
routine changdx module=trymod returns=long
   transpose=yes;
arg 1 input num format=rb8. byvalue;
arg 2 update num format=rb8.;
routine changi module=trymod returns=long;
arg 1 input num format=ib4. byvalue;
arg 2 update num format=ib4.;
routine changix module=trymod returns=long
   transpose=yes;
arg 1 input num format=ib4. byvalue;
arg 2 update num format=ib4.;
Here is the PROC IML step:
proc iml;
   x1 = J(4,5,0);
   do i=1 to 4;
      do j=1 to 5;
         x1[i,j] = i*10+j+3;
         end;
   end;
   y1= x1; x2 = x1; y2 = y1;
   rc = modulein('changd',6,x1);
   rc = modulein('changdx',6,x2);
   rc = modulein('changi',6,y1);
   rc = modulein('changix',6,y2);
   print x1 x2 y1 y2;
run;
The following are the results of the PRINT statement:
Invoking a Shared Library Routine from PROC IML
X1
 20        31        42        53        64
130       141       152       163       174
240       251       262       273       284
350       361       372       383       394
 X2
 20        31        42        53        64
130       141       152       163       174
240       251       262       273       284
350       361       372       383       394
 Y1
 20        31        42        53        64
130       141       152       163       174
240       251       262       273       284
350       361       372       383       394
 Y2
 20        31        42        53        64
130       141       152       163       174
240       251       262       273       284
350       361       372       383       394