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