1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 2021. 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 #include "erl_driver.h"
22 
23 static void stop(ErlDrvData drv_data);
24 static ErlDrvData start(ErlDrvPort port,
25 			char *command);
26 static void output(ErlDrvData drv_data,
27 		   char *buf, ErlDrvSizeT len);
28 static void flush(ErlDrvData drv_data);
29 static void timeout(ErlDrvData drv_data);
30 static void process_exit(ErlDrvData drv_data, ErlDrvMonitor *monitor);
31 
32 static ErlDrvEntry unlink_signal_entry = {
33     NULL /* init */,
34     start,
35     stop,
36     output,
37     NULL /* ready_input */,
38     NULL /* ready_output */,
39     "unlink_signal_drv",
40     NULL /* finish */,
41     NULL /* handle */,
42     NULL /* control */,
43     timeout,
44     NULL /* outputv */,
45     NULL /* ready_async */,
46     flush,
47     NULL /* call */,
48     NULL /* event */,
49     ERL_DRV_EXTENDED_MARKER,
50     ERL_DRV_EXTENDED_MAJOR_VERSION,
51     ERL_DRV_EXTENDED_MINOR_VERSION,
52     ERL_DRV_FLAG_USE_PORT_LOCKING,
53     NULL /* handle2 */,
54     process_exit,
55     NULL /* stop_select */
56 };
57 
DRIVER_INIT(unlink_signal_entry)58 DRIVER_INIT(unlink_signal_entry)
59 {
60     return &unlink_signal_entry;
61 }
62 
63 typedef struct {
64     ErlDrvData port;
65     int timeout_count;
66 } us_drv_state;
67 
stop(ErlDrvData drv_data)68 static void stop(ErlDrvData drv_data)
69 {
70     driver_free((void *) drv_data);
71 }
72 
start(ErlDrvPort port,char * command)73 static ErlDrvData start(ErlDrvPort port,
74 			char *command)
75 {
76     us_drv_state *state = (us_drv_state *) driver_alloc(sizeof(us_drv_state));
77     state->port = port;
78     state->timeout_count = 0;
79     return (ErlDrvData) state;
80 }
81 
output(ErlDrvData drv_data,char * buf,ErlDrvSizeT len)82 static void output(ErlDrvData drv_data,
83 		   char *buf, ErlDrvSizeT len)
84 {
85     us_drv_state *state = (us_drv_state *) drv_data;
86     driver_set_timer(state->port, 2);
87 }
88 
flush(ErlDrvData drv_data)89 static void flush(ErlDrvData drv_data)
90 {
91     us_drv_state *state = (us_drv_state *) drv_data;
92     driver_set_timer(state->port, 5);
93 }
94 
timeout(ErlDrvData drv_data)95 static void timeout(ErlDrvData drv_data)
96 {
97     us_drv_state *state = (us_drv_state *) drv_data;
98     state->timeout_count++;
99     if (state->timeout_count == 1) {
100         int i, limit;
101         ErlDrvTermData connected = driver_connected(state->port);
102         /*
103          * Prevent completion of port termination, so that connected
104          * process will be able to send an unlink-ack signal to the
105          * port...
106          */
107         driver_enq(state->port, "x", 1);
108         limit = (int) (((unsigned)state->port) % 1000);
109         /*
110          * Spam connected process with various amounts of monitor,
111          * demonitor signals...
112          */
113         for (i = 0; i < limit; i++) {
114             ErlDrvMonitor *monitor = driver_alloc(sizeof(ErlDrvMonitor));
115             driver_monitor_process(state->port, connected, monitor);
116             driver_demonitor_process(state->port, monitor);
117             driver_free(monitor);
118         }
119         /* driver_exit() will send an unlink signal to conneced process... */
120         driver_exit(state->port, 0);
121     }
122     else {
123         /* Let port complete termination.. */
124         driver_deq(state->port, 1);
125     }
126 }
127 
128 static void
process_exit(ErlDrvData drv_data,ErlDrvMonitor * monitor)129 process_exit(ErlDrvData drv_data, ErlDrvMonitor *monitor)
130 {
131     driver_free(monitor);
132 }
133