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 #include "erl_osenv.h"
48 
49 static Port *open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump);
50 static int merge_global_environment(erts_osenv_t *env, Eterm key_value_pairs);
51 static char **convert_args(Eterm);
52 static void free_args(char **);
53 
54 char *erts_default_arg0 = "default";
55 
erts_internal_open_port_2(BIF_ALIST_2)56 BIF_RETTYPE erts_internal_open_port_2(BIF_ALIST_2)
57 {
58     BIF_RETTYPE ret;
59     Port *port;
60     Eterm res;
61     char *str;
62     int err_type, err_num;
63     ErtsLink *proc_lnk, *port_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 == -4) {
68             /* Invalid settings arguments. */
69             return am_badopt;
70         } else if (err_type == -3) {
71 	    ASSERT(err_num == BADARG || err_num == SYSTEM_LIMIT);
72 	    if (err_num == BADARG)
73                 res = am_badarg;
74             else if (err_num == SYSTEM_LIMIT)
75                 res = am_system_limit;
76             else
77                 /* this is only here to silence gcc, it should not happen */
78                 BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR);
79 	} else if (err_type == -2) {
80 	    str = erl_errno_id(err_num);
81             res = erts_atom_put((byte *) str, sys_strlen(str), ERTS_ATOM_ENC_LATIN1, 1);
82 	} else {
83 	    res = am_einval;
84 	}
85         BIF_RET(res);
86     }
87 
88     proc_lnk = erts_link_internal_create(ERTS_LNK_TYPE_PORT, port->common.id);
89     port_lnk = erts_link_internal_create(ERTS_LNK_TYPE_PORT, BIF_P->common.id);
90     /*
91      * This link should not already be present, but can potentially
92      * due to id wrapping...
93      */
94     if (!!erts_link_tree_lookup_insert(&ERTS_P_LINKS(BIF_P), proc_lnk))
95         erts_link_internal_release(proc_lnk);
96     erts_link_tree_insert(&ERTS_P_LINKS(port), port_lnk);
97 
98     if (port->drv_ptr->flags & ERL_DRV_FLAG_USE_INIT_ACK) {
99 
100         /* Copied from erl_port_task.c */
101         port->async_open_port = erts_alloc(ERTS_ALC_T_PRTSD,
102                                            sizeof(*port->async_open_port));
103         erts_make_ref_in_array(port->async_open_port->ref);
104         port->async_open_port->to = BIF_P->common.id;
105 
106         /*
107          * We unconditionaly *must* do a receive on a message
108          * containing the reference after this...
109          */
110         erts_msgq_set_save_end(BIF_P);
111 
112         res = erts_proc_store_ref(BIF_P, port->async_open_port->ref);
113     } else {
114         res = port->common.id;
115     }
116 
117     if (IS_TRACED_FL(BIF_P, F_TRACE_PROCS))
118         trace_proc(BIF_P, ERTS_PROC_LOCK_MAIN, BIF_P,
119                    am_link, port->common.id);
120 
121     ERTS_BIF_PREP_RET(ret, res);
122 
123     erts_port_release(port);
124 
125     return ret;
126 }
127 
128 static ERTS_INLINE Port *
lookup_port(Process * c_p,Eterm id_or_name,Uint32 invalid_flags)129 lookup_port(Process *c_p, Eterm id_or_name, Uint32 invalid_flags)
130 {
131     /* TODO: Implement nicer lookup in register... */
132     Eterm id;
133     if (is_atom(id_or_name))
134 	id = erts_whereis_name_to_id(c_p, id_or_name);
135     else
136 	id = id_or_name;
137     return erts_port_lookup(id, invalid_flags);
138 }
139 
140 static ERTS_INLINE Port *
sig_lookup_port(Process * c_p,Eterm id_or_name)141 sig_lookup_port(Process *c_p, Eterm id_or_name)
142 {
143     return lookup_port(c_p, id_or_name, ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP);
144 }
145 
146 /* Non-inline copy of sig_lookup_port to be exported */
erts_sig_lookup_port(Process * c_p,Eterm id_or_name)147 Port *erts_sig_lookup_port(Process *c_p, Eterm id_or_name)
148 {
149     return lookup_port(c_p, id_or_name, ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP);
150 }
151 
152 static ERTS_INLINE Port *
data_lookup_port(Process * c_p,Eterm id_or_name)153 data_lookup_port(Process *c_p, Eterm id_or_name)
154 {
155     return lookup_port(c_p, id_or_name, ERTS_PORT_SFLGS_INVALID_LOOKUP);
156 }
157 
158 /*
159  * erts_internal:port_command/3 is used by the
160  * erlang:port_command/2 and erlang:port_command/3
161  * BIFs.
162  */
163 
erts_internal_port_command_3(BIF_ALIST_3)164 BIF_RETTYPE erts_internal_port_command_3(BIF_ALIST_3)
165 {
166     BIF_RETTYPE res;
167     Port *prt;
168     int flags = 0;
169     erts_aint32_t state;
170 
171     if (is_not_nil(BIF_ARG_3)) {
172 	Eterm l = BIF_ARG_3;
173 	while (is_list(l)) {
174 	    Eterm* cons = list_val(l);
175 	    Eterm car = CAR(cons);
176 	    if (car == am_force)
177 		flags |= ERTS_PORT_SIG_FLG_FORCE;
178 	    else if (car == am_nosuspend)
179 		flags |= ERTS_PORT_SIG_FLG_NOSUSPEND;
180 	    else
181 		BIF_RET(am_badopt);
182 	    l = CDR(cons);
183 	}
184 	if (!is_nil(l))
185 	    BIF_RET(am_badopt);
186     }
187 
188     prt = sig_lookup_port(BIF_P, BIF_ARG_1);
189     if (!prt)
190         ERTS_BIF_PREP_RET(res, am_badarg);
191     else if ((flags & ERTS_PORT_SIG_FLG_FORCE)
192              && !(prt->drv_ptr->flags & ERL_DRV_FLAG_SOFT_BUSY)) {
193         ERTS_BIF_PREP_RET(res, am_notsup);
194     }
195     else {
196         Eterm ref;
197 #ifdef DEBUG
198         ref = NIL;
199 #endif
200 
201 	switch (erts_port_output(BIF_P, flags, prt, prt->common.id,
202 				 BIF_ARG_2, &ref)) {
203 	case ERTS_PORT_OP_BADARG:
204 	case ERTS_PORT_OP_DROPPED:
205 	    ERTS_BIF_PREP_RET(res, am_badarg);
206 	    break;
207 	case ERTS_PORT_OP_BUSY:
208 	    ASSERT(!(flags & ERTS_PORT_SIG_FLG_FORCE));
209 	    if (flags & ERTS_PORT_SIG_FLG_NOSUSPEND)
210 		ERTS_BIF_PREP_RET(res, am_false);
211 	    else {
212 		erts_suspend(BIF_P, ERTS_PROC_LOCK_MAIN, prt);
213 		ERTS_BIF_YIELD3(BIF_TRAP_EXPORT(BIF_erts_internal_port_command_3),
214 				BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
215 	    }
216 	    break;
217 	case ERTS_PORT_OP_BUSY_SCHEDULED:
218 	    ASSERT(!(flags & ERTS_PORT_SIG_FLG_FORCE));
219 	    /* Fall through... */
220 	case ERTS_PORT_OP_SCHEDULED:
221 	    ASSERT(is_internal_ordinary_ref(ref));
222 	    /* Signal order preserved by reply... */
223 	    BIF_RET(ref);
224 	    break;
225 	case ERTS_PORT_OP_DONE:
226 	    ERTS_BIF_PREP_RET(res, am_true);
227 	    break;
228 	default:
229 	    ERTS_INTERNAL_ERROR("Unexpected erts_port_output() result");
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_badtype;
520     }
521     else {
522 	retval = am_badtype;
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  * -4 if port settings were invalid.
726  */
727 
728 static Port *
open_port(Process * p,Eterm name,Eterm settings,int * err_typep,int * err_nump)729 open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump)
730 {
731     int merged_environment = 0;
732     Sint i;
733     Eterm option;
734     Uint arity;
735     Eterm* tp;
736     Uint* nargs;
737     erts_driver_t* driver;
738     char* name_buf = NULL;
739     SysDriverOpts opts;
740     Sint linebuf;
741     Eterm edir = NIL;
742     byte dir[MAXPATHLEN];
743     erts_aint32_t sflgs = 0;
744     Port *port;
745 
746     /* These are the defaults */
747     opts.packet_bytes = 0;
748     opts.use_stdio = 1;
749     opts.redir_stderr = 0;
750     opts.read_write = 0;
751     opts.hide_window = 0;
752     opts.wd = NULL;
753     opts.exit_status = 0;
754     opts.overlapped_io = 0;
755     opts.spawn_type = ERTS_SPAWN_ANY;
756     opts.argv = NULL;
757     opts.parallelism = erts_port_parallelism;
758     opts.high_watermark = 8192;
759     opts.low_watermark = opts.high_watermark / 2;
760     opts.port_watermarks_set = 0;
761     opts.msgq_watermarks_set = 0;
762     erts_osenv_init(&opts.envir);
763 
764     linebuf = 0;
765 
766     *err_nump = 0;
767 
768     if (is_not_list(settings) && is_not_nil(settings)) {
769 	goto bad_settings;
770     }
771 
772     /*
773      * Parse the settings.
774      */
775 
776     if (is_not_nil(settings)) {
777 	nargs = list_val(settings);
778 	while (1) {
779 	    if (is_tuple_arity(*nargs, 2)) {
780 		tp = tuple_val(*nargs);
781 		arity = *tp++;
782 		option = *tp++;
783 		if (option == am_packet) {
784 		    if (is_not_small(*tp)) {
785 			goto bad_settings;
786 		    }
787 		    opts.packet_bytes = signed_val(*tp);
788 		    switch (opts.packet_bytes) {
789 		    case 1:
790 		    case 2:
791 		    case 4:
792 			break;
793 		    default:
794 			goto bad_settings;
795 		   }
796 		} else if (option == am_line) {
797 		    if (is_not_small(*tp)) {
798 			goto bad_settings;
799 		    }
800 		    linebuf = signed_val(*tp);
801 		    if (linebuf <= 0) {
802 			goto bad_settings;
803 		    }
804 		} else if (option == am_env) {
805 		    if (merged_environment) {
806 		        /* Ignore previous env option */
807 		        erts_osenv_clear(&opts.envir);
808 		    }
809 
810 		    merged_environment = 1;
811 
812 		    if (merge_global_environment(&opts.envir, *tp)) {
813 		        goto bad_settings;
814 		    }
815 		} else if (option == am_args) {
816 		    char **av;
817 		    char **oav = opts.argv;
818 		    if ((av = convert_args(*tp)) == NULL) {
819 			goto bad_settings;
820 		    }
821 		    opts.argv = av;
822 		    if (oav) {
823 			opts.argv[0] = oav[0];
824 			oav[0] = erts_default_arg0;
825 			free_args(oav);
826 		    }
827 
828 		} else if (option == am_arg0) {
829 		    char *a0;
830 
831 		    if ((a0 = erts_convert_filename_to_native(*tp, NULL, 0, ERTS_ALC_T_TMP, 1, 1, NULL)) == NULL) {
832 			goto bad_settings;
833 		    }
834 		    if (opts.argv == NULL) {
835 			opts.argv = erts_alloc(ERTS_ALC_T_TMP,
836 					       2 * sizeof(char **));
837 			opts.argv[0] = a0;
838 			opts.argv[1] = NULL;
839 		    } else {
840 			if (opts.argv[0] != erts_default_arg0) {
841 			    erts_free(ERTS_ALC_T_TMP, opts.argv[0]);
842 			}
843 			opts.argv[0] = a0;
844 		    }
845 		} else if (option == am_cd) {
846 		    edir = *tp;
847 		} else if (option == am_parallelism) {
848 		    if (*tp == am_true)
849 			opts.parallelism = 1;
850 		    else if (*tp == am_false)
851 			opts.parallelism = 0;
852 		    else
853 			goto bad_settings;
854                 } else if (option == am_busy_limits_port) {
855                     Uint high, low;
856                     if (*tp == am_disabled)
857                         low = high = ERL_DRV_BUSY_MSGQ_DISABLED;
858                     else if (!is_tuple_arity(*tp, 2))
859                         goto bad_settings;
860                     else {
861                         Eterm *wtp = tuple_val(*tp);
862                         if (!term_to_Uint(wtp[1], &low))
863                             goto bad_settings;
864                         if (!term_to_Uint(wtp[2], &high))
865                             goto bad_settings;
866                         if (high < ERL_DRV_BUSY_MSGQ_LIM_MIN)
867                             goto bad_settings;
868                         if (high > ERL_DRV_BUSY_MSGQ_LIM_MAX)
869                             goto bad_settings;
870                         if (low < ERL_DRV_BUSY_MSGQ_LIM_MIN)
871                             goto bad_settings;
872                         if (low > ERL_DRV_BUSY_MSGQ_LIM_MAX)
873                             goto bad_settings;
874                         if (high == ~((Uint) 0) || low == ~((Uint) 0))
875                             goto bad_settings;
876                         if (low > high)
877                             low = high;
878                     }
879                     opts.low_watermark = low;
880                     opts.high_watermark = high;
881                     opts.port_watermarks_set = !0;
882                 } else if (option == am_busy_limits_msgq) {
883                     Uint high, low;
884                     if (*tp == am_disabled)
885                         low = high = ERL_DRV_BUSY_MSGQ_DISABLED;
886                     else if (!is_tuple_arity(*tp, 2))
887                         goto bad_settings;
888                     else {
889                         Eterm *wtp = tuple_val(*tp);
890                         if (!term_to_Uint(wtp[1], &low))
891                             goto bad_settings;
892                         if (!term_to_Uint(wtp[2], &high))
893                             goto bad_settings;
894                         if (high < ERL_DRV_BUSY_MSGQ_LIM_MIN)
895                             goto bad_settings;
896                         if (high > ERL_DRV_BUSY_MSGQ_LIM_MAX)
897                             goto bad_settings;
898                         if (low < ERL_DRV_BUSY_MSGQ_LIM_MIN)
899                             goto bad_settings;
900                         if (low > ERL_DRV_BUSY_MSGQ_LIM_MAX)
901                             goto bad_settings;
902                         if (high == ~((Uint) 0) || low == ~((Uint) 0))
903                             goto bad_settings;
904                         if (low > high)
905                             low = high;
906                     }
907                     opts.low_msgq_watermark = low;
908                     opts.high_msgq_watermark = high;
909                     opts.msgq_watermarks_set = !0;
910 		} else {
911 		    goto bad_settings;
912 		}
913 	    } else if (*nargs == am_stream) {
914 		opts.packet_bytes = 0;
915 	    } else if (*nargs == am_use_stdio) {
916 		opts.use_stdio = 1;
917 	    } else if (*nargs == am_stderr_to_stdout) {
918 		opts.redir_stderr = 1;
919 	    } else if (*nargs == am_line) {
920 		linebuf = 512;
921 	    } else if (*nargs == am_nouse_stdio) {
922 		opts.use_stdio = 0;
923 	    } else if (*nargs == am_binary) {
924 		sflgs |= ERTS_PORT_SFLG_BINARY_IO;
925 	    } else if (*nargs == am_in) {
926 		opts.read_write |= DO_READ;
927 	    } else if (*nargs == am_out) {
928 		opts.read_write |= DO_WRITE;
929 	    } else if (*nargs == am_eof) {
930 		sflgs |= ERTS_PORT_SFLG_SOFT_EOF;
931 	    } else if (*nargs == am_hide) {
932 		opts.hide_window = 1;
933 	    } else if (*nargs == am_exit_status) {
934 		opts.exit_status = 1;
935 	    } else if (*nargs == am_overlapped_io) {
936 		opts.overlapped_io = 1;
937 	    } else {
938 		goto bad_settings;
939 	    }
940 	    if (is_nil(*++nargs))
941 		break;
942 	    if (is_not_list(*nargs)) {
943 		goto bad_settings;
944 	    }
945 	    nargs = list_val(*nargs);
946 	}
947     }
948 
949     if (opts.read_write == 0)	/* implement default */
950 	opts.read_write = DO_READ|DO_WRITE;
951 
952     /* Mutually exclusive arguments. */
953     if((linebuf && opts.packet_bytes) ||
954        (opts.redir_stderr && !opts.use_stdio)) {
955 	goto bad_settings;
956     }
957 
958     /* If we lacked an env option, fill in the global environment without
959      * changes. */
960     if (!merged_environment) {
961         merge_global_environment(&opts.envir, NIL);
962     }
963 
964     /*
965      * Parse the first argument and start the appropriate driver.
966      */
967 
968     if (is_atom(name) || (i = is_string(name))) {
969 	/* a vanilla port */
970 	if (is_atom(name)) {
971 	    name_buf = (char *) erts_alloc(ERTS_ALC_T_TMP,
972 					   atom_tab(atom_val(name))->len+1);
973 	    sys_memcpy((void *) name_buf,
974 		       (void *) atom_tab(atom_val(name))->name,
975 		       atom_tab(atom_val(name))->len);
976 	    name_buf[atom_tab(atom_val(name))->len] = '\0';
977 	} else {
978 	    name_buf = (char *) erts_alloc(ERTS_ALC_T_TMP, i + 1);
979 	    if (intlist_to_buf(name, name_buf, i) != i)
980 		erts_exit(ERTS_ERROR_EXIT, "%s:%d: Internal error\n", __FILE__, __LINE__);
981 	    name_buf[i] = '\0';
982 	}
983 	driver = &vanilla_driver;
984     } else {
985 	if (is_not_tuple(name)) {
986 	    goto badarg;		/* Not a process or fd port */
987 	}
988 	tp = tuple_val(name);
989 	arity = *tp++;
990 
991 	if (arity == make_arityval(0)) {
992 	    goto badarg;
993 	}
994 
995 	if (*tp == am_spawn || *tp == am_spawn_driver || *tp == am_spawn_executable) {	/* A process port */
996 	    int encoding;
997 	    if (arity != make_arityval(2)) {
998 		goto badarg;
999 	    }
1000 	    name = tp[1];
1001 	    encoding = erts_get_native_filename_encoding();
1002 	    /* Do not convert the command to utf-16le yet, do that in win32 specific code */
1003 	    /* since the cmd is used for comparsion with drivers names and copied to port info */
1004 	    if (encoding == ERL_FILENAME_WIN_WCHAR) {
1005 		encoding = ERL_FILENAME_UTF8;
1006 	    }
1007 	    if ((name_buf = erts_convert_filename_to_encoding(name, NULL, 0, ERTS_ALC_T_TMP,0,1, encoding, NULL, 0))
1008 		== NULL) {
1009 		goto badarg;
1010 	    }
1011 
1012 	    if (*tp == am_spawn_driver) {
1013 		opts.spawn_type = ERTS_SPAWN_DRIVER;
1014 	    } else if (*tp == am_spawn_executable) {
1015 		opts.spawn_type = ERTS_SPAWN_EXECUTABLE;
1016 	    }
1017 
1018 	    driver = &spawn_driver;
1019 	} else if (*tp == am_fd) { /* An fd port */
1020 	    if (arity != make_arityval(3)) {
1021 		goto badarg;
1022 	    }
1023 	    if (is_not_small(tp[1]) || is_not_small(tp[2])) {
1024 		goto badarg;
1025 	    }
1026 	    opts.ifd = unsigned_val(tp[1]);
1027 	    opts.ofd = unsigned_val(tp[2]);
1028 
1029             /* Syntesize name from input and output descriptor. */
1030             name_buf = erts_alloc(ERTS_ALC_T_TMP, 256);
1031             erts_snprintf(name_buf, 256, "%i/%i", opts.ifd, opts.ofd);
1032 
1033 	    driver = &fd_driver;
1034 	} else {
1035 	    goto badarg;
1036 	}
1037     }
1038 
1039     if ((driver != &spawn_driver && opts.argv != NULL) ||
1040 	(driver == &spawn_driver &&
1041 	 opts.spawn_type != ERTS_SPAWN_EXECUTABLE &&
1042 	 opts.argv != NULL)) {
1043 	/* Argument vector only if explicit spawn_executable */
1044 	goto badarg;
1045     }
1046 
1047     if (edir != NIL) {
1048 	if ((opts.wd = erts_convert_filename_to_native(edir, NULL, 0, ERTS_ALC_T_TMP,0,1,NULL)) == NULL) {
1049 	    goto badarg;
1050 	}
1051     }
1052 
1053     if (driver != &spawn_driver && opts.exit_status) {
1054 	goto badarg;
1055     }
1056 
1057     if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) {
1058         trace_sched(p, ERTS_PROC_LOCK_MAIN, am_out);
1059     }
1060 
1061 
1062     erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
1063 
1064     port = erts_open_driver(driver, p->common.id, name_buf, &opts, err_typep, err_nump);
1065 #ifdef USE_VM_PROBES
1066     if (port && DTRACE_ENABLED(port_open)) {
1067         DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE);
1068         DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE);
1069 
1070         dtrace_proc_str(p, process_str);
1071         erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", port->common.id);
1072         DTRACE3(port_open, process_str, name_buf, port_str);
1073     }
1074 #endif
1075 
1076     if (port && IS_TRACED_FL(port, F_TRACE_PORTS))
1077         trace_port(port, am_getting_linked, p->common.id);
1078 
1079     erts_proc_lock(p, ERTS_PROC_LOCK_MAIN);
1080 
1081     if (IS_TRACED_FL(p, F_TRACE_SCHED_PROCS)) {
1082         trace_sched(p, ERTS_PROC_LOCK_MAIN, am_in);
1083     }
1084 
1085     if (!port) {
1086 	DEBUGF(("open_driver returned (%d:%d)\n",
1087 		err_typep ? *err_typep : 4711,
1088 		err_nump ? *err_nump : 4711));
1089 	goto do_return;
1090     }
1091 
1092     if (linebuf && port->linebuf == NULL){
1093 	port->linebuf = allocate_linebuf(linebuf);
1094 	sflgs |= ERTS_PORT_SFLG_LINEBUF_IO;
1095     }
1096 
1097     if (sflgs)
1098 	erts_atomic32_read_bor_relb(&port->state, sflgs);
1099 
1100  do_return:
1101     erts_osenv_clear(&opts.envir);
1102     if (name_buf)
1103 	erts_free(ERTS_ALC_T_TMP, (void *) name_buf);
1104     if (opts.argv) {
1105 	free_args(opts.argv);
1106     }
1107     if (opts.wd && opts.wd != ((char *)dir)) {
1108 	erts_free(ERTS_ALC_T_TMP, (void *) opts.wd);
1109     }
1110     return port;
1111 
1112  bad_settings:
1113     if (err_typep)
1114 	*err_typep = -4;
1115     port = NULL;
1116     goto do_return;
1117 
1118  badarg:
1119     if (err_typep)
1120 	*err_typep = -3;
1121     if (err_nump)
1122 	*err_nump = BADARG;
1123     port = NULL;
1124     goto do_return;
1125 }
1126 
1127 /* Merges the the global environment and the given {Key, Value} list into env,
1128  * unsetting all keys whose value is either 'false' or NIL. The behavior on
1129  * NIL is undocumented and perhaps surprising, but the previous implementation
1130  * worked in this manner. */
merge_global_environment(erts_osenv_t * env,Eterm key_value_pairs)1131 static int merge_global_environment(erts_osenv_t *env, Eterm key_value_pairs) {
1132     const erts_osenv_t *global_env = erts_sys_rlock_global_osenv();
1133     erts_osenv_merge(env, global_env, 0);
1134     erts_sys_runlock_global_osenv();
1135 
1136     while (is_list(key_value_pairs)) {
1137         Eterm *cell, *tuple;
1138 
1139         cell = list_val(key_value_pairs);
1140 
1141         if(!is_tuple_arity(CAR(cell), 2)) {
1142             return -1;
1143         }
1144 
1145         tuple = tuple_val(CAR(cell));
1146         key_value_pairs = CDR(cell);
1147 
1148         if(is_nil(tuple[2]) || tuple[2] == am_false) {
1149             if(erts_osenv_unset_term(env, tuple[1]) < 0) {
1150                 return -1;
1151             }
1152         } else {
1153             if(erts_osenv_put_term(env, tuple[1], tuple[2]) < 0) {
1154                 return -1;
1155             }
1156         }
1157     }
1158 
1159     if(!is_nil(key_value_pairs)) {
1160         return -1;
1161     }
1162 
1163     return 0;
1164 }
1165 
1166 /* Arguments can be given i unicode and as raw binaries, convert filename is used to convert */
convert_args(Eterm l)1167 static char **convert_args(Eterm l)
1168 {
1169     char **pp;
1170     char *b;
1171     Sint n;
1172     Sint i = 0;
1173     Eterm str;
1174     if (is_not_list(l) && is_not_nil(l)) {
1175 	return NULL;
1176     }
1177 
1178     n = erts_list_length(l);
1179     if (n < 0)
1180         return NULL;
1181     /* We require at least one element in argv[0] + NULL at end */
1182     pp = erts_alloc(ERTS_ALC_T_TMP, (n + 2) * sizeof(char **));
1183     pp[i++] = erts_default_arg0;
1184     while (is_list(l)) {
1185 	str = CAR(list_val(l));
1186 	if ((b = erts_convert_filename_to_native(str,NULL,0,ERTS_ALC_T_TMP,1,1,NULL)) == NULL) {
1187 	    int j;
1188 	    for (j = 1; j < i; ++j)
1189 		erts_free(ERTS_ALC_T_TMP, pp[j]);
1190 	    erts_free(ERTS_ALC_T_TMP, pp);
1191 	    return NULL;
1192 	}
1193 	pp[i++] = b;
1194 	l = CDR(list_val(l));
1195     }
1196     pp[i] = NULL;
1197     return pp;
1198 }
1199 
free_args(char ** av)1200 static void free_args(char **av)
1201 {
1202     int i;
1203     if (av == NULL)
1204 	return;
1205     for (i = 0; av[i] != NULL; ++i) {
1206 	if (av[i] != erts_default_arg0) {
1207 	    erts_free(ERTS_ALC_T_TMP, av[i]);
1208 	}
1209     }
1210     erts_free(ERTS_ALC_T_TMP, av);
1211 }
1212 
1213 /* ------------ decode_packet() and friends: */
1214 
1215 struct packet_callback_args
1216 {
1217     Process* p;  /* In */
1218     Eterm res;   /* Out */
1219     int string_as_bin; /* return strings as binaries (http_bin): */
1220     byte* aligned_ptr;
1221     Uint bin_sz;
1222     Eterm orig;
1223     Uint bin_offs;
1224     byte bin_bitoffs;
1225 };
1226 
1227 #define in_area(ptr,start,nbytes) \
1228     ((UWord)((char*)(ptr) - (char*)(start)) < (nbytes))
1229 
1230 static Eterm
http_bld_string(struct packet_callback_args * pca,Uint ** hpp,Uint * szp,const char * str,Sint len)1231 http_bld_string(struct packet_callback_args* pca, Uint **hpp, Uint *szp,
1232 		const char *str, Sint len)
1233 {
1234     Eterm res = THE_NON_VALUE;
1235     Uint size;
1236     int make_subbin;
1237 
1238     if (pca->string_as_bin) {
1239 	size = heap_bin_size(len);
1240 	make_subbin = (size > ERL_SUB_BIN_SIZE
1241 		       && in_area(str, pca->aligned_ptr, pca->bin_sz));
1242 	if (szp) {
1243 	    *szp += make_subbin ? ERL_SUB_BIN_SIZE : size;
1244 	}
1245 	if (hpp) {
1246 	    res = make_binary(*hpp);
1247 	    if (make_subbin) {
1248 		ErlSubBin* bin = (ErlSubBin*) *hpp;
1249 		bin->thing_word = HEADER_SUB_BIN;
1250 		bin->size = len;
1251 		bin->offs = pca->bin_offs + ((byte*)str - pca->aligned_ptr);
1252 		bin->orig = pca->orig;
1253 		bin->bitoffs = pca->bin_bitoffs;
1254 		bin->bitsize = 0;
1255 		bin->is_writable = 0;
1256 		*hpp += ERL_SUB_BIN_SIZE;
1257 	    }
1258 	    else {
1259 		ErlHeapBin* bin = (ErlHeapBin*) *hpp;
1260 		bin->thing_word = header_heap_bin(len);
1261 		bin->size = len;
1262 		sys_memcpy(bin->data, str, len);
1263 		*hpp += size;
1264 	    }
1265 	}
1266     }
1267     else {
1268 	res = erts_bld_string_n(hpp, szp, str, len);
1269     }
1270     return res;
1271 }
1272 
http_response_erl(void * arg,int major,int minor,int status,const char * phrase,int phrase_len)1273 static int http_response_erl(void *arg, int major, int minor,
1274                              int status, const char* phrase, int phrase_len)
1275 {
1276     /* {http_response,{Major,Minor},Status,"Phrase"} */
1277     struct packet_callback_args* pca = (struct packet_callback_args*) arg;
1278     Eterm phrase_term, ver;
1279     Uint hsize = 3 + 5;
1280     Eterm* hp;
1281 #ifdef DEBUG
1282     Eterm* hend;
1283 #endif
1284 
1285     http_bld_string(pca, NULL, &hsize, phrase, phrase_len);
1286     hp = HAlloc(pca->p, hsize);
1287 #ifdef DEBUG
1288     hend = hp + hsize;
1289 #endif
1290     phrase_term = http_bld_string(pca, &hp, NULL, phrase, phrase_len);
1291     ver = TUPLE2(hp, make_small(major), make_small(minor));
1292     hp += 3;
1293     pca->res = TUPLE4(hp, am_http_response, ver, make_small(status), phrase_term);
1294     ASSERT(hp+5==hend);
1295     return 1;
1296 }
1297 
http_bld_uri(struct packet_callback_args * pca,Eterm ** hpp,Uint * szp,const PacketHttpURI * uri)1298 static Eterm http_bld_uri(struct packet_callback_args* pca,
1299 			  Eterm** hpp, Uint* szp, const PacketHttpURI* uri)
1300 {
1301     Eterm s1, s2;
1302     if (uri->type == URI_STAR) {
1303         return am_Times; /* '*' */
1304     }
1305 
1306     s1 = http_bld_string(pca, hpp, szp, uri->s1_ptr, uri->s1_len);
1307 
1308     switch (uri->type) {
1309     case URI_ABS_PATH:
1310         return erts_bld_tuple(hpp, szp, 2, am_abs_path, s1);
1311     case URI_HTTP:
1312     case URI_HTTPS:
1313         s2 = http_bld_string(pca, hpp, szp, uri->s2_ptr, uri->s2_len);
1314         return erts_bld_tuple
1315             (hpp, szp, 5, am_absoluteURI,
1316              ((uri->type==URI_HTTP) ? am_http : am_https),
1317              s1,
1318              ((uri->port==0) ? am_undefined : make_small(uri->port)),
1319              s2);
1320 
1321     case URI_STRING:
1322         return s1;
1323     case URI_SCHEME:
1324         s2 = http_bld_string(pca, hpp, szp, uri->s2_ptr, uri->s2_len);
1325         return erts_bld_tuple(hpp, szp, 3, am_scheme, s1, s2);
1326 
1327     default:
1328         erts_exit(ERTS_ERROR_EXIT, "%s, line %d: type=%u\n", __FILE__, __LINE__, uri->type);
1329     }
1330 }
1331 
http_request_erl(void * arg,const http_atom_t * meth,const char * meth_ptr,int meth_len,const PacketHttpURI * uri,int major,int minor)1332 static int http_request_erl(void* arg, const http_atom_t* meth,
1333                             const char* meth_ptr, int meth_len,
1334                             const PacketHttpURI* uri, int major, int minor)
1335 {
1336     struct packet_callback_args* pca = (struct packet_callback_args*) arg;
1337     Eterm meth_term, uri_term, ver_term;
1338     Uint sz = 0;
1339     Uint* szp = &sz;
1340     Eterm* hp;
1341     Eterm** hpp = NULL;
1342 
1343     /* {http_request,Meth,Uri,Version} */
1344 
1345     for (;;) {
1346         meth_term = (meth!=NULL) ? meth->atom :
1347 	    http_bld_string(pca, hpp, szp, meth_ptr, meth_len);
1348         uri_term = http_bld_uri(pca, hpp, szp, uri);
1349         ver_term = erts_bld_tuple(hpp, szp, 2,
1350                                   make_small(major), make_small(minor));
1351         pca->res = erts_bld_tuple(hpp, szp, 4, am_http_request, meth_term,
1352                                   uri_term, ver_term);
1353         if (hpp != NULL) break;
1354         hpp = &hp;
1355         hp = HAlloc(pca->p, sz);
1356         szp = NULL;
1357     }
1358     return 1;
1359 }
1360 
1361 static int
http_header_erl(void * arg,const http_atom_t * name,const char * name_ptr,int name_len,const char * oname_ptr,int oname_len,const char * value_ptr,int value_len)1362 http_header_erl(void* arg, const http_atom_t* name,
1363 		const char* name_ptr, int name_len,
1364 		const char* oname_ptr, int oname_len,
1365 		const char* value_ptr, int value_len)
1366 {
1367     struct packet_callback_args* pca = (struct packet_callback_args*) arg;
1368     Eterm bit_term, name_term, oname_term, val_term;
1369     Uint sz = 6;
1370     Eterm* hp;
1371 #ifdef DEBUG
1372     Eterm* hend;
1373 #endif
1374 
1375     /* {http_header,Bit,Name,Oname,Value} */
1376 
1377     if (name == NULL) {
1378 	http_bld_string(pca, NULL, &sz, name_ptr, name_len);
1379     }
1380     http_bld_string(pca, NULL, &sz, oname_ptr, oname_len);
1381     http_bld_string(pca, NULL, &sz, value_ptr, value_len);
1382 
1383     hp = HAlloc(pca->p, sz);
1384 #ifdef DEBUG
1385     hend = hp + sz;
1386 #endif
1387 
1388     if (name != NULL) {
1389 	bit_term = make_small(name->index+1);
1390 	name_term = name->atom;
1391     }
1392     else {
1393 	bit_term = make_small(0);
1394 	name_term = http_bld_string(pca, &hp,NULL,name_ptr,name_len);
1395     }
1396 
1397     oname_term = http_bld_string(pca, &hp, NULL, oname_ptr, oname_len);
1398     val_term = http_bld_string(pca, &hp, NULL, value_ptr, value_len);
1399     pca->res = TUPLE5(hp, am_http_header, bit_term, name_term, oname_term, val_term);
1400     ASSERT(hp+6==hend);
1401     return 1;
1402 }
1403 
http_eoh_erl(void * arg)1404 static int http_eoh_erl(void* arg)
1405 {
1406     /* http_eoh */
1407     struct packet_callback_args* pca = (struct packet_callback_args*) arg;
1408     pca->res = am_http_eoh;
1409     return 1;
1410 }
1411 
http_error_erl(void * arg,const char * buf,int len)1412 static int http_error_erl(void* arg, const char* buf, int len)
1413 {
1414     /* {http_error,Line} */
1415     struct packet_callback_args* pca = (struct packet_callback_args*) arg;
1416     Uint sz = 3;
1417     Eterm* hp;
1418 #ifdef DEBUG
1419     Eterm* hend;
1420 #endif
1421 
1422     http_bld_string(pca, NULL, &sz, buf, len);
1423 
1424     hp = HAlloc(pca->p, sz);
1425 #ifdef DEBUG
1426     hend = hp + sz;
1427 #endif
1428     pca->res = erts_bld_tuple(&hp, NULL, 2, am_http_error,
1429 			      http_bld_string(pca, &hp, NULL, buf, len));
1430     ASSERT(hp==hend);
1431     return 1;
1432 }
1433 
1434 static
ssl_tls_erl(void * arg,unsigned type,unsigned major,unsigned minor,const char * buf,int len,const char * prefix,int plen)1435 int ssl_tls_erl(void* arg, unsigned type, unsigned major, unsigned minor,
1436 		const char* buf, int len, const char* prefix, int plen)
1437 {
1438     struct packet_callback_args* pca = (struct packet_callback_args*) arg;
1439     Eterm* hp;
1440     Eterm ver;
1441     Eterm bin = new_binary(pca->p, NULL, plen+len);
1442     byte* bin_ptr = binary_bytes(bin);
1443 
1444     sys_memcpy(bin_ptr+plen, buf, len);
1445     if (plen) {
1446         sys_memcpy(bin_ptr, prefix, plen);
1447     }
1448 
1449     /* {ssl_tls,NIL,ContentType,{Major,Minor},Bin} */
1450     hp = HAlloc(pca->p, 3+6);
1451     ver = TUPLE2(hp, make_small(major), make_small(minor));
1452     hp += 3;
1453     pca->res = TUPLE5(hp, am_ssl_tls, NIL, make_small(type), ver, bin);
1454     return 1;
1455 }
1456 
1457 
1458 PacketCallbacks packet_callbacks_erl = {
1459     http_response_erl,
1460     http_request_erl,
1461     http_eoh_erl,
1462     http_header_erl,
1463     http_error_erl,
1464     ssl_tls_erl
1465 };
1466 
1467 /*
1468  decode_packet(Type,Bin,Options)
1469  Returns:
1470      {ok, PacketBodyBin, RestBin}
1471      {more, PacketSz | undefined}
1472      {error, invalid}
1473 */
decode_packet_3(BIF_ALIST_3)1474 BIF_RETTYPE decode_packet_3(BIF_ALIST_3)
1475 {
1476     unsigned max_plen = 0;   /* Packet max length, 0=no limit */
1477     unsigned trunc_len = 0;  /* Truncate lines if longer, 0=no limit */
1478     int http_state = 0;      /* 0=request/response 1=header */
1479     int packet_sz;           /*-------Binaries involved: ------------------*/
1480     byte* bin_ptr;           /*| orig: original binary                     */
1481     byte bin_bitsz;          /*| bin: BIF_ARG_2, may be sub-binary of orig */
1482 	                     /*| packet: prefix of bin                     */
1483     char* body_ptr;          /*| body: part of packet to return            */
1484     int body_sz;             /*| rest: bin without packet                  */
1485     struct packet_callback_args pca;
1486     enum PacketParseType type;
1487     Eterm* hp;
1488     Eterm* hend;
1489     ErlSubBin* rest;
1490     Eterm res;
1491     Eterm options;
1492     int   code;
1493     char  delimiter = '\n';
1494 
1495     switch (BIF_ARG_1) {
1496     case make_small(0): case am_raw: type = TCP_PB_RAW; break;
1497     case make_small(1): type = TCP_PB_1; break;
1498     case make_small(2): type = TCP_PB_2; break;
1499     case make_small(4): type = TCP_PB_4; break;
1500     case am_asn1: type = TCP_PB_ASN1; break;
1501     case am_sunrm: type = TCP_PB_RM; break;
1502     case am_cdr: type = TCP_PB_CDR; break;
1503     case am_fcgi: type = TCP_PB_FCGI; break;
1504     case am_line: type = TCP_PB_LINE_LF; break;
1505     case am_tpkt: type = TCP_PB_TPKT; break;
1506     case am_http: type = TCP_PB_HTTP; break;
1507     case am_httph: type = TCP_PB_HTTPH; break;
1508     case am_http_bin: type = TCP_PB_HTTP_BIN; break;
1509     case am_httph_bin: type = TCP_PB_HTTPH_BIN; break;
1510     case am_ssl_tls: type = TCP_PB_SSL_TLS; break;
1511     default:
1512         BIF_P->fvalue = am_badopt;
1513         BIF_ERROR(BIF_P, BADARG | EXF_HAS_EXT_INFO);
1514     }
1515 
1516     if (!is_binary(BIF_ARG_2) ||
1517         (!is_list(BIF_ARG_3) && !is_nil(BIF_ARG_3))) {
1518         BIF_ERROR(BIF_P, BADARG);
1519     }
1520 
1521     options = BIF_ARG_3;
1522     while (!is_nil(options)) {
1523         Eterm* cons = list_val(options);
1524         if (is_tuple(CAR(cons))) {
1525             Eterm* tpl = tuple_val(CAR(cons));
1526             Uint val;
1527             if (tpl[0] == make_arityval(2) &&
1528 		term_to_Uint(tpl[2],&val) && val <= UINT_MAX) {
1529                 switch (tpl[1]) {
1530                 case am_packet_size:
1531                     max_plen = val;
1532                     goto next_option;
1533                 case am_line_length:
1534                     trunc_len = val;
1535                     goto next_option;
1536                 case am_line_delimiter:
1537                     if (type == TCP_PB_LINE_LF && val <= 255) {
1538                         delimiter = (char)val;
1539                         goto next_option;
1540                     }
1541                 }
1542             }
1543         }
1544         BIF_ERROR(BIF_P, BADARG);
1545 
1546     next_option:
1547         options = CDR(cons);
1548     }
1549 
1550 
1551     pca.bin_sz = binary_size(BIF_ARG_2);
1552     ERTS_GET_BINARY_BYTES(BIF_ARG_2, bin_ptr, pca.bin_bitoffs, bin_bitsz);
1553     if (pca.bin_bitoffs != 0) {
1554         pca.aligned_ptr = erts_alloc(ERTS_ALC_T_TMP, pca.bin_sz);
1555         erts_copy_bits(bin_ptr, pca.bin_bitoffs, 1, pca.aligned_ptr, 0, 1, pca.bin_sz*8);
1556     }
1557     else {
1558         pca.aligned_ptr = bin_ptr;
1559     }
1560     packet_sz = packet_get_length(type, (char*)pca.aligned_ptr, pca.bin_sz,
1561                                   max_plen, trunc_len, delimiter, &http_state);
1562     if (!(packet_sz > 0 && packet_sz <= pca.bin_sz)) {
1563         if (packet_sz < 0) {
1564 	    goto error;
1565         }
1566         else { /* not enough data */
1567             Eterm plen = (packet_sz==0) ? am_undefined :
1568                 erts_make_integer(packet_sz, BIF_P);
1569             Eterm* hp = HAlloc(BIF_P,3);
1570             res = TUPLE2(hp, am_more, plen);
1571             goto done;
1572         }
1573     }
1574     /* We got a whole packet */
1575 
1576     body_ptr = (char*) pca.aligned_ptr;
1577     body_sz = packet_sz;
1578     packet_get_body(type, (const char**) &body_ptr, &body_sz);
1579 
1580     ERTS_GET_REAL_BIN(BIF_ARG_2, pca.orig, pca.bin_offs, pca.bin_bitoffs, bin_bitsz);
1581     pca.p = BIF_P;
1582     pca.res = THE_NON_VALUE;
1583     pca.string_as_bin = (type == TCP_PB_HTTP_BIN || type == TCP_PB_HTTPH_BIN);
1584     code = packet_parse(type, (char*)pca.aligned_ptr, packet_sz, &http_state,
1585 			&packet_callbacks_erl, &pca);
1586     if (code == 0) { /* no special packet parsing, make plain binary */
1587         ErlSubBin* body;
1588         Uint hsz = 2*ERL_SUB_BIN_SIZE + 4;
1589         hp = HAlloc(BIF_P, hsz);
1590         hend = hp + hsz;
1591 
1592         body = (ErlSubBin *) hp;
1593         body->thing_word = HEADER_SUB_BIN;
1594         body->size = body_sz;
1595         body->offs = pca.bin_offs + (body_ptr - (char*)pca.aligned_ptr);
1596         body->orig = pca.orig;
1597         body->bitoffs = pca.bin_bitoffs;
1598         body->bitsize = 0;
1599         body->is_writable = 0;
1600         hp += ERL_SUB_BIN_SIZE;
1601         pca.res = make_binary(body);
1602     }
1603     else if (code > 0) {
1604 	Uint hsz = ERL_SUB_BIN_SIZE + 4;
1605 	ASSERT(pca.res != THE_NON_VALUE);
1606 	hp = HAlloc(BIF_P, hsz);
1607 	hend = hp + hsz;
1608     }
1609     else {
1610 error:
1611 	hp = HAlloc(BIF_P,3);
1612 	res = TUPLE2(hp, am_error, am_invalid);
1613 	goto done;
1614     }
1615 
1616     rest = (ErlSubBin *) hp;
1617     rest->thing_word = HEADER_SUB_BIN;
1618     rest->size = pca.bin_sz - packet_sz;
1619     rest->offs = pca.bin_offs + packet_sz;
1620     rest->orig = pca.orig;
1621     rest->bitoffs = pca.bin_bitoffs;
1622     rest->bitsize = bin_bitsz;   /* The extra bits go into the rest. */
1623     rest->is_writable = 0;
1624     hp += ERL_SUB_BIN_SIZE;
1625     res = TUPLE3(hp, am_ok, pca.res, make_binary(rest));
1626     hp += 4;
1627     ASSERT(hp==hend); (void)hend;
1628 
1629 done:
1630     if (pca.aligned_ptr != bin_ptr) {
1631         erts_free(ERTS_ALC_T_TMP, pca.aligned_ptr);
1632     }
1633     BIF_RET(res);
1634 }
1635 
1636