1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 2001-2020. All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * %CopyrightEnd%
19 */
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include <ctype.h>
26
27 #define ERTS_WANT_EXTERNAL_TAGS
28 #include "sys.h"
29 #include "erl_vm.h"
30 #include "erl_sys_driver.h"
31 #include "global.h"
32 #include "erl_process.h"
33 #include "error.h"
34 #include "bif.h"
35 #include "big.h"
36 #include "dist.h"
37 #include "erl_version.h"
38 #include "erl_binary.h"
39 #include "erl_db_util.h"
40 #include "register.h"
41 #include "external.h"
42 #include "packet_parser.h"
43 #include "erl_bits.h"
44 #include "erl_bif_unique.h"
45 #include "dtrace-wrapper.h"
46 #include "erl_proc_sig_queue.h"
47
48 static Port *open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump);
49 static int merge_global_environment(erts_osenv_t *env, Eterm key_value_pairs);
50 static char **convert_args(Eterm);
51 static void free_args(char **);
52
53 char *erts_default_arg0 = "default";
54
erts_internal_open_port_2(BIF_ALIST_2)55 BIF_RETTYPE erts_internal_open_port_2(BIF_ALIST_2)
56 {
57 BIF_RETTYPE ret;
58 Port *port;
59 Eterm res;
60 char *str;
61 int err_type, err_num;
62 ErtsLinkData *ldp;
63 ErtsLink *lnk;
64
65 port = open_port(BIF_P, BIF_ARG_1, BIF_ARG_2, &err_type, &err_num);
66 if (!port) {
67 if (err_type == -3) {
68 ASSERT(err_num == BADARG || err_num == SYSTEM_LIMIT);
69 if (err_num == BADARG)
70 res = am_badarg;
71 else if (err_num == SYSTEM_LIMIT)
72 res = am_system_limit;
73 else
74 /* this is only here to silence gcc, it should not happen */
75 BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
76 } else if (err_type == -2) {
77 str = erl_errno_id(err_num);
78 res = erts_atom_put((byte *) str, sys_strlen(str), ERTS_ATOM_ENC_LATIN1, 1);
79 } else {
80 res = am_einval;
81 }
82 BIF_RET(res);
83 }
84
85 ldp = erts_link_create(ERTS_LNK_TYPE_PORT, BIF_P->common.id, port->common.id);
86 ASSERT(ldp->a.other.item == port->common.id);
87 ASSERT(ldp->b.other.item == BIF_P->common.id);
88 /*
89 * This link should not already be present, but can potentially
90 * due to id wrapping...
91 */
92 lnk = erts_link_tree_lookup_insert(&ERTS_P_LINKS(BIF_P), &ldp->a);
93 erts_link_tree_insert(&ERTS_P_LINKS(port), &ldp->b);
94
95 if (port->drv_ptr->flags & ERL_DRV_FLAG_USE_INIT_ACK) {
96
97 /* Copied from erl_port_task.c */
98 port->async_open_port = erts_alloc(ERTS_ALC_T_PRTSD,
99 sizeof(*port->async_open_port));
100 erts_make_ref_in_array(port->async_open_port->ref);
101 port->async_open_port->to = BIF_P->common.id;
102
103 /*
104 * We unconditionaly *must* do a receive on a message
105 * containing the reference after this...
106 */
107 ERTS_RECV_MARK_SAVE(BIF_P);
108 ERTS_RECV_MARK_SET(BIF_P);
109
110 res = erts_proc_store_ref(BIF_P, port->async_open_port->ref);
111 } else {
112 res = port->common.id;
113 }
114
115 if (IS_TRACED_FL(BIF_P, F_TRACE_PROCS))
116 trace_proc(BIF_P, ERTS_PROC_LOCK_MAIN, BIF_P,
117 am_link, port->common.id);
118
119 ERTS_BIF_PREP_RET(ret, res);
120
121 erts_port_release(port);
122
123 if (lnk)
124 erts_link_release(lnk);
125
126 return ret;
127 }
128
129 static ERTS_INLINE Port *
lookup_port(Process * c_p,Eterm id_or_name,Uint32 invalid_flags)130 lookup_port(Process *c_p, Eterm id_or_name, Uint32 invalid_flags)
131 {
132 /* TODO: Implement nicer lookup in register... */
133 Eterm id;
134 if (is_atom(id_or_name))
135 id = erts_whereis_name_to_id(c_p, id_or_name);
136 else
137 id = id_or_name;
138 return erts_port_lookup(id, invalid_flags);
139 }
140
141 static ERTS_INLINE Port *
sig_lookup_port(Process * c_p,Eterm id_or_name)142 sig_lookup_port(Process *c_p, Eterm id_or_name)
143 {
144 return lookup_port(c_p, id_or_name, ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP);
145 }
146
147 /* Non-inline copy of sig_lookup_port to be exported */
erts_sig_lookup_port(Process * c_p,Eterm id_or_name)148 Port *erts_sig_lookup_port(Process *c_p, Eterm id_or_name)
149 {
150 return lookup_port(c_p, id_or_name, ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP);
151 }
152
153 static ERTS_INLINE Port *
data_lookup_port(Process * c_p,Eterm id_or_name)154 data_lookup_port(Process *c_p, Eterm id_or_name)
155 {
156 return lookup_port(c_p, id_or_name, ERTS_PORT_SFLGS_INVALID_LOOKUP);
157 }
158
159 /*
160 * erts_internal:port_command/3 is used by the
161 * erlang:port_command/2 and erlang:port_command/3
162 * BIFs.
163 */
164
erts_internal_port_command_3(BIF_ALIST_3)165 BIF_RETTYPE erts_internal_port_command_3(BIF_ALIST_3)
166 {
167 BIF_RETTYPE res;
168 Port *prt;
169 int flags = 0;
170 erts_aint32_t state;
171
172 if (is_not_nil(BIF_ARG_3)) {
173 Eterm l = BIF_ARG_3;
174 while (is_list(l)) {
175 Eterm* cons = list_val(l);
176 Eterm car = CAR(cons);
177 if (car == am_force)
178 flags |= ERTS_PORT_SIG_FLG_FORCE;
179 else if (car == am_nosuspend)
180 flags |= ERTS_PORT_SIG_FLG_NOSUSPEND;
181 else
182 BIF_RET(am_badarg);
183 l = CDR(cons);
184 }
185 if (!is_nil(l))
186 BIF_RET(am_badarg);
187 }
188
189 prt = sig_lookup_port(BIF_P, BIF_ARG_1);
190 if (!prt)
191 ERTS_BIF_PREP_RET(res, am_badarg);
192 else if ((flags & ERTS_PORT_SIG_FLG_FORCE)
193 && !(prt->drv_ptr->flags & ERL_DRV_FLAG_SOFT_BUSY)) {
194 ERTS_BIF_PREP_RET(res, am_notsup);
195 }
196 else {
197 Eterm ref;
198 #ifdef DEBUG
199 ref = NIL;
200 #endif
201
202 switch (erts_port_output(BIF_P, flags, prt, prt->common.id,
203 BIF_ARG_2, &ref)) {
204 case ERTS_PORT_OP_BADARG:
205 case ERTS_PORT_OP_DROPPED:
206 ERTS_BIF_PREP_RET(res, am_badarg);
207 break;
208 case ERTS_PORT_OP_BUSY:
209 ASSERT(!(flags & ERTS_PORT_SIG_FLG_FORCE));
210 if (flags & ERTS_PORT_SIG_FLG_NOSUSPEND)
211 ERTS_BIF_PREP_RET(res, am_false);
212 else {
213 erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, prt);
214 ERTS_BIF_YIELD3(bif_export[BIF_erts_internal_port_command_3],
215 BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
216 }
217 break;
218 case ERTS_PORT_OP_BUSY_SCHEDULED:
219 ASSERT(!(flags & ERTS_PORT_SIG_FLG_FORCE));
220 /* Fall through... */
221 case ERTS_PORT_OP_SCHEDULED:
222 ASSERT(is_internal_ordinary_ref(ref));
223 BIF_RET(ref);
224 case ERTS_PORT_OP_DONE:
225 ERTS_BIF_PREP_RET(res, am_true);
226 break;
227 default:
228 ERTS_INTERNAL_ERROR("Unexpected erts_port_output() result");
229 ERTS_BIF_PREP_RET(res, am_internal_error);
230 break;
231 }
232 }
233
234 state = erts_atomic32_read_acqb(&BIF_P->state);
235 if (state & ERTS_PSFLG_EXITING) {
236 KILL_CATCHES(BIF_P); /* Must exit */
237 ERTS_BIF_PREP_ERROR(res, BIF_P, EXC_ERROR);
238 }
239 else {
240 /* Ensure signal order is preserved... */
241 if (state & (ERTS_PSFLG_SIG_Q|ERTS_PSFLG_SIG_IN_Q))
242 ERTS_BIF_PREP_HANDLE_SIGNALS_RETURN(res, BIF_P, res);
243 }
244
245 return res;
246 }
247
erts_internal_port_call_3(BIF_ALIST_3)248 BIF_RETTYPE erts_internal_port_call_3(BIF_ALIST_3)
249 {
250 Port* prt;
251 Eterm retval;
252 Uint uint_op;
253 erts_aint32_t state;
254
255 prt = sig_lookup_port(BIF_P, BIF_ARG_1);
256 if (!prt)
257 retval = am_badarg;
258 else if (!term_to_Uint(BIF_ARG_2, &uint_op))
259 retval = am_badarg;
260 else if (uint_op > (Uint) UINT_MAX)
261 retval = am_badarg;
262 else {
263 unsigned int op = (unsigned int) uint_op;
264
265 switch (erts_port_call(BIF_P, prt, op, BIF_ARG_3, &retval)) {
266 case ERTS_PORT_OP_DROPPED:
267 case ERTS_PORT_OP_BADARG:
268 retval = am_badarg;
269 break;
270 case ERTS_PORT_OP_SCHEDULED:
271 ASSERT(is_internal_ordinary_ref(retval));
272 /* Signal order preserved by reply... */
273 BIF_RET(retval);
274 break;
275 case ERTS_PORT_OP_DONE:
276 ASSERT(is_not_internal_ref(retval));
277 break;
278 default:
279 ERTS_INTERNAL_ERROR("Unexpected erts_port_call() result");
280 retval = am_internal_error;
281 break;
282 }
283 }
284
285 state = erts_atomic32_read_acqb(&BIF_P->state);
286 if (state & ERTS_PSFLG_EXITING)
287 ERTS_BIF_EXITED(BIF_P);
288 else {
289 /* Ensure signal order is preserved... */
290 if (state & (ERTS_PSFLG_SIG_Q|ERTS_PSFLG_SIG_IN_Q))
291 ERTS_BIF_HANDLE_SIGNALS_RETURN(BIF_P, retval);
292 }
293
294 BIF_RET(retval);
295 }
296
erts_internal_port_control_3(BIF_ALIST_3)297 BIF_RETTYPE erts_internal_port_control_3(BIF_ALIST_3)
298 {
299 Port* prt;
300 Eterm retval;
301 Uint uint_op;
302 erts_aint32_t state;
303
304 prt = sig_lookup_port(BIF_P, BIF_ARG_1);
305 if (!prt)
306 retval = am_badarg;
307 else if (!term_to_Uint(BIF_ARG_2, &uint_op))
308 retval = am_badarg;
309 else if (uint_op > (Uint) UINT_MAX)
310 retval = am_badarg;
311 else {
312 unsigned int op = (unsigned int) uint_op;
313
314 switch (erts_port_control(BIF_P, prt, op, BIF_ARG_3, &retval)) {
315 case ERTS_PORT_OP_BADARG:
316 case ERTS_PORT_OP_DROPPED:
317 retval = am_badarg;
318 break;
319 case ERTS_PORT_OP_SCHEDULED:
320 ASSERT(is_internal_ordinary_ref(retval));
321 /* Signal order preserved by reply... */
322 BIF_RET(retval);
323 case ERTS_PORT_OP_DONE:
324 ASSERT(is_not_internal_ref(retval));
325 break;
326 default:
327 ERTS_INTERNAL_ERROR("Unexpected erts_port_control() result");
328 retval = am_internal_error;
329 break;
330 }
331 }
332
333 state = erts_atomic32_read_acqb(&BIF_P->state);
334 if (state & ERTS_PSFLG_EXITING)
335 ERTS_BIF_EXITED(BIF_P);
336 else {
337 /* Ensure signal order is preserved... */
338 if (state & (ERTS_PSFLG_SIG_Q|ERTS_PSFLG_SIG_IN_Q))
339 ERTS_BIF_HANDLE_SIGNALS_RETURN(BIF_P, retval);
340 }
341
342 BIF_RET(retval);
343 }
344
345 /*
346 * erts_internal:port_close/1 is used by the
347 * erlang:port_close/1 BIF.
348 */
erts_internal_port_close_1(BIF_ALIST_1)349 BIF_RETTYPE erts_internal_port_close_1(BIF_ALIST_1)
350 {
351 Eterm retval;
352 Port *prt;
353
354 prt = sig_lookup_port(BIF_P, BIF_ARG_1);
355 if (!prt)
356 retval = am_badarg;
357 else {
358
359 #ifdef DEBUG
360 retval = NIL;
361 #endif
362
363 switch (erts_port_exit(BIF_P, 0, prt, BIF_P->common.id,
364 am_normal, &retval)) {
365 case ERTS_PORT_OP_BADARG:
366 case ERTS_PORT_OP_DROPPED:
367 retval = am_badarg;
368 break;
369 case ERTS_PORT_OP_SCHEDULED:
370 ASSERT(is_internal_ordinary_ref(retval));
371 /* Signal order preserved by reply... */
372 BIF_RET(retval);
373 break;
374 case ERTS_PORT_OP_DONE:
375 retval = am_true;
376 break;
377 default:
378 ERTS_INTERNAL_ERROR("Unexpected erts_port_exit() result");
379 retval = am_internal_error;
380 break;
381 }
382 }
383
384 /* Ensure signal order is preserved... */
385 if (ERTS_PROC_HAS_INCOMING_SIGNALS(BIF_P))
386 ERTS_BIF_HANDLE_SIGNALS_RETURN(BIF_P, retval);
387
388 BIF_RET(retval);
389 }
390
391 /*
392 * erts_internal:port_connect/2 is used by the
393 * erlang:port_connect/2 BIF.
394 */
erts_internal_port_connect_2(BIF_ALIST_2)395 BIF_RETTYPE erts_internal_port_connect_2(BIF_ALIST_2)
396 {
397 Eterm retval;
398 Port* prt;
399
400 prt = sig_lookup_port(BIF_P, BIF_ARG_1);
401 if (!prt)
402 retval = am_badarg;
403 else {
404 #ifdef DEBUG
405 retval = NIL;
406 #endif
407
408 switch (erts_port_connect(BIF_P, 0, prt, BIF_P->common.id,
409 BIF_ARG_2, &retval)) {
410 case ERTS_PORT_OP_BADARG:
411 case ERTS_PORT_OP_DROPPED:
412 retval = am_badarg;
413 break;
414 case ERTS_PORT_OP_SCHEDULED:
415 ASSERT(is_internal_ordinary_ref(retval));
416 /* Signal order preserved by reply... */
417 BIF_RET(retval);
418 case ERTS_PORT_OP_DONE:
419 retval = am_true;
420 break;
421 default:
422 ERTS_INTERNAL_ERROR("Unexpected erts_port_connect() result");
423 retval = am_internal_error;
424 break;
425 }
426 }
427
428 /* Ensure signal order is preserved... */
429 if (ERTS_PROC_HAS_INCOMING_SIGNALS(BIF_P))
430 ERTS_BIF_HANDLE_SIGNALS_RETURN(BIF_P, retval);
431
432 BIF_RET(retval);
433 }
434
erts_internal_port_info_1(BIF_ALIST_1)435 BIF_RETTYPE erts_internal_port_info_1(BIF_ALIST_1)
436 {
437 Eterm retval;
438 Port* prt;
439
440 if (is_internal_port(BIF_ARG_1) || is_atom(BIF_ARG_1)) {
441 prt = sig_lookup_port(BIF_P, BIF_ARG_1);
442 if (!prt)
443 retval = am_undefined;
444 else {
445 switch (erts_port_info(BIF_P, prt, THE_NON_VALUE, &retval)) {
446 case ERTS_PORT_OP_BADARG:
447 retval = am_badarg;
448 break;
449 case ERTS_PORT_OP_DROPPED:
450 retval = am_undefined;
451 break;
452 case ERTS_PORT_OP_SCHEDULED:
453 ASSERT(is_internal_ordinary_ref(retval));
454 /* Signal order preserved by reply... */
455 BIF_RET(retval);
456 case ERTS_PORT_OP_DONE:
457 ASSERT(is_not_internal_ref(retval));
458 break;
459 default:
460 ERTS_INTERNAL_ERROR("Unexpected erts_port_info() result");
461 retval = am_internal_error;
462 break;
463 }
464 }
465 }
466 else if (is_external_port(BIF_ARG_1)) {
467 if (external_port_dist_entry(BIF_ARG_1) == erts_this_dist_entry)
468 retval = am_undefined;
469 else
470 retval = am_badarg;
471 }
472 else {
473 retval = am_badarg;
474 }
475
476 /* Ensure signal order is preserved... */
477 if (ERTS_PROC_HAS_INCOMING_SIGNALS(BIF_P))
478 ERTS_BIF_HANDLE_SIGNALS_RETURN(BIF_P, retval);
479
480 BIF_RET(retval);
481 }
482
483
erts_internal_port_info_2(BIF_ALIST_2)484 BIF_RETTYPE erts_internal_port_info_2(BIF_ALIST_2)
485 {
486 Eterm retval;
487 Port* prt;
488
489 if (is_internal_port(BIF_ARG_1) || is_atom(BIF_ARG_1)) {
490 prt = sig_lookup_port(BIF_P, BIF_ARG_1);
491 if (!prt)
492 retval = am_undefined;
493 else {
494 switch (erts_port_info(BIF_P, prt, BIF_ARG_2, &retval)) {
495 case ERTS_PORT_OP_BADARG:
496 retval = am_badarg;
497 break;
498 case ERTS_PORT_OP_DROPPED:
499 retval = am_undefined;
500 break;
501 case ERTS_PORT_OP_SCHEDULED:
502 ASSERT(is_internal_ordinary_ref(retval));
503 /* Signal order preserved by reply... */
504 BIF_RET(retval);
505 case ERTS_PORT_OP_DONE:
506 ASSERT(is_not_internal_ref(retval));
507 break;
508 default:
509 ERTS_INTERNAL_ERROR("Unexpected erts_port_info() result");
510 retval = am_internal_error;
511 break;
512 }
513 }
514 }
515 else if (is_external_port(BIF_ARG_1)) {
516 if (external_port_dist_entry(BIF_ARG_1) == erts_this_dist_entry)
517 retval = am_undefined;
518 else
519 retval = am_badarg;
520 }
521 else {
522 retval = am_badarg;
523 }
524
525 /* Ensure signal order is preserved... */
526 if (ERTS_PROC_HAS_INCOMING_SIGNALS(BIF_P))
527 ERTS_BIF_HANDLE_SIGNALS_RETURN(BIF_P, retval);
528
529 BIF_RET(retval);
530 }
531
532 /*
533 * The erlang:port_set_data()/erlang:port_get_data() operations should
534 * be viewed as operations on a table (inet_db) with data values
535 * associated with port identifier keys. That is, these operations are
536 * *not* signals to/from ports.
537 */
538
539 #if (TAG_PRIMARY_IMMED1 & 0x3) == 0
540 # error "erlang:port_set_data()/erlang:port_get_data() needs to be rewritten!"
541 #endif
542
543 typedef struct {
544 ErtsThrPrgrLaterOp later_op;
545 Uint hsize;
546 Eterm data;
547 ErlOffHeap off_heap;
548 Eterm heap[1];
549 } ErtsPortDataHeap;
550
551 static void
free_port_data_heap(void * vpdhp)552 free_port_data_heap(void *vpdhp)
553 {
554 erts_cleanup_offheap(&((ErtsPortDataHeap *) vpdhp)->off_heap);
555 erts_free(ERTS_ALC_T_PORT_DATA_HEAP, vpdhp);
556 }
557
558 static ERTS_INLINE void
cleanup_old_port_data(erts_aint_t data)559 cleanup_old_port_data(erts_aint_t data)
560 {
561 if ((data & 0x3) != 0) {
562 ASSERT(is_immed((Eterm) data));
563 }
564 else {
565 ErtsPortDataHeap *pdhp = (ErtsPortDataHeap *) data;
566 size_t size;
567 ERTS_THR_DATA_DEPENDENCY_READ_MEMORY_BARRIER;
568 size = sizeof(ErtsPortDataHeap) + (pdhp->hsize-1)*sizeof(Eterm);
569 erts_schedule_thr_prgr_later_cleanup_op(free_port_data_heap,
570 (void *) pdhp,
571 &pdhp->later_op,
572 size);
573 }
574 }
575
576 void
erts_init_port_data(Port * prt)577 erts_init_port_data(Port *prt)
578 {
579 erts_atomic_init_nob(&prt->data, (erts_aint_t) am_undefined);
580 }
581
582 void
erts_cleanup_port_data(Port * prt)583 erts_cleanup_port_data(Port *prt)
584 {
585 ASSERT(erts_atomic32_read_nob(&prt->state) & ERTS_PORT_SFLGS_INVALID_LOOKUP);
586 cleanup_old_port_data(erts_atomic_xchg_nob(&prt->data,
587 (erts_aint_t) NULL));
588 }
589
590 Uint
erts_port_data_size(Port * prt)591 erts_port_data_size(Port *prt)
592 {
593 erts_aint_t data = erts_atomic_read_ddrb(&prt->data);
594
595 if ((data & 0x3) != 0) {
596 ASSERT(is_immed((Eterm) (UWord) data));
597 return (Uint) 0;
598 }
599 else {
600 ErtsPortDataHeap *pdhp = (ErtsPortDataHeap *) data;
601 return (Uint) sizeof(ErtsPortDataHeap) + (pdhp->hsize-1)*sizeof(Eterm);
602 }
603 }
604
605 ErlOffHeap *
erts_port_data_offheap(Port * prt)606 erts_port_data_offheap(Port *prt)
607 {
608 erts_aint_t data = erts_atomic_read_ddrb(&prt->data);
609
610 if ((data & 0x3) != 0) {
611 ASSERT(is_immed((Eterm) (UWord) data));
612 return NULL;
613 }
614 else {
615 ErtsPortDataHeap *pdhp = (ErtsPortDataHeap *) data;
616 return &pdhp->off_heap;
617 }
618 }
619
port_set_data_2(BIF_ALIST_2)620 BIF_RETTYPE port_set_data_2(BIF_ALIST_2)
621 {
622 /*
623 * This is not a signal. See comment above.
624 */
625 erts_aint_t data;
626 Port* prt;
627
628 prt = data_lookup_port(BIF_P, BIF_ARG_1);
629 if (!prt)
630 BIF_ERROR(BIF_P, BADARG);
631
632 if (is_immed(BIF_ARG_2)) {
633 data = (erts_aint_t) BIF_ARG_2;
634 ASSERT((data & 0x3) != 0);
635 }
636 else {
637 ErtsPortDataHeap *pdhp;
638 Uint hsize;
639 Eterm *hp;
640
641 hsize = size_object(BIF_ARG_2);
642 pdhp = erts_alloc(ERTS_ALC_T_PORT_DATA_HEAP,
643 sizeof(ErtsPortDataHeap) + (hsize-1)*sizeof(Eterm));
644 hp = &pdhp->heap[0];
645 pdhp->off_heap.first = NULL;
646 pdhp->off_heap.overhead = 0;
647 pdhp->hsize = hsize;
648 pdhp->data = copy_struct(BIF_ARG_2, hsize, &hp, &pdhp->off_heap);
649 data = (erts_aint_t) pdhp;
650 ASSERT((data & 0x3) == 0);
651 }
652
653 data = erts_atomic_xchg_wb(&prt->data, data);
654
655 if (data == (erts_aint_t)NULL) {
656 /* Port terminated by racing thread */
657 data = erts_atomic_xchg_wb(&prt->data, data);
658 ASSERT(data != (erts_aint_t)NULL);
659 cleanup_old_port_data(data);
660 BIF_ERROR(BIF_P, BADARG);
661 }
662 cleanup_old_port_data(data);
663 BIF_RET(am_true);
664 }
665
666
port_get_data_1(BIF_ALIST_1)667 BIF_RETTYPE port_get_data_1(BIF_ALIST_1)
668 {
669 /*
670 * This is not a signal. See comment above.
671 */
672 Eterm res;
673 erts_aint_t data;
674 Port* prt;
675
676 prt = data_lookup_port(BIF_P, BIF_ARG_1);
677 if (!prt)
678 BIF_ERROR(BIF_P, BADARG);
679
680 data = erts_atomic_read_ddrb(&prt->data);
681 if (data == (erts_aint_t)NULL)
682 BIF_ERROR(BIF_P, BADARG); /* Port terminated by racing thread */
683
684 if ((data & 0x3) != 0) {
685 res = (Eterm) (UWord) data;
686 ASSERT(is_immed(res));
687 }
688 else {
689 ErtsPortDataHeap *pdhp = (ErtsPortDataHeap *) data;
690 Eterm *hp = HAlloc(BIF_P, pdhp->hsize);
691 res = copy_struct(pdhp->data, pdhp->hsize, &hp, &MSO(BIF_P));
692 }
693
694 BIF_RET(res);
695 }
696
erts_port_data_read(Port * prt)697 Eterm erts_port_data_read(Port* prt)
698 {
699 Eterm res;
700 erts_aint_t data;
701
702 data = erts_atomic_read_ddrb(&prt->data);
703 if (data == (erts_aint_t)NULL)
704 return am_undefined; /* Port terminated by racing thread */
705
706 if ((data & 0x3) != 0) {
707 res = (Eterm) (UWord) data;
708 ASSERT(is_immed(res));
709 }
710 else {
711 ErtsPortDataHeap *pdhp = (ErtsPortDataHeap *) data;
712 res = pdhp->data;
713 }
714 return res;
715 }
716
717
718 /*
719 * Open a port. Most of the work is not done here but rather in
720 * the file io.c.
721 * Error returns: -1 or -2 returned from open_driver (-2 implies
722 * that *err_nump contains the error code; -1 means we don't really know what happened),
723 * -3 if argument parsing failed or we are out of ports (*err_nump should contain
724 * either BADARG or SYSTEM_LIMIT).
725 */
726
727 static Port *
open_port(Process * p,Eterm name,Eterm settings,int * err_typep,int * err_nump)728 open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump)
729 {
730 int merged_environment = 0;
731 Sint i;
732 Eterm option;
733 Uint arity;
734 Eterm* tp;
735 Uint* nargs;
736 erts_driver_t* driver;
737 char* name_buf = NULL;
738 SysDriverOpts opts;
739 Sint linebuf;
740 Eterm edir = NIL;
741 byte dir[MAXPATHLEN];
742 erts_aint32_t sflgs = 0;
743 Port *port;
744
745 /* These are the defaults */
746 opts.packet_bytes = 0;
747 opts.use_stdio = 1;
748 opts.redir_stderr = 0;
749 opts.read_write = 0;
750 opts.hide_window = 0;
751 opts.wd = NULL;
752 opts.exit_status = 0;
753 opts.overlapped_io = 0;
754 opts.spawn_type = ERTS_SPAWN_ANY;
755 opts.argv = NULL;
756 opts.parallelism = erts_port_parallelism;
757 opts.high_watermark = 8192;
758 opts.low_watermark = opts.high_watermark / 2;
759 opts.port_watermarks_set = 0;
760 opts.msgq_watermarks_set = 0;
761 erts_osenv_init(&opts.envir);
762
763 linebuf = 0;
764
765 *err_nump = 0;
766
767 if (is_not_list(settings) && is_not_nil(settings)) {
768 goto badarg;
769 }
770 /*
771 * Parse the settings.
772 */
773
774 if (is_not_nil(settings)) {
775 nargs = list_val(settings);
776 while (1) {
777 if (is_tuple_arity(*nargs, 2)) {
778 tp = tuple_val(*nargs);
779 arity = *tp++;
780 option = *tp++;
781 if (option == am_packet) {
782 if (is_not_small(*tp)) {
783 goto badarg;
784 }
785 opts.packet_bytes = signed_val(*tp);
786 switch (opts.packet_bytes) {
787 case 1:
788 case 2:
789 case 4:
790 break;
791 default:
792 goto badarg;
793 }
794 } else if (option == am_line) {
795 if (is_not_small(*tp)) {
796 goto badarg;
797 }
798 linebuf = signed_val(*tp);
799 if (linebuf <= 0) {
800 goto badarg;
801 }
802 } else if (option == am_env) {
803 if (merged_environment) {
804 /* Ignore previous env option */
805 erts_osenv_clear(&opts.envir);
806 }
807
808 merged_environment = 1;
809
810 if (merge_global_environment(&opts.envir, *tp)) {
811 goto badarg;
812 }
813 } else if (option == am_args) {
814 char **av;
815 char **oav = opts.argv;
816 if ((av = convert_args(*tp)) == NULL) {
817 goto badarg;
818 }
819 opts.argv = av;
820 if (oav) {
821 opts.argv[0] = oav[0];
822 oav[0] = erts_default_arg0;
823 free_args(oav);
824 }
825
826 } else if (option == am_arg0) {
827 char *a0;
828
829 if ((a0 = erts_convert_filename_to_native(*tp, NULL, 0, ERTS_ALC_T_TMP, 1, 1, NULL)) == NULL) {
830 goto badarg;
831 }
832 if (opts.argv == NULL) {
833 opts.argv = erts_alloc(ERTS_ALC_T_TMP,
834 2 * sizeof(char **));
835 opts.argv[0] = a0;
836 opts.argv[1] = NULL;
837 } else {
838 if (opts.argv[0] != erts_default_arg0) {
839 erts_free(ERTS_ALC_T_TMP, opts.argv[0]);
840 }
841 opts.argv[0] = a0;
842 }
843 } else if (option == am_cd) {
844 edir = *tp;
845 } else if (option == am_parallelism) {
846 if (*tp == am_true)
847 opts.parallelism = 1;
848 else if (*tp == am_false)
849 opts.parallelism = 0;
850 else
851 goto badarg;
852 } else if (option == am_busy_limits_port) {
853 Uint high, low;
854 if (*tp == am_disabled)
855 low = high = ERL_DRV_BUSY_MSGQ_DISABLED;
856 else if (!is_tuple_arity(*tp, 2))
857 goto badarg;
858 else {
859 Eterm *wtp = tuple_val(*tp);
860 if (!term_to_Uint(wtp[1], &low))
861 goto badarg;
862 if (!term_to_Uint(wtp[2], &high))
863 goto badarg;
864 if (high < ERL_DRV_BUSY_MSGQ_LIM_MIN)
865 goto badarg;
866 if (high > ERL_DRV_BUSY_MSGQ_LIM_MAX)
867 goto badarg;
868 if (low < ERL_DRV_BUSY_MSGQ_LIM_MIN)
869 goto badarg;
870 if (low > ERL_DRV_BUSY_MSGQ_LIM_MAX)
871 goto badarg;
872 if (high == ~((Uint) 0) || low == ~((Uint) 0))
873 goto badarg;
874 if (low > high)
875 low = high;
876 }
877 opts.low_watermark = low;
878 opts.high_watermark = high;
879 opts.port_watermarks_set = !0;
880 } else if (option == am_busy_limits_msgq) {
881 Uint high, low;
882 if (*tp == am_disabled)
883 low = high = ERL_DRV_BUSY_MSGQ_DISABLED;
884 else if (!is_tuple_arity(*tp, 2))
885 goto badarg;
886 else {
887 Eterm *wtp = tuple_val(*tp);
888 if (!term_to_Uint(wtp[1], &low))
889 goto badarg;
890 if (!term_to_Uint(wtp[2], &high))
891 goto badarg;
892 if (high < ERL_DRV_BUSY_MSGQ_LIM_MIN)
893 goto badarg;
894 if (high > ERL_DRV_BUSY_MSGQ_LIM_MAX)
895 goto badarg;
896 if (low < ERL_DRV_BUSY_MSGQ_LIM_MIN)
897 goto badarg;
898 if (low > ERL_DRV_BUSY_MSGQ_LIM_MAX)
899 goto badarg;
900 if (high == ~((Uint) 0) || low == ~((Uint) 0))
901 goto badarg;
902 if (low > high)
903 low = high;
904 }
905 opts.low_msgq_watermark = low;
906 opts.high_msgq_watermark = high;
907 opts.msgq_watermarks_set = !0;
908 } else {
909 goto badarg;
910 }
911 } else if (*nargs == am_stream) {
912 opts.packet_bytes = 0;
913 } else if (*nargs == am_use_stdio) {
914 opts.use_stdio = 1;
915 } else if (*nargs == am_stderr_to_stdout) {
916 opts.redir_stderr = 1;
917 } else if (*nargs == am_line) {
918 linebuf = 512;
919 } else if (*nargs == am_nouse_stdio) {
920 opts.use_stdio = 0;
921 } else if (*nargs == am_binary) {
922 sflgs |= ERTS_PORT_SFLG_BINARY_IO;
923 } else if (*nargs == am_in) {
924 opts.read_write |= DO_READ;
925 } else if (*nargs == am_out) {
926 opts.read_write |= DO_WRITE;
927 } else if (*nargs == am_eof) {
928 sflgs |= ERTS_PORT_SFLG_SOFT_EOF;
929 } else if (*nargs == am_hide) {
930 opts.hide_window = 1;
931 } else if (*nargs == am_exit_status) {
932 opts.exit_status = 1;
933 } else if (*nargs == am_overlapped_io) {
934 opts.overlapped_io = 1;
935 } else {
936 goto badarg;
937 }
938 if (is_nil(*++nargs))
939 break;
940 if (is_not_list(*nargs)) {
941 goto badarg;
942 }
943 nargs = list_val(*nargs);
944 }
945 }
946
947 if (opts.read_write == 0) /* implement default */
948 opts.read_write = DO_READ|DO_WRITE;
949
950 /* Mutually exclusive arguments. */
951 if((linebuf && opts.packet_bytes) ||
952 (opts.redir_stderr && !opts.use_stdio)) {
953 goto badarg;
954 }
955
956 /* If we lacked an env option, fill in the global environment without
957 * changes. */
958 if (!merged_environment) {
959 merge_global_environment(&opts.envir, NIL);
960 }
961
962 /*
963 * Parse the first argument and start the appropriate driver.
964 */
965
966 if (is_atom(name) || (i = is_string(name))) {
967 /* a vanilla port */
968 if (is_atom(name)) {
969 name_buf = (char *) erts_alloc(ERTS_ALC_T_TMP,
970 atom_tab(atom_val(name))->len+1);
971 sys_memcpy((void *) name_buf,
972 (void *) atom_tab(atom_val(name))->name,
973 atom_tab(atom_val(name))->len);
974 name_buf[atom_tab(atom_val(name))->len] = '\0';
975 } else {
976 name_buf = (char *) erts_alloc(ERTS_ALC_T_TMP, i + 1);
977 if (intlist_to_buf(name, name_buf, i) != i)
978 erts_exit(ERTS_ERROR_EXIT, "%s:%d: Internal error\n", __FILE__, __LINE__);
979 name_buf[i] = '\0';
980 }
981 driver = &vanilla_driver;
982 } else {
983 if (is_not_tuple(name)) {
984 goto badarg; /* Not a process or fd port */
985 }
986 tp = tuple_val(name);
987 arity = *tp++;
988
989 if (arity == make_arityval(0)) {
990 goto badarg;
991 }
992
993 if (*tp == am_spawn || *tp == am_spawn_driver || *tp == am_spawn_executable) { /* A process port */
994 int encoding;
995 if (arity != make_arityval(2)) {
996 goto badarg;
997 }
998 name = tp[1];
999 encoding = erts_get_native_filename_encoding();
1000 /* Do not convert the command to utf-16le yet, do that in win32 specific code */
1001 /* since the cmd is used for comparsion with drivers names and copied to port info */
1002 if (encoding == ERL_FILENAME_WIN_WCHAR) {
1003 encoding = ERL_FILENAME_UTF8;
1004 }
1005 if ((name_buf = erts_convert_filename_to_encoding(name, NULL, 0, ERTS_ALC_T_TMP,0,1, encoding, NULL, 0))
1006 == NULL) {
1007 goto badarg;
1008 }
1009
1010 if (*tp == am_spawn_driver) {
1011 opts.spawn_type = ERTS_SPAWN_DRIVER;
1012 } else if (*tp == am_spawn_executable) {
1013 opts.spawn_type = ERTS_SPAWN_EXECUTABLE;
1014 }
1015
1016 driver = &spawn_driver;
1017 } else if (*tp == am_fd) { /* An fd port */
1018 if (arity != make_arityval(3)) {
1019 goto badarg;
1020 }
1021 if (is_not_small(tp[1]) || is_not_small(tp[2])) {
1022 goto badarg;
1023 }
1024 opts.ifd = unsigned_val(tp[1]);
1025 opts.ofd = unsigned_val(tp[2]);
1026
1027 /* Syntesize name from input and output descriptor. */
1028 name_buf = erts_alloc(ERTS_ALC_T_TMP, 256);
1029 erts_snprintf(name_buf, 256, "%i/%i", opts.ifd, opts.ofd);
1030
1031 driver = &fd_driver;
1032 } else {
1033 goto badarg;
1034 }
1035 }
1036
1037 if ((driver != &spawn_driver && opts.argv != NULL) ||
1038 (driver == &spawn_driver &&
1039 opts.spawn_type != ERTS_SPAWN_EXECUTABLE &&
1040 opts.argv != NULL)) {
1041 /* Argument vector only if explicit spawn_executable */
1042 goto badarg;
1043 }
1044
1045 if (edir != NIL) {
1046 if ((opts.wd = erts_convert_filename_to_native(edir, NULL, 0, ERTS_ALC_T_TMP,0,1,NULL)) == NULL) {
1047 goto badarg;
1048 }
1049 }
1050
1051 if (driver != &spawn_driver && opts.exit_status) {
1052 goto badarg;
1053 }
1054
1055 if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) {
1056 trace_sched(p, ERTS_PROC_LOCK_MAIN, am_out);
1057 }
1058
1059
1060 erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
1061
1062 port = erts_open_driver(driver, p->common.id, name_buf, &opts, err_typep, err_nump);
1063 #ifdef USE_VM_PROBES
1064 if (port && DTRACE_ENABLED(port_open)) {
1065 DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE);
1066 DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE);
1067
1068 dtrace_proc_str(p, process_str);
1069 erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", port->common.id);
1070 DTRACE3(port_open, process_str, name_buf, port_str);
1071 }
1072 #endif
1073
1074 if (port && IS_TRACED_FL(port, F_TRACE_PORTS))
1075 trace_port(port, am_getting_linked, p->common.id);
1076
1077 erts_proc_lock(p, ERTS_PROC_LOCK_MAIN);
1078
1079 if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) {
1080 trace_sched(p, ERTS_PROC_LOCK_MAIN, am_in);
1081 }
1082
1083 if (!port) {
1084 DEBUGF(("open_driver returned (%d:%d)\n",
1085 err_typep ? *err_typep : 4711,
1086 err_nump ? *err_nump : 4711));
1087 goto do_return;
1088 }
1089
1090 if (linebuf && port->linebuf == NULL){
1091 port->linebuf = allocate_linebuf(linebuf);
1092 sflgs |= ERTS_PORT_SFLG_LINEBUF_IO;
1093 }
1094
1095 if (sflgs)
1096 erts_atomic32_read_bor_relb(&port->state, sflgs);
1097
1098 do_return:
1099 erts_osenv_clear(&opts.envir);
1100 if (name_buf)
1101 erts_free(ERTS_ALC_T_TMP, (void *) name_buf);
1102 if (opts.argv) {
1103 free_args(opts.argv);
1104 }
1105 if (opts.wd && opts.wd != ((char *)dir)) {
1106 erts_free(ERTS_ALC_T_TMP, (void *) opts.wd);
1107 }
1108 return port;
1109
1110 badarg:
1111 if (err_typep)
1112 *err_typep = -3;
1113 if (err_nump)
1114 *err_nump = BADARG;
1115 port = NULL;
1116 goto do_return;
1117 }
1118
1119 /* Merges the the global environment and the given {Key, Value} list into env,
1120 * unsetting all keys whose value is either 'false' or NIL. The behavior on
1121 * NIL is undocumented and perhaps surprising, but the previous implementation
1122 * worked in this manner. */
merge_global_environment(erts_osenv_t * env,Eterm key_value_pairs)1123 static int merge_global_environment(erts_osenv_t *env, Eterm key_value_pairs) {
1124 const erts_osenv_t *global_env = erts_sys_rlock_global_osenv();
1125 erts_osenv_merge(env, global_env, 0);
1126 erts_sys_runlock_global_osenv();
1127
1128 while (is_list(key_value_pairs)) {
1129 Eterm *cell, *tuple;
1130
1131 cell = list_val(key_value_pairs);
1132
1133 if(!is_tuple_arity(CAR(cell), 2)) {
1134 return -1;
1135 }
1136
1137 tuple = tuple_val(CAR(cell));
1138 key_value_pairs = CDR(cell);
1139
1140 if(is_nil(tuple[2]) || tuple[2] == am_false) {
1141 if(erts_osenv_unset_term(env, tuple[1]) < 0) {
1142 return -1;
1143 }
1144 } else {
1145 if(erts_osenv_put_term(env, tuple[1], tuple[2]) < 0) {
1146 return -1;
1147 }
1148 }
1149 }
1150
1151 if(!is_nil(key_value_pairs)) {
1152 return -1;
1153 }
1154
1155 return 0;
1156 }
1157
1158 /* Arguments can be given i unicode and as raw binaries, convert filename is used to convert */
convert_args(Eterm l)1159 static char **convert_args(Eterm l)
1160 {
1161 char **pp;
1162 char *b;
1163 Sint n;
1164 Sint i = 0;
1165 Eterm str;
1166 if (is_not_list(l) && is_not_nil(l)) {
1167 return NULL;
1168 }
1169
1170 n = erts_list_length(l);
1171 if (n < 0)
1172 return NULL;
1173 /* We require at least one element in argv[0] + NULL at end */
1174 pp = erts_alloc(ERTS_ALC_T_TMP, (n + 2) * sizeof(char **));
1175 pp[i++] = erts_default_arg0;
1176 while (is_list(l)) {
1177 str = CAR(list_val(l));
1178 if ((b = erts_convert_filename_to_native(str,NULL,0,ERTS_ALC_T_TMP,1,1,NULL)) == NULL) {
1179 int j;
1180 for (j = 1; j < i; ++j)
1181 erts_free(ERTS_ALC_T_TMP, pp[j]);
1182 erts_free(ERTS_ALC_T_TMP, pp);
1183 return NULL;
1184 }
1185 pp[i++] = b;
1186 l = CDR(list_val(l));
1187 }
1188 pp[i] = NULL;
1189 return pp;
1190 }
1191
free_args(char ** av)1192 static void free_args(char **av)
1193 {
1194 int i;
1195 if (av == NULL)
1196 return;
1197 for (i = 0; av[i] != NULL; ++i) {
1198 if (av[i] != erts_default_arg0) {
1199 erts_free(ERTS_ALC_T_TMP, av[i]);
1200 }
1201 }
1202 erts_free(ERTS_ALC_T_TMP, av);
1203 }
1204
1205 /* ------------ decode_packet() and friends: */
1206
1207 struct packet_callback_args
1208 {
1209 Process* p; /* In */
1210 Eterm res; /* Out */
1211 int string_as_bin; /* return strings as binaries (http_bin): */
1212 byte* aligned_ptr;
1213 Uint bin_sz;
1214 Eterm orig;
1215 Uint bin_offs;
1216 byte bin_bitoffs;
1217 };
1218
1219 #define in_area(ptr,start,nbytes) \
1220 ((UWord)((char*)(ptr) - (char*)(start)) < (nbytes))
1221
1222 static Eterm
http_bld_string(struct packet_callback_args * pca,Uint ** hpp,Uint * szp,const char * str,Sint len)1223 http_bld_string(struct packet_callback_args* pca, Uint **hpp, Uint *szp,
1224 const char *str, Sint len)
1225 {
1226 Eterm res = THE_NON_VALUE;
1227 Uint size;
1228 int make_subbin;
1229
1230 if (pca->string_as_bin) {
1231 size = heap_bin_size(len);
1232 make_subbin = (size > ERL_SUB_BIN_SIZE
1233 && in_area(str, pca->aligned_ptr, pca->bin_sz));
1234 if (szp) {
1235 *szp += make_subbin ? ERL_SUB_BIN_SIZE : size;
1236 }
1237 if (hpp) {
1238 res = make_binary(*hpp);
1239 if (make_subbin) {
1240 ErlSubBin* bin = (ErlSubBin*) *hpp;
1241 bin->thing_word = HEADER_SUB_BIN;
1242 bin->size = len;
1243 bin->offs = pca->bin_offs + ((byte*)str - pca->aligned_ptr);
1244 bin->orig = pca->orig;
1245 bin->bitoffs = pca->bin_bitoffs;
1246 bin->bitsize = 0;
1247 bin->is_writable = 0;
1248 *hpp += ERL_SUB_BIN_SIZE;
1249 }
1250 else {
1251 ErlHeapBin* bin = (ErlHeapBin*) *hpp;
1252 bin->thing_word = header_heap_bin(len);
1253 bin->size = len;
1254 sys_memcpy(bin->data, str, len);
1255 *hpp += size;
1256 }
1257 }
1258 }
1259 else {
1260 res = erts_bld_string_n(hpp, szp, str, len);
1261 }
1262 return res;
1263 }
1264
http_response_erl(void * arg,int major,int minor,int status,const char * phrase,int phrase_len)1265 static int http_response_erl(void *arg, int major, int minor,
1266 int status, const char* phrase, int phrase_len)
1267 {
1268 /* {http_response,{Major,Minor},Status,"Phrase"} */
1269 struct packet_callback_args* pca = (struct packet_callback_args*) arg;
1270 Eterm phrase_term, ver;
1271 Uint hsize = 3 + 5;
1272 Eterm* hp;
1273 #ifdef DEBUG
1274 Eterm* hend;
1275 #endif
1276
1277 http_bld_string(pca, NULL, &hsize, phrase, phrase_len);
1278 hp = HAlloc(pca->p, hsize);
1279 #ifdef DEBUG
1280 hend = hp + hsize;
1281 #endif
1282 phrase_term = http_bld_string(pca, &hp, NULL, phrase, phrase_len);
1283 ver = TUPLE2(hp, make_small(major), make_small(minor));
1284 hp += 3;
1285 pca->res = TUPLE4(hp, am_http_response, ver, make_small(status), phrase_term);
1286 ASSERT(hp+5==hend);
1287 return 1;
1288 }
1289
http_bld_uri(struct packet_callback_args * pca,Eterm ** hpp,Uint * szp,const PacketHttpURI * uri)1290 static Eterm http_bld_uri(struct packet_callback_args* pca,
1291 Eterm** hpp, Uint* szp, const PacketHttpURI* uri)
1292 {
1293 Eterm s1, s2;
1294 if (uri->type == URI_STAR) {
1295 return am_Times; /* '*' */
1296 }
1297
1298 s1 = http_bld_string(pca, hpp, szp, uri->s1_ptr, uri->s1_len);
1299
1300 switch (uri->type) {
1301 case URI_ABS_PATH:
1302 return erts_bld_tuple(hpp, szp, 2, am_abs_path, s1);
1303 case URI_HTTP:
1304 case URI_HTTPS:
1305 s2 = http_bld_string(pca, hpp, szp, uri->s2_ptr, uri->s2_len);
1306 return erts_bld_tuple
1307 (hpp, szp, 5, am_absoluteURI,
1308 ((uri->type==URI_HTTP) ? am_http : am_https),
1309 s1,
1310 ((uri->port==0) ? am_undefined : make_small(uri->port)),
1311 s2);
1312
1313 case URI_STRING:
1314 return s1;
1315 case URI_SCHEME:
1316 s2 = http_bld_string(pca, hpp, szp, uri->s2_ptr, uri->s2_len);
1317 return erts_bld_tuple(hpp, szp, 3, am_scheme, s1, s2);
1318
1319 default:
1320 erts_exit(ERTS_ERROR_EXIT, "%s, line %d: type=%u\n", __FILE__, __LINE__, uri->type);
1321 }
1322 }
1323
http_request_erl(void * arg,const http_atom_t * meth,const char * meth_ptr,int meth_len,const PacketHttpURI * uri,int major,int minor)1324 static int http_request_erl(void* arg, const http_atom_t* meth,
1325 const char* meth_ptr, int meth_len,
1326 const PacketHttpURI* uri, int major, int minor)
1327 {
1328 struct packet_callback_args* pca = (struct packet_callback_args*) arg;
1329 Eterm meth_term, uri_term, ver_term;
1330 Uint sz = 0;
1331 Uint* szp = &sz;
1332 Eterm* hp;
1333 Eterm** hpp = NULL;
1334
1335 /* {http_request,Meth,Uri,Version} */
1336
1337 for (;;) {
1338 meth_term = (meth!=NULL) ? meth->atom :
1339 http_bld_string(pca, hpp, szp, meth_ptr, meth_len);
1340 uri_term = http_bld_uri(pca, hpp, szp, uri);
1341 ver_term = erts_bld_tuple(hpp, szp, 2,
1342 make_small(major), make_small(minor));
1343 pca->res = erts_bld_tuple(hpp, szp, 4, am_http_request, meth_term,
1344 uri_term, ver_term);
1345 if (hpp != NULL) break;
1346 hpp = &hp;
1347 hp = HAlloc(pca->p, sz);
1348 szp = NULL;
1349 }
1350 return 1;
1351 }
1352
1353 static int
http_header_erl(void * arg,const http_atom_t * name,const char * name_ptr,int name_len,const char * value_ptr,int value_len)1354 http_header_erl(void* arg, const http_atom_t* name, const char* name_ptr,
1355 int name_len, const char* value_ptr, int value_len)
1356 {
1357 struct packet_callback_args* pca = (struct packet_callback_args*) arg;
1358 Eterm bit_term, name_term, val_term;
1359 Uint sz = 6;
1360 Eterm* hp;
1361 #ifdef DEBUG
1362 Eterm* hend;
1363 #endif
1364
1365 /* {http_header,Bit,Name,IValue,Value} */
1366
1367 if (name == NULL) {
1368 http_bld_string(pca, NULL, &sz, name_ptr, name_len);
1369 }
1370 http_bld_string(pca, NULL, &sz, value_ptr, value_len);
1371
1372 hp = HAlloc(pca->p, sz);
1373 #ifdef DEBUG
1374 hend = hp + sz;
1375 #endif
1376
1377 if (name != NULL) {
1378 bit_term = make_small(name->index+1);
1379 name_term = name->atom;
1380 }
1381 else {
1382 bit_term = make_small(0);
1383 name_term = http_bld_string(pca, &hp,NULL,name_ptr,name_len);
1384 }
1385
1386 val_term = http_bld_string(pca, &hp, NULL, value_ptr, value_len);
1387 pca->res = TUPLE5(hp, am_http_header, bit_term, name_term, am_undefined, val_term);
1388 ASSERT(hp+6==hend);
1389 return 1;
1390 }
1391
http_eoh_erl(void * arg)1392 static int http_eoh_erl(void* arg)
1393 {
1394 /* http_eoh */
1395 struct packet_callback_args* pca = (struct packet_callback_args*) arg;
1396 pca->res = am_http_eoh;
1397 return 1;
1398 }
1399
http_error_erl(void * arg,const char * buf,int len)1400 static int http_error_erl(void* arg, const char* buf, int len)
1401 {
1402 /* {http_error,Line} */
1403 struct packet_callback_args* pca = (struct packet_callback_args*) arg;
1404 Uint sz = 3;
1405 Eterm* hp;
1406 #ifdef DEBUG
1407 Eterm* hend;
1408 #endif
1409
1410 http_bld_string(pca, NULL, &sz, buf, len);
1411
1412 hp = HAlloc(pca->p, sz);
1413 #ifdef DEBUG
1414 hend = hp + sz;
1415 #endif
1416 pca->res = erts_bld_tuple(&hp, NULL, 2, am_http_error,
1417 http_bld_string(pca, &hp, NULL, buf, len));
1418 ASSERT(hp==hend);
1419 return 1;
1420 }
1421
1422 static
ssl_tls_erl(void * arg,unsigned type,unsigned major,unsigned minor,const char * buf,int len,const char * prefix,int plen)1423 int ssl_tls_erl(void* arg, unsigned type, unsigned major, unsigned minor,
1424 const char* buf, int len, const char* prefix, int plen)
1425 {
1426 struct packet_callback_args* pca = (struct packet_callback_args*) arg;
1427 Eterm* hp;
1428 Eterm ver;
1429 Eterm bin = new_binary(pca->p, NULL, plen+len);
1430 byte* bin_ptr = binary_bytes(bin);
1431
1432 sys_memcpy(bin_ptr+plen, buf, len);
1433 if (plen) {
1434 sys_memcpy(bin_ptr, prefix, plen);
1435 }
1436
1437 /* {ssl_tls,NIL,ContentType,{Major,Minor},Bin} */
1438 hp = HAlloc(pca->p, 3+6);
1439 ver = TUPLE2(hp, make_small(major), make_small(minor));
1440 hp += 3;
1441 pca->res = TUPLE5(hp, am_ssl_tls, NIL, make_small(type), ver, bin);
1442 return 1;
1443 }
1444
1445
1446 PacketCallbacks packet_callbacks_erl = {
1447 http_response_erl,
1448 http_request_erl,
1449 http_eoh_erl,
1450 http_header_erl,
1451 http_error_erl,
1452 ssl_tls_erl
1453 };
1454
1455 /*
1456 decode_packet(Type,Bin,Options)
1457 Returns:
1458 {ok, PacketBodyBin, RestBin}
1459 {more, PacketSz | undefined}
1460 {error, invalid}
1461 */
decode_packet_3(BIF_ALIST_3)1462 BIF_RETTYPE decode_packet_3(BIF_ALIST_3)
1463 {
1464 unsigned max_plen = 0; /* Packet max length, 0=no limit */
1465 unsigned trunc_len = 0; /* Truncate lines if longer, 0=no limit */
1466 int http_state = 0; /* 0=request/response 1=header */
1467 int packet_sz; /*-------Binaries involved: ------------------*/
1468 byte* bin_ptr; /*| orig: original binary */
1469 byte bin_bitsz; /*| bin: BIF_ARG_2, may be sub-binary of orig */
1470 /*| packet: prefix of bin */
1471 char* body_ptr; /*| body: part of packet to return */
1472 int body_sz; /*| rest: bin without packet */
1473 struct packet_callback_args pca;
1474 enum PacketParseType type;
1475 Eterm* hp;
1476 Eterm* hend;
1477 ErlSubBin* rest;
1478 Eterm res;
1479 Eterm options;
1480 int code;
1481 char delimiter = '\n';
1482
1483 if (!is_binary(BIF_ARG_2) ||
1484 (!is_list(BIF_ARG_3) && !is_nil(BIF_ARG_3))) {
1485 BIF_ERROR(BIF_P, BADARG);
1486 }
1487 switch (BIF_ARG_1) {
1488 case make_small(0): case am_raw: type = TCP_PB_RAW; break;
1489 case make_small(1): type = TCP_PB_1; break;
1490 case make_small(2): type = TCP_PB_2; break;
1491 case make_small(4): type = TCP_PB_4; break;
1492 case am_asn1: type = TCP_PB_ASN1; break;
1493 case am_sunrm: type = TCP_PB_RM; break;
1494 case am_cdr: type = TCP_PB_CDR; break;
1495 case am_fcgi: type = TCP_PB_FCGI; break;
1496 case am_line: type = TCP_PB_LINE_LF; break;
1497 case am_tpkt: type = TCP_PB_TPKT; break;
1498 case am_http: type = TCP_PB_HTTP; break;
1499 case am_httph: type = TCP_PB_HTTPH; break;
1500 case am_http_bin: type = TCP_PB_HTTP_BIN; break;
1501 case am_httph_bin: type = TCP_PB_HTTPH_BIN; break;
1502 case am_ssl_tls: type = TCP_PB_SSL_TLS; break;
1503 default:
1504 BIF_ERROR(BIF_P, BADARG);
1505 }
1506
1507 options = BIF_ARG_3;
1508 while (!is_nil(options)) {
1509 Eterm* cons = list_val(options);
1510 if (is_tuple(CAR(cons))) {
1511 Eterm* tpl = tuple_val(CAR(cons));
1512 Uint val;
1513 if (tpl[0] == make_arityval(2) &&
1514 term_to_Uint(tpl[2],&val) && val <= UINT_MAX) {
1515 switch (tpl[1]) {
1516 case am_packet_size:
1517 max_plen = val;
1518 goto next_option;
1519 case am_line_length:
1520 trunc_len = val;
1521 goto next_option;
1522 case am_line_delimiter:
1523 if (type == TCP_PB_LINE_LF && val <= 255) {
1524 delimiter = (char)val;
1525 goto next_option;
1526 }
1527 }
1528 }
1529 }
1530 BIF_ERROR(BIF_P, BADARG);
1531
1532 next_option:
1533 options = CDR(cons);
1534 }
1535
1536
1537 pca.bin_sz = binary_size(BIF_ARG_2);
1538 ERTS_GET_BINARY_BYTES(BIF_ARG_2, bin_ptr, pca.bin_bitoffs, bin_bitsz);
1539 if (pca.bin_bitoffs != 0) {
1540 pca.aligned_ptr = erts_alloc(ERTS_ALC_T_TMP, pca.bin_sz);
1541 erts_copy_bits(bin_ptr, pca.bin_bitoffs, 1, pca.aligned_ptr, 0, 1, pca.bin_sz*8);
1542 }
1543 else {
1544 pca.aligned_ptr = bin_ptr;
1545 }
1546 packet_sz = packet_get_length(type, (char*)pca.aligned_ptr, pca.bin_sz,
1547 max_plen, trunc_len, delimiter, &http_state);
1548 if (!(packet_sz > 0 && packet_sz <= pca.bin_sz)) {
1549 if (packet_sz < 0) {
1550 goto error;
1551 }
1552 else { /* not enough data */
1553 Eterm plen = (packet_sz==0) ? am_undefined :
1554 erts_make_integer(packet_sz, BIF_P);
1555 Eterm* hp = HAlloc(BIF_P,3);
1556 res = TUPLE2(hp, am_more, plen);
1557 goto done;
1558 }
1559 }
1560 /* We got a whole packet */
1561
1562 body_ptr = (char*) pca.aligned_ptr;
1563 body_sz = packet_sz;
1564 packet_get_body(type, (const char**) &body_ptr, &body_sz);
1565
1566 ERTS_GET_REAL_BIN(BIF_ARG_2, pca.orig, pca.bin_offs, pca.bin_bitoffs, bin_bitsz);
1567 pca.p = BIF_P;
1568 pca.res = THE_NON_VALUE;
1569 pca.string_as_bin = (type == TCP_PB_HTTP_BIN || type == TCP_PB_HTTPH_BIN);
1570 code = packet_parse(type, (char*)pca.aligned_ptr, packet_sz, &http_state,
1571 &packet_callbacks_erl, &pca);
1572 if (code == 0) { /* no special packet parsing, make plain binary */
1573 ErlSubBin* body;
1574 Uint hsz = 2*ERL_SUB_BIN_SIZE + 4;
1575 hp = HAlloc(BIF_P, hsz);
1576 hend = hp + hsz;
1577
1578 body = (ErlSubBin *) hp;
1579 body->thing_word = HEADER_SUB_BIN;
1580 body->size = body_sz;
1581 body->offs = pca.bin_offs + (body_ptr - (char*)pca.aligned_ptr);
1582 body->orig = pca.orig;
1583 body->bitoffs = pca.bin_bitoffs;
1584 body->bitsize = 0;
1585 body->is_writable = 0;
1586 hp += ERL_SUB_BIN_SIZE;
1587 pca.res = make_binary(body);
1588 }
1589 else if (code > 0) {
1590 Uint hsz = ERL_SUB_BIN_SIZE + 4;
1591 ASSERT(pca.res != THE_NON_VALUE);
1592 hp = HAlloc(BIF_P, hsz);
1593 hend = hp + hsz;
1594 }
1595 else {
1596 error:
1597 hp = HAlloc(BIF_P,3);
1598 res = TUPLE2(hp, am_error, am_invalid);
1599 goto done;
1600 }
1601
1602 rest = (ErlSubBin *) hp;
1603 rest->thing_word = HEADER_SUB_BIN;
1604 rest->size = pca.bin_sz - packet_sz;
1605 rest->offs = pca.bin_offs + packet_sz;
1606 rest->orig = pca.orig;
1607 rest->bitoffs = pca.bin_bitoffs;
1608 rest->bitsize = bin_bitsz; /* The extra bits go into the rest. */
1609 rest->is_writable = 0;
1610 hp += ERL_SUB_BIN_SIZE;
1611 res = TUPLE3(hp, am_ok, pca.res, make_binary(rest));
1612 hp += 4;
1613 ASSERT(hp==hend); (void)hend;
1614
1615 done:
1616 if (pca.aligned_ptr != bin_ptr) {
1617 erts_free(ERTS_ALC_T_TMP, pca.aligned_ptr);
1618 }
1619 BIF_RET(res);
1620 }
1621
1622