Let’s ‘C’ a bit more PHP

Thought I would add a simple addition to the PHP process so that we can extract some information from the system such as the processor group, LPAR ID and license info.  This is something we use a lot for our Availability products to allow us to generate LPP keys so we thought it would be something others could gain benefit from.

The process will use a separate page to connect and gather the information as we have yet to provide a persistent connection that would allow us to retrieve information from a single connection (We have a plan to show how that can be done in a later post).  Also we have to add the code required to retrieve the information from the OS which will be run from the WORKER Program.

The first change is to add a new case statement into the worker program which will allow the request to be processed.
         case 4 :
            if(valid_user == 1) {
               if(handle_LR(accept_sd,a_e_ccsid,e_a_ccsid) != 1) {
                  sprintf(msg_dta,"Failed to retrieve license info");
                  len = strlen(msg_dta);
                  convert_buffer(msg_dta,convBuf,len,_32K,e_a_ccsid);
                  rc = send(accept_sd,convBuf,len,0);
                  }
               }
            else {
               sprintf(msg_dta,"Must be signed on to send request");
               len = strlen(msg_dta);
               convert_buffer(msg_dta,convBuf,len,_32K,e_a_ccsid);
               rc = send(accept_sd,convBuf,len,0);
               }
            break;    
Pretty simple and it follows the same requirements as others where it needs a valid sign on before it will attempt to process anything. Be aware that we have purposely left the security at the profile level, we are not adopting authority to allow the request to be made by anyone who does not have the authority to do so.

Next we have added a new service program function which will be called from the case statement (handle_LR) again it takes the same parameters as previous requests keeping it simple. Adding the new function has added the need for additional header files and type definitions so we have added these to the H/SRVFUNC file.
   #include <qlza.h>                        // License Key structs
   #include <qlzartv.h>                     // Retrieve License Key
   #include <qszrtvpr.h>                    // retrieve prd
   #include <qpmlpmgt.h>                    // LPAR/CPU info
   #include <qwcrsval.h>                    // retrieve system vals

   typedef _Packed struct  Sys_Value_Table  {
                        char System_Value[10];
                        char Type_Data;
                        char Information_Status;
                        int  Length_Data;
                        char Data[10];
                        } Sys_Value_Table_t;

   typedef _Packed struct  Dta_Rtd_x {
                        int  Number_Sys_Vals_Rtnd;
                        int  Offset_Sys_Val_Table;
                        Sys_Value_Table_t System_Value_t;
                        } Dta_Rtd_t;     

   int handle_LR(int,iconv_t,iconv_t); 

The code for the actual processing is added to the QCSRC/SVRFUNC file, we are only showing the code we have added as its starting to get quite a large file now, this code was added to the end of the source file.
// Function handle_LR()
// purpose: retrieve license info
// @parms
//      socket
//      conversion table a-e
//      conversion table e-a
// returns 1 on success

int handle_LR(int accept_sd,
              iconv_t a_e_ccsid,
              iconv_t e_a_ccsid) {
int lpar_id = 0;                            // lpar number
int data_format = 1;                        // format 1
int receiver_length = 0;                    // length of output
int len = 0;                                // length string
int rc = 0;                                 // return code
char convBuf[_1KB];                         // conversion buffer
char Msg_Buf[_1KB];                         // returned message
char json_str[500];                         // json string
char *tmp;                                  // temp ptr
Sys_Value_Table_t *data;                    // pointer to sysval data
Dta_Rtd_t sysval_dta;                       // returned sysval struct
Qlza_LICP0100_t Prd_Input;                  // product input struct
Qlza_LICR0200_t RI;                         // returned lic info
Qsz_PRDR0100_t PD;                          // product details
Qsz_Product_Info_Rec_t OS_Info;             // product info struct
dlpar_info_format1_t info;                  // returned lpar info struct
Os_EC_t Error_Code = {0};                   // Error Code

Error_Code.EC.Bytes_Provided = _ERR_REC;
memcpy(OS_Info.Product_Id,"*OPSYS ",7);
memcpy(OS_Info.Release_Level,"*CUR  ",6);
memcpy(OS_Info.Product_Option,"0000",4);
memcpy(OS_Info.Load_Id,"*CODE     ",10);
// retrieve the OS product details
QSZRTVPR(&PD,
         sizeof(PD),
         "PRDR0100",
         &OS_Info,
         &Error_Code);
if(Error_Code.EC.Bytes_Available > 0) {
   snd_error_msg(Error_Code);
   return -1;
   }
// data to return string
sprintf(json_str,"{PrdId : %.7s,RlsLvl : %.6s,PrdOpt : %.4s,LoadId : %.4s,LoadType : %.10s,MinRls : %.6s,",
        PD.Product_Id,PD.Release_Level,PD.Product_Option,PD.Load_Id,PD.Load_Type,PD.Min_Tgt_Rls);
strcpy(Msg_Buf,json_str);
// get the license info
memcpy(Prd_Input.PID,PD.Product_Id,7);
memcpy(Prd_Input.Rls,PD.Release_Level,6);
memcpy(Prd_Input.Feature,PD.Load_Id,4);
QLZARTV(&RI,
        sizeof(RI),
        "LICR0200",
        &Prd_Input,
        "LICP0100",
        &Error_Code);
if(Error_Code.EC.Bytes_Available > 0) {
   snd_error_msg(Error_Code);
   return -1;
   }
sprintf(json_str,"LicTerm : %.6s, LicRlsLvl : %.6s, PGroup : %.3s,",RI.Lic_Term,RI.Rls_Lvl,RI.Proc_Grp);
strcat(Msg_Buf,json_str);
// get the serial number
QWCRSVAL(&sysval_dta,
         sizeof(sysval_dta),
         1,
         "QSRLNBR   ",
         &Error_Code);
if(Error_Code.EC.Bytes_Available) {
   snd_error_msg(Error_Code);
   return -1;
   }
tmp = (char *)&sysval_dta;
tmp += sysval_dta.Offset_Sys_Val_Table;
data = (Sys_Value_Table_t *)tmp;
sprintf(json_str,"SrlNbr : %.8s,",data->Data);
strcat(Msg_Buf,json_str);
// get the processor feature
QWCRSVAL(&sysval_dta,
         sizeof(sysval_dta),
         1,
         "QPRCFEAT  ",
         &Error_Code);
if(Error_Code.EC.Bytes_Available) {
   snd_error_msg(Error_Code);
   }
tmp = (char *)&sysval_dta;
tmp += sysval_dta.Offset_Sys_Val_Table;
data = (Sys_Value_Table_t *)tmp;
sprintf(json_str,"PrcFeat : %.4s,",data->Data);
strcat(Msg_Buf,json_str);
// get the model number
QWCRSVAL(&sysval_dta,
         sizeof(sysval_dta),
         1,
         "QMODEL    ",
         &Error_Code);
if(Error_Code.EC.Bytes_Available) {
   snd_error_msg(Error_Code);
   }
tmp = (char *)&sysval_dta;
tmp += sysval_dta.Offset_Sys_Val_Table;
data = (Sys_Value_Table_t *)tmp;
sprintf(json_str,"QModel : %.4s,",data->Data);
strcat(Msg_Buf,json_str);
// get the lapr id
receiver_length = sizeof(info); rc = dlpar_get_info(&info,data_format,receiver_length); if(rc < 0) { sprintf(json_str,"LparId : 0}"); strcat(Msg_Buf,json_str); } else { // set lpar number sprintf(json_str,"LparId : %d}",info.lpar_number); strcat(Msg_Buf,json_str); } len = strlen(Msg_Buf); convert_buffer(Msg_Buf,convBuf,len,_1KB,e_a_ccsid); rc = send(accept_sd,convBuf,len,0); if(rc != len) { return -1; } return 1; }

The only other change is to the export list in the QSRVSRC/SRVFUNC member. Remember when adding changes to the service program exports they must be added at the end of the file. As this change is only affecting a single program we could have ran a RTVBNDSRC command and rebuilt the QSRVSRC/SRVFUNC member, but in this case we added a new line entry to the existing exports.
STRPGMEXP PGMLVL(*CURRENT)
/********************************************************************/
/*   *MODULE      SRVFUNC      OSLIB        06/25/18  09:25:25      */
/********************************************************************/
  EXPORT SYMBOL("Cvt_Hex_Buf")
  EXPORT SYMBOL("Crt_Q_Name")
  EXPORT SYMBOL("handle_MR")
  EXPORT SYMBOL("handle_CM")
  EXPORT SYMBOL("Handle_SO")
  EXPORT SYMBOL("convert_buffer")
  EXPORT SYMBOL("handle_LR")
ENDPGMEXP

Now you can compile the objects and plae them in the required library (OSPGM), the service program has to be deleted before it can be recreated. We have published the compiling code previously plus the QCLSRC/CRTOBJS program will do everything for you if needed.

Next we created the php code to allow it to be run from the browser, we created a new index_1.php page but you could call it anything you want as long as it has the .php extension.
<?php
// set up some variables to be used
$key = "0000 ";
$name = $_POST['userid'];
$pass = $_POST['pwd'];
$server = 'your server address here';
$port = 12345;
$bytes_read = 0;
$bytes_written = 0;
// get the host address
$address = gethostbyname($server);
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
    $_SESSION['ErrMsg'] = "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
    header("Location: /index.php");
    exit(0);
}
// connect to the remote host
$result = socket_connect($socket, $address, $port);
if ($result === false) {
    $_SESSION['ErrMsg'] = "socket_connect() failed.\nReason: ($result) " . socket_strerror(socket_last_error($socket)) . "\n";
    //header("Location: /index.php");
    exit(0);
}
// send the request to server
$bytes_written = socket_write($socket, $key, 5);
// the server now sends request for name 
$output = socket_read($socket, 2048, PHP_BINARY_READ);
// make sure we get what we expect
if ($output != "Please enter your Profile name : ") {
    $_SESSION['ErrMsg'] = "Incorrect data returned : " . $output;
    socket_close($socket);
    header("Location: /index.php");
    exit(0);
}
// we could check to make sure its the right info 
// now send the user name from above
$bytes_written = socket_write($socket, $name, strlen($name));
if ($bytes_written <= 0) {
    $_SESSION['ErrMsg'] = "Failed to write to socket : " . $bytes_written . " : " . $name;
    socket_close($socket);
    //header("Location: /index.php");
    exit(0);
}
// request for password
$output = socket_read($socket, 2048, PHP_BINARY_READ);
if ($output != "Please enter your Password : ") {
    $_SESSION['ErrMsg'] = "Incorrect data returned : " . $output;
    socket_close($socket);
    header("Location: /index.php");
    exit(0);
}
// send in the password
$bytes_written = socket_write($socket, $pass, strlen($pass));
if ($bytes_written <= 0) {
    $_SESSION['ErrMsg'] = "Failed to write to socket : " . $bytes_written . " : " . $pass;
    socket_close($socket);
    //header("Location: /index.php");
    exit(0);
}
// get the license info
$key = "0004 ";
$output = "";
socket_write($socket, $key, 5);
// get the returned info
$output = socket_read($socket, 2048, PHP_BINARY_READ);
echo($output ."<br />");
echo("<br />");
// send the sign off key
socket_write($socket,"0001 ");
// close the socket
socket_close($socket);
exit(0);

Once the page has been uploaded to your web server you can run the request and you should see something similar to the following being output to your browser.
Get License Info

Get License Info

Enter your user credentials and press the button, you should see something similar to the following output to your browser.

{PrdId : 5770SS1,RlsLvl : V7R3M0,PrdOpt : 0000,LoadId : 5050,LoadType : *CODE ,MinRls : V7R3M0,LicTerm : V7R3M0, LicRlsLvl : V7R3M0, PGroup : P05,SrlNbr : 218FFEW,PrcFeat : EPXK,QModel : 41A,LparId : 4}


As we mentioned above we are going to move on from this to develop a process which will allow persistent connections to be made. this is important because we need to be able to keep any number of processes open at a time each one linked to a specific profile. This will allow us to leave the connection open and reconnect to the same job with the same profile each time from a web server. At the moment we are disconnecting each time we request a page because the profile is set for the job each time it connects, having multiple connection requests at the same time benefits from persistent connections.

All of the updates will be pushed up to the GitHub repository for those who are interested.

Chris…