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