1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 #include <stdlib.h>
30 #include <sys/types.h>
31 #include <dhcpmsg.h>
32 #include <dhcpagent_ipc.h>
33
34 #include "agent.h"
35 #include "states.h"
36 #include "interface.h"
37 #include "ipc_action.h"
38 #include "util.h"
39
40 static iu_tq_callback_t ipc_action_timeout;
41
42 /*
43 * ipc_action_init(): initializes the ipc_action structure
44 *
45 * input: ipc_action_t *: the structure to initialize
46 * output: void
47 */
48
49 void
50 ipc_action_init(ipc_action_t *ia)
51 {
52 ia->ia_cmd = 0;
53 ia->ia_fd = -1;
54 ia->ia_tid = -1;
55 ia->ia_eid = -1;
56 ia->ia_request = NULL;
57 }
58
59 /*
60 * ipc_action_start(): starts an ipc_action request on a DHCP state machine
61 *
62 * input: dhcp_smach_t *: the state machine to start the action on
63 * ipc_action_t *: request structure
64 * output: B_TRUE if the request is started successfully, B_FALSE otherwise
65 * original request is still valid on failure, consumed otherwise.
66 */
67
68 boolean_t
69 ipc_action_start(dhcp_smach_t *dsmp, ipc_action_t *iareq)
70 {
71 struct ipc_action *ia = &dsmp->dsm_ia;
72
73 if (ia->ia_fd != -1 || ia->ia_tid != -1 || iareq->ia_fd == -1) {
74 dhcpmsg(MSG_CRIT, "ipc_action_start: attempted restart on %s",
75 dsmp->dsm_name);
76 return (B_FALSE);
77 }
78
79 if (!async_cancel(dsmp)) {
80 dhcpmsg(MSG_WARNING, "ipc_action_start: unable to cancel "
81 "action on %s", dsmp->dsm_name);
82 return (B_FALSE);
83 }
84
85 if (iareq->ia_request->timeout == DHCP_IPC_WAIT_DEFAULT)
86 iareq->ia_request->timeout = DHCP_IPC_DEFAULT_WAIT;
87
88 if (iareq->ia_request->timeout == DHCP_IPC_WAIT_FOREVER) {
89 iareq->ia_tid = -1;
90 } else {
91 iareq->ia_tid = iu_schedule_timer(tq,
92 iareq->ia_request->timeout, ipc_action_timeout, dsmp);
93
94 if (iareq->ia_tid == -1) {
95 dhcpmsg(MSG_ERROR, "ipc_action_start: failed to set "
96 "timer for %s on %s",
97 dhcp_ipc_type_to_string(iareq->ia_cmd),
98 dsmp->dsm_name);
99 return (B_FALSE);
100 }
101
102 hold_smach(dsmp);
103 }
104
105 *ia = *iareq;
106
107 /* We've taken ownership, so the input request is now invalid */
108 ipc_action_init(iareq);
109
110 dhcpmsg(MSG_DEBUG, "ipc_action_start: started %s (command %d) on %s,"
111 " buffer length %u",
112 dhcp_ipc_type_to_string(ia->ia_cmd), ia->ia_cmd, dsmp->dsm_name,
113 ia->ia_request == NULL ? 0 : ia->ia_request->data_length);
114
115 dsmp->dsm_dflags |= DHCP_IF_BUSY;
116
117 /* This cannot fail due to the async_cancel above */
118 (void) async_start(dsmp, ia->ia_cmd, B_TRUE);
119
120 return (B_TRUE);
121 }
122
123 /*
124 * ipc_action_finish(): completes an ipc_action request on an interface
125 *
126 * input: dhcp_smach_t *: the state machine to complete the action on
127 * int: the reason why the action finished (nonzero on error)
128 * output: void
129 */
130
131 void
132 ipc_action_finish(dhcp_smach_t *dsmp, int reason)
133 {
134 struct ipc_action *ia = &dsmp->dsm_ia;
135
136 dsmp->dsm_dflags &= ~DHCP_IF_BUSY;
137
138 if (dsmp->dsm_ia.ia_fd == -1) {
139 dhcpmsg(MSG_ERROR,
140 "ipc_action_finish: attempted to finish unknown action "
141 "on %s", dsmp->dsm_name);
142 return;
143 }
144
145 dhcpmsg(MSG_DEBUG,
146 "ipc_action_finish: finished %s (command %d) on %s: %d",
147 dhcp_ipc_type_to_string(ia->ia_cmd), (int)ia->ia_cmd,
148 dsmp->dsm_name, reason);
149
150 /*
151 * if we can't cancel this timer, we're really in the
152 * twilight zone. however, as long as we don't drop the
153 * reference to the state machine, it shouldn't hurt us
154 */
155
156 if (dsmp->dsm_ia.ia_tid != -1 &&
157 iu_cancel_timer(tq, dsmp->dsm_ia.ia_tid, NULL) == 1) {
158 dsmp->dsm_ia.ia_tid = -1;
159 release_smach(dsmp);
160 }
161
162 if (reason == 0)
163 send_ok_reply(ia);
164 else
165 send_error_reply(ia, reason);
166
167 async_finish(dsmp);
168 }
169
170 /*
171 * ipc_action_timeout(): times out an ipc_action on a state machine (the
172 * request continues asynchronously, however)
173 *
174 * input: iu_tq_t *: unused
175 * void *: the dhcp_smach_t * the ipc_action was pending on
176 * output: void
177 */
178
179 /* ARGSUSED */
180 static void
181 ipc_action_timeout(iu_tq_t *tq, void *arg)
182 {
183 dhcp_smach_t *dsmp = arg;
184 struct ipc_action *ia = &dsmp->dsm_ia;
185
186 dsmp->dsm_dflags &= ~DHCP_IF_BUSY;
187
188 ia->ia_tid = -1;
189
190 dhcpmsg(MSG_VERBOSE, "ipc timeout waiting for agent to complete "
191 "%s (command %d) for %s", dhcp_ipc_type_to_string(ia->ia_cmd),
192 ia->ia_cmd, dsmp->dsm_name);
193
194 send_error_reply(ia, DHCP_IPC_E_TIMEOUT);
195
196 async_finish(dsmp);
197 release_smach(dsmp);
198 }
199
200 /*
201 * send_ok_reply(): sends an "ok" reply to a request and closes the ipc
202 * connection
203 *
204 * input: ipc_action_t *: the request to reply to
205 * output: void
206 * note: the request is freed (thus the request must be on the heap).
207 */
208
209 void
210 send_ok_reply(ipc_action_t *ia)
211 {
212 send_error_reply(ia, 0);
213 }
214
215 /*
216 * send_error_reply(): sends an "error" reply to a request and closes the ipc
217 * connection
218 *
219 * input: ipc_action_t *: the request to reply to
220 * int: the error to send back on the ipc connection
221 * output: void
222 * note: the request is freed (thus the request must be on the heap).
223 */
224
225 void
226 send_error_reply(ipc_action_t *ia, int error)
227 {
228 send_data_reply(ia, error, DHCP_TYPE_NONE, NULL, 0);
229 }
230
231 /*
232 * send_data_reply(): sends a reply to a request and closes the ipc connection
233 *
234 * input: ipc_action_t *: the request to reply to
235 * int: the status to send back on the ipc connection (zero for
236 * success, DHCP_IPC_E_* otherwise).
237 * dhcp_data_type_t: the type of the payload in the reply
238 * const void *: the payload for the reply, or NULL if there is no
239 * payload
240 * size_t: the size of the payload
241 * output: void
242 * note: the request is freed (thus the request must be on the heap).
243 */
244
245 void
246 send_data_reply(ipc_action_t *ia, int error, dhcp_data_type_t type,
247 const void *buffer, size_t size)
248 {
249 dhcp_ipc_reply_t *reply;
250 int retval;
251
252 if (ia->ia_fd == -1 || ia->ia_request == NULL)
253 return;
254
255 reply = dhcp_ipc_alloc_reply(ia->ia_request, error, buffer, size,
256 type);
257 if (reply == NULL) {
258 dhcpmsg(MSG_ERR, "send_data_reply: cannot allocate reply");
259
260 } else if ((retval = dhcp_ipc_send_reply(ia->ia_fd, reply)) != 0) {
261 dhcpmsg(MSG_ERROR, "send_data_reply: dhcp_ipc_send_reply: %s",
262 dhcp_ipc_strerror(retval));
263 }
264
265 /*
266 * free the request since we've now used it to send our reply.
267 * we can also close the socket since the reply has been sent.
268 */
269
270 free(reply);
271 free(ia->ia_request);
272 if (ia->ia_eid != -1)
273 (void) iu_unregister_event(eh, ia->ia_eid, NULL);
274 (void) dhcp_ipc_close(ia->ia_fd);
275 ia->ia_request = NULL;
276 ia->ia_fd = -1;
277 ia->ia_eid = -1;
278 }