Actual source code: globus.c

      1: #include <petscwebclient.h>
      2: #pragma clang diagnostic ignored "-Wdeprecated-declarations"
      3: #pragma gcc diagnostic ignored "-Wdeprecated-declarations"

      5: /*
      6:     Encodes and decodes from MIME Base64
      7: */
      8: static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
      9:                                 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
     10:                                 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
     11:                                 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
     12:                                 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
     13:                                 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
     14:                                 'w', 'x', 'y', 'z', '0', '1', '2', '3',
     15:                                 '4', '5', '6', '7', '8', '9', '+', '/'};

     17: static PetscErrorCode base64_encode(const unsigned char *data,unsigned char *encoded_data,size_t len)
     18: {
     19:   static size_t  mod_table[] = {0, 2, 1};
     20:   size_t         i,j;
     21:   size_t         input_length,output_length;

     25:   PetscStrlen((const char*)data,&input_length);
     26:   output_length = 4 * ((input_length + 2) / 3);
     27:   if (output_length > len) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Output length not large enough");

     29:   for (i = 0, j = 0; i < input_length;) {
     30:      uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0;
     31:      uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0;
     32:      uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0;
     33:      uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;

     35:      encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
     36:      encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
     37:      encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
     38:      encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
     39:   }
     40:   encoded_data[j] = 0;
     41:   for (i = 0; i < mod_table[input_length % 3]; i++) encoded_data[output_length - 1 - i] = '=';
     42:   return(0);
     43: }

     45: PETSC_UNUSED static PetscErrorCode base64_decode(const unsigned char *data,unsigned char* decoded_data, size_t length)
     46: {
     47:   static char    decoding_table[257];
     48:   static int     decode_table_built = 0;
     49:   size_t         i,j;
     51:   size_t         input_length,output_length;

     54:   if (!decode_table_built) {
     55:     for (i = 0; i < 64; i++) decoding_table[(unsigned char) encoding_table[i]] = i;
     56:     decode_table_built = 1;
     57:   }

     59:   PetscStrlen((const char*)data,&input_length);
     60:   if (input_length % 4 != 0) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Input length must be divisible by 4");

     62:   output_length = input_length / 4 * 3;
     63:   if (data[input_length - 1] == '=') (output_length)--;
     64:   if (data[input_length - 2] == '=') (output_length)--;
     65:   if (output_length > length) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Output length too shore");

     67:   for (i = 0, j = 0; i < input_length;) {
     68:     uint32_t sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]];
     69:     uint32_t sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]];
     70:     uint32_t sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]];
     71:     uint32_t sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]];
     72:     uint32_t triple = (sextet_a << 3 * 6) + (sextet_b << 2 * 6) + (sextet_c << 1 * 6) + (sextet_d << 0 * 6);

     74:     if (j < output_length) decoded_data[j++] = (triple >> 2 * 8) & 0xFF;
     75:     if (j < output_length) decoded_data[j++] = (triple >> 1 * 8) & 0xFF;
     76:     if (j < output_length) decoded_data[j++] = (triple >> 0 * 8) & 0xFF;
     77:   }
     78:   decoded_data[j] = 0;
     79:   return(0);
     80: }

     82: #if defined(PETSC_HAVE_UNISTD_H)
     83: #include <unistd.h>
     84: #endif

     86: /*@C
     87:      PetscGlobusAuthorize - Get an access token allowing PETSc applications to make Globus file transfer requests

     89:    Not collective, only the first process in MPI_Comm does anything

     91:    Input Parameters:
     92: +  comm - the MPI communicator
     93: -  tokensize - size of the token array

     95:    Output Parameters:
     96: .  access_token - can be used with PetscGlobusUpLoad() for 30 days

     98:    Notes:
     99:     This call requires stdout and stdin access from process 0 on the MPI communicator

    101:    You can run src/sys/webclient/tutorials/globusobtainaccesstoken to get an access token

    103:    Level: intermediate

    105: .seealso: PetscGoogleDriveRefresh(), PetscGoogleDriveUpload(), PetscURLShorten(), PetscGlobusUpload()

    107: @*/
    108: PetscErrorCode PetscGlobusAuthorize(MPI_Comm comm,char access_token[],size_t tokensize)
    109: {
    110:   SSL_CTX        *ctx;
    111:   SSL            *ssl;
    112:   int            sock;
    114:   char           buff[8*1024],*ptr,head[1024];
    115:   PetscMPIInt    rank;
    116:   size_t         len;
    117:   PetscBool      found;

    120:   MPI_Comm_rank(comm,&rank);
    121:   if (!rank) {
    122:     if (!isatty(fileno(PETSC_STDOUT))) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_USER,"Requires users input/output");
    123:     PetscPrintf(comm,"Enter globus username:");
    124:     ptr  = fgets(buff, 1024, stdin);
    125:     if (!ptr) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Error reading from stdin: %d", errno);
    126:     PetscStrlen(buff,&len);
    127:     buff[len-1] = ':'; /* remove carriage return at end of line */

    129:     PetscPrintf(comm,"Enter globus password:");
    130:     ptr  = fgets(buff+len, 1024-len, stdin);
    131:     if (!ptr) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Error reading from stdin: %d", errno);
    132:     PetscStrlen(buff,&len);
    133:     buff[len-1] = '\0'; /* remove carriage return at end of line */
    134:     PetscStrcpy(head,"Authorization: Basic ");
    135:     base64_encode((const unsigned char*)buff,(unsigned char*)(head+21),sizeof(head)-21);
    136:     PetscStrcat(head,"\r\n");

    138:     PetscSSLInitializeContext(&ctx);
    139:     PetscHTTPSConnect("nexus.api.globusonline.org",443,ctx,&sock,&ssl);
    140:     PetscHTTPSRequest("GET","nexus.api.globusonline.org/goauth/token?grant_type=client_credentials",head,"application/x-www-form-urlencoded",NULL,ssl,buff,sizeof(buff));
    141:     PetscSSLDestroyContext(ctx);
    142:     close(sock);

    144:     PetscPullJSONValue(buff,"access_token",access_token,tokensize,&found);
    145:     if (!found) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Globus did not return access token");

    147:     PetscPrintf(comm,"Here is your Globus access token, save it in a save place, in the future you can run PETSc\n");
    148:     PetscPrintf(comm,"programs with the option -globus_access_token %s\n",access_token);
    149:     PetscPrintf(comm,"to access Globus automatically\n");
    150:   }
    151:   return(0);
    152: }

    155: /*@C
    156:      PetscGlobusGetTransfers - Get a record of current transfers requested from Globus

    158:    Not collective, only the first process in MPI_Comm does anything

    160:    Input Parameters:
    161: +  comm - the MPI communicator
    162: .  access_token - Globus access token, if NULL will check in options database for -globus_access_token XXX otherwise
    163:                   will call PetscGlobusAuthorize().
    164: -  buffsize - size of the buffer

    166:    Output Parameters:
    167: .  buff - location to put Globus information

    169:    Level: intermediate

    171: .seealso: PetscGoogleDriveRefresh(), PetscGoogleDriveUpload(), PetscURLShorten(), PetscGlobusUpload(), PetscGlobusAuthorize()

    173: @*/
    174: PetscErrorCode PetscGlobusGetTransfers(MPI_Comm comm,const char access_token[],char buff[],size_t buffsize)
    175: {
    176:   SSL_CTX        *ctx;
    177:   SSL            *ssl;
    178:   int            sock;
    180:   char           head[4096];
    181:   PetscMPIInt    rank;

    184:   MPI_Comm_rank(comm,&rank);
    185:   if (!rank) {
    186:     PetscStrcpy(head,"Authorization : Globus-Goauthtoken ");
    187:     if (access_token) {
    188:       PetscStrcat(head,access_token);
    189:     } else {
    190:       PetscBool set;
    191:       char      accesstoken[4096];
    192:       PetscOptionsGetString(NULL,NULL,"-globus_access_token",accesstoken,sizeof(accesstoken),&set);
    193:       if (!set) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_USER,"Pass in Globus accesstoken or use -globus_access_token XXX");
    194:       PetscStrcat(head,accesstoken);
    195:     }
    196:     PetscStrcat(head,"\r\n");

    198:     PetscSSLInitializeContext(&ctx);
    199:     PetscHTTPSConnect("transfer.api.globusonline.org",443,ctx,&sock,&ssl);
    200:     PetscHTTPSRequest("GET","transfer.api.globusonline.org/v0.10/tasksummary",head,"application/json",NULL,ssl,buff,buffsize);
    201:     PetscSSLDestroyContext(ctx);
    202:     close(sock);
    203:   }
    204:   return(0);
    205: }

    207: /*@C
    208:      PetscGlobusUpload - Loads a file to Globus

    210:      Not collective, only the first process in the MPI_Comm uploads the file

    212:   Input Parameters:
    213: +   comm - MPI communicator
    214: .   access_token - obtained with PetscGlobusAuthorize(), pass NULL to use -globus_access_token XXX from the PETSc database
    215: -   filename - file to upload

    217:   Options Database:
    218: .  -globus_access_token XXX - the Globus token

    220:    Level: intermediate

    222: .seealso: PetscURLShorten(), PetscGoogleDriveAuthorize(), PetscGoogleDriveRefresh(), PetscGlobusAuthorize()

    224: @*/
    225: PetscErrorCode PetscGlobusUpload(MPI_Comm comm,const char access_token[],const char filename[])
    226: {
    227:   SSL_CTX        *ctx;
    228:   SSL            *ssl;
    229:   int            sock;
    231:   char           head[4096],buff[8*1024],body[4096],submission_id[4096];
    232:   PetscMPIInt    rank;
    233:   PetscBool      flg,found;

    236:   MPI_Comm_rank(comm,&rank);
    237:   if (!rank) {
    238:     PetscTestFile(filename,'r',&flg);
    239:     if (!flg) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to find file: %s",filename);

    241:     PetscStrcpy(head,"Authorization : Globus-Goauthtoken ");
    242:     if (access_token) {
    243:       PetscStrcat(head,access_token);
    244:     } else {
    245:       PetscBool set;
    246:       char      accesstoken[4096];
    247:       PetscOptionsGetString(NULL,NULL,"-globus_access_token",accesstoken,sizeof(accesstoken),&set);
    248:       if (!set) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_USER,"Pass in Globus accesstoken or use -globus_access_token XXX");
    249:       PetscStrcat(head,accesstoken);
    250:     }
    251:     PetscStrcat(head,"\r\n");

    253:     /* Get Globus submission id */
    254:     PetscSSLInitializeContext(&ctx);
    255:     PetscHTTPSConnect("transfer.api.globusonline.org",443,ctx,&sock,&ssl);
    256:     PetscHTTPSRequest("GET","transfer.api.globusonline.org/v0.10/submission_id",head,"application/json",NULL,ssl,buff,sizeof(buff));
    257:     PetscSSLDestroyContext(ctx);
    258:     close(sock);
    259:     PetscPullJSONValue(buff,"value",submission_id,sizeof(submission_id),&found);
    260:     if (!found) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Globus did not return submission id");

    262:     /* build JSON body of transfer request */
    263:     PetscStrcpy(body,"{");
    264:     PetscPushJSONValue(body,"submission_id",submission_id,sizeof(body));                 PetscStrcat(body,",");
    265:     PetscPushJSONValue(body,"DATA_TYPE","transfer",sizeof(body));                        PetscStrcat(body,",");
    266:     PetscPushJSONValue(body,"sync_level","null",sizeof(body));                           PetscStrcat(body,",");
    267:     PetscPushJSONValue(body,"source_endpoint","barryfsmith#MacBookPro",sizeof(body));    PetscStrcat(body,",");
    268:     PetscPushJSONValue(body,"label","PETSc transfer label",sizeof(body));                PetscStrcat(body,",");
    269:     PetscPushJSONValue(body,"length","1",sizeof(body));                                  PetscStrcat(body,",");
    270:     PetscPushJSONValue(body,"destination_endpoint","mcs#home",sizeof(body));             PetscStrcat(body,",");

    272:     PetscStrcat(body,"\"DATA\": [ {");
    273:     PetscPushJSONValue(body,"source_path","/~/FEM_GPU.pdf",sizeof(body));                PetscStrcat(body,",");
    274:     PetscPushJSONValue(body,"destination_path","/~/FEM_GPU.pdf",sizeof(body));           PetscStrcat(body,",");
    275:     PetscPushJSONValue(body,"verify_size","null",sizeof(body));                          PetscStrcat(body,",");
    276:     PetscPushJSONValue(body,"recursive","false",sizeof(body));                           PetscStrcat(body,",");
    277:     PetscPushJSONValue(body,"DATA_TYPE","transfer_item",sizeof(body));
    278:     PetscStrcat(body,"} ] }");

    280:     PetscSSLInitializeContext(&ctx);
    281:     PetscHTTPSConnect("transfer.api.globusonline.org",443,ctx,&sock,&ssl);
    282:     PetscHTTPSRequest("POST","transfer.api.globusonline.org/v0.10/transfer",head,"application/json",body,ssl,buff,sizeof(buff));
    283:     PetscSSLDestroyContext(ctx);
    284:     close(sock);
    285:     PetscPullJSONValue(buff,"code",submission_id,sizeof(submission_id),&found);
    286:     if (!found) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Globus did not return code on transfer");
    287:     PetscStrcmp(submission_id,"Accepted",&found);
    288:     if (!found) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Globus did not accept transfer");
    289:   }
    290:   return(0);
    291: }