xref: /illumos-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/ipc_action.c (revision defc4c8acfa01dba1ef3c13ca0cafccfcede51c0)
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
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
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
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
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
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
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
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