xref: /petsc/src/sys/webclient/globus.c (revision e6e75211d226c622f451867f53ce5d558649ff4f)
1 #include <petscwebclient.h>
2 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
3 #pragma gcc diagnostic ignored "-Wdeprecated-declarations"
4 
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', '+', '/'};
16 
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;
22   PetscErrorCode ierr;
23 
24   PetscFunctionBegin;
25   ierr = PetscStrlen((const char*)data,&input_length);CHKERRQ(ierr);
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");
28 
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;
34 
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   PetscFunctionReturn(0);
43 }
44 
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;
50   PetscErrorCode ierr;
51   size_t         input_length,output_length;
52 
53   PetscFunctionBegin;
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   }
58 
59   ierr = PetscStrlen((const char*)data,&input_length);CHKERRQ(ierr);
60   if (input_length % 4 != 0) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Input length must be divisible by 4");
61 
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");
66 
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);
73 
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   PetscFunctionReturn(0);
80 }
81 
82 #if defined(PETSC_HAVE_UNISTD_H)
83 #include <unistd.h>
84 #endif
85 
86 #undef __FUNCT__
87 #define __FUNCT__ "PetscGlobusAuthorize"
88 /*@C
89      PetscGlobusAuthorize - Get an access token allowing PETSc applications to make Globus file transfer requests
90 
91    Not collective, only the first process in MPI_Comm does anything
92 
93    Input Parameters:
94 +  comm - the MPI communicator
95 -  tokensize - size of the token array
96 
97    Output Parameters:
98 .  access_token - can be used with PetscGlobusUpLoad() for 30 days
99 
100    Notes: This call requires stdout and stdin access from process 0 on the MPI communicator
101 
102    You can run src/sys/webclient/examples/tutorials/globusobtainaccesstoken to get an access token
103 
104    Level: intermediate
105 
106 .seealso: PetscGoogleDriveRefresh(), PetscGoogleDriveUpload(), PetscURLShorten(), PetscGlobusUpload()
107 
108 @*/
109 PetscErrorCode PetscGlobusAuthorize(MPI_Comm comm,char access_token[],size_t tokensize)
110 {
111   SSL_CTX        *ctx;
112   SSL            *ssl;
113   int            sock;
114   PetscErrorCode ierr;
115   char           buff[8*1024],*ptr,head[1024];
116   PetscMPIInt    rank;
117   size_t         len;
118   PetscBool      found;
119 
120   PetscFunctionBegin;
121   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
122   if (!rank) {
123     if (!isatty(fileno(PETSC_STDOUT))) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_USER,"Requires users input/output");
124     ierr = PetscPrintf(comm,"Enter globus username:");CHKERRQ(ierr);
125     ptr  = fgets(buff, 1024, stdin);
126     if (!ptr) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Error reading from stdin: %d", errno);
127     ierr = PetscStrlen(buff,&len);CHKERRQ(ierr);
128     buff[len-1] = ':'; /* remove carriage return at end of line */
129 
130     ierr = PetscPrintf(comm,"Enter globus password:");CHKERRQ(ierr);
131     ptr  = fgets(buff+len, 1024-len, stdin);
132     if (!ptr) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Error reading from stdin: %d", errno);
133     ierr = PetscStrlen(buff,&len);CHKERRQ(ierr);
134     buff[len-1] = '\0'; /* remove carriage return at end of line */
135     ierr = PetscStrcpy(head,"Authorization: Basic ");CHKERRQ(ierr);
136     ierr = base64_encode((const unsigned char*)buff,(unsigned char*)(head+21),sizeof(head)-21);CHKERRQ(ierr);
137     ierr = PetscStrcat(head,"\r\n");CHKERRQ(ierr);
138 
139     ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr);
140     ierr = PetscHTTPSConnect("nexus.api.globusonline.org",443,ctx,&sock,&ssl);CHKERRQ(ierr);
141     ierr = PetscHTTPSRequest("GET","nexus.api.globusonline.org/goauth/token?grant_type=client_credentials",head,"application/x-www-form-urlencoded",NULL,ssl,buff,sizeof(buff));CHKERRQ(ierr);
142     ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr);
143     close(sock);
144 
145     ierr   = PetscPullJSONValue(buff,"access_token",access_token,tokensize,&found);CHKERRQ(ierr);
146     if (!found) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Globus did not return access token");
147 
148     ierr = PetscPrintf(comm,"Here is your Globus access token, save it in a save place, in the future you can run PETSc\n");CHKERRQ(ierr);
149     ierr = PetscPrintf(comm,"programs with the option -globus_access_token %s\n",access_token);CHKERRQ(ierr);
150     ierr = PetscPrintf(comm,"to access Globus automatically\n");CHKERRQ(ierr);
151   }
152   PetscFunctionReturn(0);
153 }
154 
155 
156 #undef __FUNCT__
157 #define __FUNCT__ "PetscGlobusGetTransfers"
158 /*@C
159      PetscGlobusGetTransfers - Get a record of current transfers requested from Globus
160 
161    Not collective, only the first process in MPI_Comm does anything
162 
163    Input Parameters:
164 +  comm - the MPI communicator
165 .  access_token - Globus access token, if NULL will check in options database for -globus_access_token XXX otherwise
166                   will call PetscGlobusAuthorize().
167 -  buffsize - size of the buffer
168 
169    Output Parameters:
170 .  buff - location to put Globus information
171 
172    Level: intermediate
173 
174 .seealso: PetscGoogleDriveRefresh(), PetscGoogleDriveUpload(), PetscURLShorten(), PetscGlobusUpload(), PetscGlobusAuthorize()
175 
176 @*/
177 PetscErrorCode PetscGlobusGetTransfers(MPI_Comm comm,const char access_token[],char buff[],size_t buffsize)
178 {
179   SSL_CTX        *ctx;
180   SSL            *ssl;
181   int            sock;
182   PetscErrorCode ierr;
183   char           head[4096];
184   PetscMPIInt    rank;
185 
186   PetscFunctionBegin;
187   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
188   if (!rank) {
189     ierr = PetscStrcpy(head,"Authorization : Globus-Goauthtoken ");CHKERRQ(ierr);
190     if (access_token) {
191       ierr = PetscStrcat(head,access_token);CHKERRQ(ierr);
192     } else {
193       PetscBool set;
194       char      accesstoken[4096];
195       ierr = PetscOptionsGetString(NULL,"-globus_access_token",accesstoken,sizeof(accesstoken),&set);CHKERRQ(ierr);
196       if (!set) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_USER,"Pass in Globus accesstoken or use -globus_access_token XXX");
197       ierr = PetscStrcat(head,accesstoken);CHKERRQ(ierr);
198     }
199     ierr = PetscStrcat(head,"\r\n");CHKERRQ(ierr);
200 
201     ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr);
202     ierr = PetscHTTPSConnect("transfer.api.globusonline.org",443,ctx,&sock,&ssl);CHKERRQ(ierr);
203     ierr = PetscHTTPSRequest("GET","transfer.api.globusonline.org/v0.10/tasksummary",head,"application/json",NULL,ssl,buff,buffsize);CHKERRQ(ierr);
204     ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr);
205     close(sock);
206   }
207   PetscFunctionReturn(0);
208 }
209 
210 #undef __FUNCT__
211 #define __FUNCT__ "PetscGlobusUpload"
212 /*@C
213      PetscGlobusUpload - Loads a file to Globus
214 
215      Not collective, only the first process in the MPI_Comm uploads the file
216 
217   Input Parameters:
218 +   comm - MPI communicator
219 .   access_token - obtained with PetscGlobusAuthorize(), pass NULL to use -globus_access_token XXX from the PETSc database
220 -   filename - file to upload
221 
222   Options Database:
223 .  -globus_access_token   XXX
224 
225    Level: intermediate
226 
227 .seealso: PetscURLShorten(), PetscGoogleDriveAuthorize(), PetscGoogleDriveRefresh(), PetscGlobusAuthorize()
228 
229 @*/
230 PetscErrorCode PetscGlobusUpload(MPI_Comm comm,const char access_token[],const char filename[])
231 {
232   SSL_CTX        *ctx;
233   SSL            *ssl;
234   int            sock;
235   PetscErrorCode ierr;
236   char           head[4096],buff[8*1024],body[4096],submission_id[4096];
237   PetscMPIInt    rank;
238   PetscBool      flg,found;
239 
240   PetscFunctionBegin;
241   ierr = MPI_Comm_rank(comm,&rank);CHKERRQ(ierr);
242   if (!rank) {
243     ierr = PetscTestFile(filename,'r',&flg);CHKERRQ(ierr);
244     if (!flg) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to find file: %s",filename);
245 
246     ierr = PetscStrcpy(head,"Authorization : Globus-Goauthtoken ");CHKERRQ(ierr);
247     if (access_token) {
248       ierr = PetscStrcat(head,access_token);CHKERRQ(ierr);
249     } else {
250       PetscBool set;
251       char      accesstoken[4096];
252       ierr = PetscOptionsGetString(NULL,"-globus_access_token",accesstoken,sizeof(accesstoken),&set);CHKERRQ(ierr);
253       if (!set) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_USER,"Pass in Globus accesstoken or use -globus_access_token XXX");
254       ierr = PetscStrcat(head,accesstoken);CHKERRQ(ierr);
255     }
256     ierr = PetscStrcat(head,"\r\n");CHKERRQ(ierr);
257 
258     /* Get Globus submission id */
259     ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr);
260     ierr = PetscHTTPSConnect("transfer.api.globusonline.org",443,ctx,&sock,&ssl);CHKERRQ(ierr);
261     ierr = PetscHTTPSRequest("GET","transfer.api.globusonline.org/v0.10/submission_id",head,"application/json",NULL,ssl,buff,sizeof(buff));CHKERRQ(ierr);
262     ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr);
263     close(sock);
264     ierr   = PetscPullJSONValue(buff,"value",submission_id,sizeof(submission_id),&found);CHKERRQ(ierr);
265     if (!found) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Globus did not return submission id");
266 
267     /* build JSON body of transfer request */
268     ierr = PetscStrcpy(body,"{");CHKERRQ(ierr);
269     ierr = PetscPushJSONValue(body,"submission_id",submission_id,sizeof(body));CHKERRQ(ierr);                 ierr = PetscStrcat(body,",");CHKERRQ(ierr);
270     ierr = PetscPushJSONValue(body,"DATA_TYPE","transfer",sizeof(body));CHKERRQ(ierr);                        ierr = PetscStrcat(body,",");CHKERRQ(ierr);
271     ierr = PetscPushJSONValue(body,"sync_level","null",sizeof(body));CHKERRQ(ierr);                           ierr = PetscStrcat(body,",");CHKERRQ(ierr);
272     ierr = PetscPushJSONValue(body,"source_endpoint","barryfsmith#MacBookPro",sizeof(body));CHKERRQ(ierr);    ierr = PetscStrcat(body,",");CHKERRQ(ierr);
273     ierr = PetscPushJSONValue(body,"label","PETSc transfer label",sizeof(body));CHKERRQ(ierr);                ierr = PetscStrcat(body,",");CHKERRQ(ierr);
274     ierr = PetscPushJSONValue(body,"length","1",sizeof(body));CHKERRQ(ierr);                                  ierr = PetscStrcat(body,",");CHKERRQ(ierr);
275     ierr = PetscPushJSONValue(body,"destination_endpoint","mcs#home",sizeof(body));CHKERRQ(ierr);             ierr = PetscStrcat(body,",");CHKERRQ(ierr);
276 
277     ierr = PetscStrcat(body,"\"DATA\": [ {");CHKERRQ(ierr);
278     ierr = PetscPushJSONValue(body,"source_path","/~/FEM_GPU.pdf",sizeof(body));CHKERRQ(ierr);                ierr = PetscStrcat(body,",");CHKERRQ(ierr);
279     ierr = PetscPushJSONValue(body,"destination_path","/~/FEM_GPU.pdf",sizeof(body));CHKERRQ(ierr);           ierr = PetscStrcat(body,",");CHKERRQ(ierr);
280     ierr = PetscPushJSONValue(body,"verify_size","null",sizeof(body));CHKERRQ(ierr);                          ierr = PetscStrcat(body,",");CHKERRQ(ierr);
281     ierr = PetscPushJSONValue(body,"recursive","false",sizeof(body));CHKERRQ(ierr);                           ierr = PetscStrcat(body,",");CHKERRQ(ierr);
282     ierr = PetscPushJSONValue(body,"DATA_TYPE","transfer_item",sizeof(body));CHKERRQ(ierr);
283     ierr = PetscStrcat(body,"} ] }");CHKERRQ(ierr);
284 
285     ierr = PetscSSLInitializeContext(&ctx);CHKERRQ(ierr);
286     ierr = PetscHTTPSConnect("transfer.api.globusonline.org",443,ctx,&sock,&ssl);CHKERRQ(ierr);
287     ierr = PetscHTTPSRequest("POST","transfer.api.globusonline.org/v0.10/transfer",head,"application/json",body,ssl,buff,sizeof(buff));CHKERRQ(ierr);
288     ierr = PetscSSLDestroyContext(ctx);CHKERRQ(ierr);
289     close(sock);
290     ierr   = PetscPullJSONValue(buff,"code",submission_id,sizeof(submission_id),&found);CHKERRQ(ierr);
291     if (!found) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Globus did not return code on transfer");
292     ierr = PetscStrcmp(submission_id,"Accepted",&found);CHKERRQ(ierr);
293     if (!found) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Globus did not accept transfer");
294   }
295   PetscFunctionReturn(0);
296 }
297 
298 
299