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