Actual source code: globus.c

      1: #include <petscwebclient.h>
      2: PETSC_PRAGMA_DIAGNOSTIC_IGNORED_BEGIN("-Wdeprecated-declarations")

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

     10: static PetscErrorCode base64_encode(const unsigned char *data, unsigned char *encoded_data, size_t len)
     11: {
     12:   static size_t mod_table[] = {0, 2, 1};
     13:   size_t        i, j;
     14:   size_t        input_length, output_length;

     16:   PetscFunctionBegin;
     17:   PetscCall(PetscStrlen((const char *)data, &input_length));
     18:   output_length = 4 * ((input_length + 2) / 3);
     19:   PetscCheck(output_length <= len, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Output length not large enough");

     21:   for (i = 0, j = 0; i < input_length;) {
     22:     uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0;
     23:     uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0;
     24:     uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0;
     25:     uint32_t triple  = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;

     27:     encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
     28:     encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
     29:     encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
     30:     encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
     31:   }
     32:   encoded_data[j] = 0;
     33:   for (i = 0; i < mod_table[input_length % 3]; i++) encoded_data[output_length - 1 - i] = '=';
     34:   PetscFunctionReturn(PETSC_SUCCESS);
     35: }

     37: PETSC_UNUSED static PetscErrorCode base64_decode(const unsigned char *data, unsigned char *decoded_data, size_t length)
     38: {
     39:   static char decoding_table[257];
     40:   static int  decode_table_built = 0;
     41:   size_t      i, j;
     42:   size_t      input_length, output_length;

     44:   PetscFunctionBegin;
     45:   if (!decode_table_built) {
     46:     for (i = 0; i < 64; i++) decoding_table[(unsigned char)encoding_table[i]] = i;
     47:     decode_table_built = 1;
     48:   }

     50:   PetscCall(PetscStrlen((const char *)data, &input_length));
     51:   PetscCheck(input_length % 4 == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Input length must be divisible by 4");

     53:   output_length = input_length / 4 * 3;
     54:   if (data[input_length - 1] == '=') (output_length)--;
     55:   if (data[input_length - 2] == '=') (output_length)--;
     56:   PetscCheck(output_length <= length, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Output length too shore");

     58:   for (i = 0, j = 0; i < input_length;) {
     59:     uint32_t sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]];
     60:     uint32_t sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]];
     61:     uint32_t sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]];
     62:     uint32_t sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[(int)data[i++]];
     63:     uint32_t triple   = (sextet_a << 3 * 6) + (sextet_b << 2 * 6) + (sextet_c << 1 * 6) + (sextet_d << 0 * 6);

     65:     if (j < output_length) decoded_data[j++] = (triple >> 2 * 8) & 0xFF;
     66:     if (j < output_length) decoded_data[j++] = (triple >> 1 * 8) & 0xFF;
     67:     if (j < output_length) decoded_data[j++] = (triple >> 0 * 8) & 0xFF;
     68:   }
     69:   decoded_data[j] = 0;
     70:   PetscFunctionReturn(PETSC_SUCCESS);
     71: }

     73: #if defined(PETSC_HAVE_UNISTD_H)
     74:   #include <unistd.h>
     75: #endif

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

     80:   Not Collective, only the first process in `MPI_Comm` does anything

     82:   Input Parameters:
     83: + comm      - the MPI communicator
     84: - tokensize - size of the token array

     86:   Output Parameter:
     87: . access_token - can be used with `PetscGlobusUpLoad()` for 30 days

     89:   Level: intermediate

     91:   Notes:
     92:   This call requires `stdout` and `stdin` access from process 0 on the MPI communicator

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

     96: .seealso: `PetscGoogleDriveRefresh()`, `PetscGoogleDriveUpload()`, `PetscGlobusUpload()`
     97: @*/
     98: PetscErrorCode PetscGlobusAuthorize(MPI_Comm comm, char access_token[], size_t tokensize)
     99: {
    100:   SSL_CTX    *ctx;
    101:   SSL        *ssl;
    102:   int         sock;
    103:   char        buff[8 * 1024], *ptr, head[1024];
    104:   PetscMPIInt rank;
    105:   size_t      len;
    106:   PetscBool   found;

    108:   PetscFunctionBegin;
    109:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
    110:   if (rank == 0) {
    111:     PetscCheck(isatty(fileno(PETSC_STDOUT)), PETSC_COMM_SELF, PETSC_ERR_USER, "Requires users input/output");
    112:     PetscCall(PetscPrintf(comm, "Enter globus username:"));
    113:     ptr = fgets(buff, 1024, stdin);
    114:     PetscCheck(ptr, PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Error reading from stdin: %d", errno);
    115:     PetscCall(PetscStrlen(buff, &len));
    116:     buff[len - 1] = ':'; /* remove carriage return at end of line */

    118:     PetscCall(PetscPrintf(comm, "Enter globus password:"));
    119:     ptr = fgets(buff + len, 1024 - len, stdin);
    120:     PetscCheck(ptr, PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Error reading from stdin: %d", errno);
    121:     PetscCall(PetscStrlen(buff, &len));
    122:     buff[len - 1] = '\0'; /* remove carriage return at end of line */
    123:     PetscCall(PetscStrncpy(head, "Authorization: Basic ", sizeof(head)));
    124:     PetscCall(base64_encode((const unsigned char *)buff, (unsigned char *)(head + 21), sizeof(head) - 21));
    125:     PetscCall(PetscStrlcat(head, "\r\n", sizeof(head)));

    127:     PetscCall(PetscSSLInitializeContext(&ctx));
    128:     PetscCall(PetscHTTPSConnect("nexus.api.globusonline.org", 443, ctx, &sock, &ssl));
    129:     PetscCall(PetscHTTPSRequest("GET", "nexus.api.globusonline.org/goauth/token?grant_type=client_credentials", head, "application/x-www-form-urlencoded", NULL, ssl, buff, sizeof(buff)));
    130:     PetscCall(PetscSSLDestroyContext(ctx));
    131:     close(sock);

    133:     PetscCall(PetscPullJSONValue(buff, "access_token", access_token, tokensize, &found));
    134:     PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_LIB, "Globus did not return access token");

    136:     PetscCall(PetscPrintf(comm, "Here is your Globus access token, save it in a save place, in the future you can run PETSc\n"));
    137:     PetscCall(PetscPrintf(comm, "programs with the option -globus_access_token %s\n", access_token));
    138:     PetscCall(PetscPrintf(comm, "to access Globus automatically\n"));
    139:   }
    140:   PetscFunctionReturn(PETSC_SUCCESS);
    141: }

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

    146:   Not Collective, only the first process in `MPI_Comm` does anything

    148:   Input Parameters:
    149: + comm         - the MPI communicator
    150: . access_token - Globus access token, if `NULL` will check in options database for -globus_access_token XXX otherwise
    151:                   will call `PetscGlobusAuthorize()`.
    152: - buffsize     - size of the buffer

    154:   Output Parameter:
    155: . buff - location to put Globus information

    157:   Level: intermediate

    159: .seealso: `PetscGoogleDriveRefresh()`, `PetscGoogleDriveUpload()`, `PetscGlobusUpload()`, `PetscGlobusAuthorize()`
    160: @*/
    161: PetscErrorCode PetscGlobusGetTransfers(MPI_Comm comm, const char access_token[], char buff[], size_t buffsize)
    162: {
    163:   SSL_CTX    *ctx;
    164:   SSL        *ssl;
    165:   int         sock;
    166:   char        head[4096];
    167:   PetscMPIInt rank;

    169:   PetscFunctionBegin;
    170:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
    171:   if (rank == 0) {
    172:     PetscCall(PetscStrncpy(head, "Authorization : Globus-Goauthtoken ", sizeof(head)));
    173:     if (access_token) {
    174:       PetscCall(PetscStrlcat(head, access_token, sizeof(head)));
    175:     } else {
    176:       PetscBool set;
    177:       char      accesstoken[4096];
    178:       PetscCall(PetscOptionsGetString(NULL, NULL, "-globus_access_token", accesstoken, sizeof(accesstoken), &set));
    179:       PetscCheck(set, PETSC_COMM_SELF, PETSC_ERR_USER, "Pass in Globus accesstoken or use -globus_access_token XXX");
    180:       PetscCall(PetscStrlcat(head, accesstoken, sizeof(head)));
    181:     }
    182:     PetscCall(PetscStrlcat(head, "\r\n", sizeof(head)));

    184:     PetscCall(PetscSSLInitializeContext(&ctx));
    185:     PetscCall(PetscHTTPSConnect("transfer.api.globusonline.org", 443, ctx, &sock, &ssl));
    186:     PetscCall(PetscHTTPSRequest("GET", "transfer.api.globusonline.org/v0.10/tasksummary", head, "application/json", NULL, ssl, buff, buffsize));
    187:     PetscCall(PetscSSLDestroyContext(ctx));
    188:     close(sock);
    189:   }
    190:   PetscFunctionReturn(PETSC_SUCCESS);
    191: }

    193: /*@C
    194:   PetscGlobusUpload - Loads a file to Globus

    196:   Not Collective, only the first process in the `MPI_Comm` uploads the file

    198:   Input Parameters:
    199: + comm         - MPI communicator
    200: . access_token - obtained with `PetscGlobusAuthorize()`, pass `NULL` to use `-globus_access_token XXX` from the PETSc database
    201: - filename     - file to upload

    203:   Options Database Key:
    204: . -globus_access_token XXX - the Globus token

    206:   Level: intermediate

    208: .seealso: `PetscGoogleDriveAuthorize()`, `PetscGoogleDriveRefresh()`, `PetscGlobusAuthorize()`
    209: @*/
    210: PetscErrorCode PetscGlobusUpload(MPI_Comm comm, const char access_token[], const char filename[])
    211: {
    212:   SSL_CTX    *ctx;
    213:   SSL        *ssl;
    214:   int         sock;
    215:   char        head[4096], buff[8 * 1024], body[4096], submission_id[4096];
    216:   PetscMPIInt rank;
    217:   PetscBool   flg, found;

    219:   PetscFunctionBegin;
    220:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
    221:   if (rank == 0) {
    222:     PetscCall(PetscTestFile(filename, 'r', &flg));
    223:     PetscCheck(flg, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to find file: %s", filename);

    225:     PetscCall(PetscStrncpy(head, "Authorization : Globus-Goauthtoken ", sizeof(head)));
    226:     if (access_token) {
    227:       PetscCall(PetscStrlcat(head, access_token, sizeof(head)));
    228:     } else {
    229:       PetscBool set;
    230:       char      accesstoken[4096];
    231:       PetscCall(PetscOptionsGetString(NULL, NULL, "-globus_access_token", accesstoken, sizeof(accesstoken), &set));
    232:       PetscCheck(set, PETSC_COMM_SELF, PETSC_ERR_USER, "Pass in Globus accesstoken or use -globus_access_token XXX");
    233:       PetscCall(PetscStrlcat(head, accesstoken, sizeof(head)));
    234:     }
    235:     PetscCall(PetscStrlcat(head, "\r\n", sizeof(head)));

    237:     /* Get Globus submission id */
    238:     PetscCall(PetscSSLInitializeContext(&ctx));
    239:     PetscCall(PetscHTTPSConnect("transfer.api.globusonline.org", 443, ctx, &sock, &ssl));
    240:     PetscCall(PetscHTTPSRequest("GET", "transfer.api.globusonline.org/v0.10/submission_id", head, "application/json", NULL, ssl, buff, sizeof(buff)));
    241:     PetscCall(PetscSSLDestroyContext(ctx));
    242:     close(sock);
    243:     PetscCall(PetscPullJSONValue(buff, "value", submission_id, sizeof(submission_id), &found));
    244:     PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_LIB, "Globus did not return submission id");

    246:     /* build JSON body of transfer request */
    247:     PetscCall(PetscStrncpy(body, "{", sizeof(body)));
    248:     PetscCall(PetscPushJSONValue(body, "submission_id", submission_id, sizeof(body)));
    249:     PetscCall(PetscStrlcat(body, ",", sizeof(body)));
    250:     PetscCall(PetscPushJSONValue(body, "DATA_TYPE", "transfer", sizeof(body)));
    251:     PetscCall(PetscStrlcat(body, ",", sizeof(body)));
    252:     PetscCall(PetscPushJSONValue(body, "sync_level", "null", sizeof(body)));
    253:     PetscCall(PetscStrlcat(body, ",", sizeof(body)));
    254:     PetscCall(PetscPushJSONValue(body, "source_endpoint", "barryfsmith#MacBookPro", sizeof(body)));
    255:     PetscCall(PetscStrlcat(body, ",", sizeof(body)));
    256:     PetscCall(PetscPushJSONValue(body, "label", "PETSc transfer label", sizeof(body)));
    257:     PetscCall(PetscStrlcat(body, ",", sizeof(body)));
    258:     PetscCall(PetscPushJSONValue(body, "length", "1", sizeof(body)));
    259:     PetscCall(PetscStrlcat(body, ",", sizeof(body)));
    260:     PetscCall(PetscPushJSONValue(body, "destination_endpoint", "mcs#home", sizeof(body)));
    261:     PetscCall(PetscStrlcat(body, ",", sizeof(body)));

    263:     PetscCall(PetscStrlcat(body, "\"DATA\": [ {", sizeof(body)));
    264:     PetscCall(PetscPushJSONValue(body, "source_path", "/~/FEM_GPU.pdf", sizeof(body)));
    265:     PetscCall(PetscStrlcat(body, ",", sizeof(body)));
    266:     PetscCall(PetscPushJSONValue(body, "destination_path", "/~/FEM_GPU.pdf", sizeof(body)));
    267:     PetscCall(PetscStrlcat(body, ",", sizeof(body)));
    268:     PetscCall(PetscPushJSONValue(body, "verify_size", "null", sizeof(body)));
    269:     PetscCall(PetscStrlcat(body, ",", sizeof(body)));
    270:     PetscCall(PetscPushJSONValue(body, "recursive", "false", sizeof(body)));
    271:     PetscCall(PetscStrlcat(body, ",", sizeof(body)));
    272:     PetscCall(PetscPushJSONValue(body, "DATA_TYPE", "transfer_item", sizeof(body)));
    273:     PetscCall(PetscStrlcat(body, "} ] }", sizeof(body)));

    275:     PetscCall(PetscSSLInitializeContext(&ctx));
    276:     PetscCall(PetscHTTPSConnect("transfer.api.globusonline.org", 443, ctx, &sock, &ssl));
    277:     PetscCall(PetscHTTPSRequest("POST", "transfer.api.globusonline.org/v0.10/transfer", head, "application/json", body, ssl, buff, sizeof(buff)));
    278:     PetscCall(PetscSSLDestroyContext(ctx));
    279:     close(sock);
    280:     PetscCall(PetscPullJSONValue(buff, "code", submission_id, sizeof(submission_id), &found));
    281:     PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_LIB, "Globus did not return code on transfer");
    282:     PetscCall(PetscStrcmp(submission_id, "Accepted", &found));
    283:     PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_LIB, "Globus did not accept transfer");
    284:   }
    285:   PetscFunctionReturn(PETSC_SUCCESS);
    286: }