xref: /onnv-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/async.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include <sys/types.h>
30*0Sstevel@tonic-gate #include <sys/stat.h>
31*0Sstevel@tonic-gate #include <dhcpmsg.h>
32*0Sstevel@tonic-gate #include <libinetutil.h>
33*0Sstevel@tonic-gate 
34*0Sstevel@tonic-gate #include "async.h"
35*0Sstevel@tonic-gate #include "util.h"
36*0Sstevel@tonic-gate #include "agent.h"
37*0Sstevel@tonic-gate #include "interface.h"
38*0Sstevel@tonic-gate #include "script_handler.h"
39*0Sstevel@tonic-gate 
40*0Sstevel@tonic-gate static void	async_timeout(iu_tq_t *, void *);
41*0Sstevel@tonic-gate 
42*0Sstevel@tonic-gate /*
43*0Sstevel@tonic-gate  * async_pending(): checks to see if an async command is pending.  if a stale
44*0Sstevel@tonic-gate  *		    async command is found, cancellation is attempted.
45*0Sstevel@tonic-gate  *
46*0Sstevel@tonic-gate  *   input: struct ifslist *: the interface to check for an async command on
47*0Sstevel@tonic-gate  *  output: boolean_t: B_TRUE if async command is pending, B_FALSE if not
48*0Sstevel@tonic-gate  */
49*0Sstevel@tonic-gate 
50*0Sstevel@tonic-gate boolean_t
51*0Sstevel@tonic-gate async_pending(struct ifslist *ifsp)
52*0Sstevel@tonic-gate {
53*0Sstevel@tonic-gate 	if (!(ifsp->if_dflags & DHCP_IF_BUSY))
54*0Sstevel@tonic-gate 		return (B_FALSE);
55*0Sstevel@tonic-gate 
56*0Sstevel@tonic-gate 	/*
57*0Sstevel@tonic-gate 	 * if the command was not started by the user (i.e., was
58*0Sstevel@tonic-gate 	 * started internal to the agent), then it will timeout in
59*0Sstevel@tonic-gate 	 * async_timeout() -- don't shoot it here.
60*0Sstevel@tonic-gate 	 */
61*0Sstevel@tonic-gate 
62*0Sstevel@tonic-gate 	if (!ifsp->if_async.as_user)
63*0Sstevel@tonic-gate 		return (B_TRUE);
64*0Sstevel@tonic-gate 
65*0Sstevel@tonic-gate 	if (ifsp->if_script_pid != -1)
66*0Sstevel@tonic-gate 		return (B_TRUE);
67*0Sstevel@tonic-gate 
68*0Sstevel@tonic-gate 	/*
69*0Sstevel@tonic-gate 	 * user command -- see if they went away.  if they went away,
70*0Sstevel@tonic-gate 	 * either a timeout was already sent to them or they
71*0Sstevel@tonic-gate 	 * control-c'd out.
72*0Sstevel@tonic-gate 	 */
73*0Sstevel@tonic-gate 
74*0Sstevel@tonic-gate 	if (ipc_action_pending(ifsp))
75*0Sstevel@tonic-gate 		return (B_TRUE);
76*0Sstevel@tonic-gate 
77*0Sstevel@tonic-gate 	/*
78*0Sstevel@tonic-gate 	 * it appears they went away.  try to cancel their pending
79*0Sstevel@tonic-gate 	 * command.  if we can't cancel it, we leave their command
80*0Sstevel@tonic-gate 	 * pending and it's just gonna have to complete its business
81*0Sstevel@tonic-gate 	 * in any case, cancel the ipc_action timer, since we know
82*0Sstevel@tonic-gate 	 * they've gone away.
83*0Sstevel@tonic-gate 	 */
84*0Sstevel@tonic-gate 
85*0Sstevel@tonic-gate 	dhcpmsg(MSG_DEBUG, "async_pending: async command left, attempting "
86*0Sstevel@tonic-gate 	    "cancellation");
87*0Sstevel@tonic-gate 
88*0Sstevel@tonic-gate 	ipc_action_cancel_timer(ifsp);
89*0Sstevel@tonic-gate 	return (async_cancel(ifsp) ? B_FALSE : B_TRUE);
90*0Sstevel@tonic-gate }
91*0Sstevel@tonic-gate 
92*0Sstevel@tonic-gate /*
93*0Sstevel@tonic-gate  * async_start(): starts an asynchronous command on an interface
94*0Sstevel@tonic-gate  *
95*0Sstevel@tonic-gate  *   input: struct ifslist *: the interface to start the async command on
96*0Sstevel@tonic-gate  *	    dhcp_ipc_type_t: the command to start
97*0Sstevel@tonic-gate  *	    boolean_t: B_TRUE if the command was started by a user
98*0Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
99*0Sstevel@tonic-gate  */
100*0Sstevel@tonic-gate 
101*0Sstevel@tonic-gate int
102*0Sstevel@tonic-gate async_start(struct ifslist *ifsp, dhcp_ipc_type_t cmd, boolean_t user)
103*0Sstevel@tonic-gate {
104*0Sstevel@tonic-gate 	iu_timer_id_t tid;
105*0Sstevel@tonic-gate 
106*0Sstevel@tonic-gate 	if (async_pending(ifsp))
107*0Sstevel@tonic-gate 		return (0);
108*0Sstevel@tonic-gate 
109*0Sstevel@tonic-gate 	tid = iu_schedule_timer(tq, DHCP_ASYNC_WAIT, async_timeout, ifsp);
110*0Sstevel@tonic-gate 	if (tid == -1)
111*0Sstevel@tonic-gate 		return (0);
112*0Sstevel@tonic-gate 
113*0Sstevel@tonic-gate 	hold_ifs(ifsp);
114*0Sstevel@tonic-gate 
115*0Sstevel@tonic-gate 	ifsp->if_async.as_tid	 = tid;
116*0Sstevel@tonic-gate 	ifsp->if_async.as_cmd	 = cmd;
117*0Sstevel@tonic-gate 	ifsp->if_async.as_user	 = user;
118*0Sstevel@tonic-gate 	ifsp->if_dflags		|= DHCP_IF_BUSY;
119*0Sstevel@tonic-gate 
120*0Sstevel@tonic-gate 	return (1);
121*0Sstevel@tonic-gate }
122*0Sstevel@tonic-gate 
123*0Sstevel@tonic-gate 
124*0Sstevel@tonic-gate /*
125*0Sstevel@tonic-gate  * async_finish(): completes an asynchronous command
126*0Sstevel@tonic-gate  *
127*0Sstevel@tonic-gate  *   input: struct ifslist *: the interface with the pending async command
128*0Sstevel@tonic-gate  *  output: void
129*0Sstevel@tonic-gate  *    note: should only be used when the command has no residual state to
130*0Sstevel@tonic-gate  *	    clean up
131*0Sstevel@tonic-gate  */
132*0Sstevel@tonic-gate 
133*0Sstevel@tonic-gate void
134*0Sstevel@tonic-gate async_finish(struct ifslist *ifsp)
135*0Sstevel@tonic-gate {
136*0Sstevel@tonic-gate 	/*
137*0Sstevel@tonic-gate 	 * be defensive here. the script may still be running if
138*0Sstevel@tonic-gate 	 * the asynchronous action times out before it is killed by the
139*0Sstevel@tonic-gate 	 * script helper process.
140*0Sstevel@tonic-gate 	 */
141*0Sstevel@tonic-gate 
142*0Sstevel@tonic-gate 	if (ifsp->if_script_pid != -1)
143*0Sstevel@tonic-gate 		script_stop(ifsp);
144*0Sstevel@tonic-gate 
145*0Sstevel@tonic-gate 	/*
146*0Sstevel@tonic-gate 	 * in case async_timeout() has already called async_cancel(),
147*0Sstevel@tonic-gate 	 * and to be idempotent, check the DHCP_IF_BUSY flag
148*0Sstevel@tonic-gate 	 */
149*0Sstevel@tonic-gate 
150*0Sstevel@tonic-gate 	if (!(ifsp->if_dflags & DHCP_IF_BUSY))
151*0Sstevel@tonic-gate 		return;
152*0Sstevel@tonic-gate 
153*0Sstevel@tonic-gate 	if (ifsp->if_async.as_tid == -1) {
154*0Sstevel@tonic-gate 		ifsp->if_dflags &= ~DHCP_IF_BUSY;
155*0Sstevel@tonic-gate 		return;
156*0Sstevel@tonic-gate 	}
157*0Sstevel@tonic-gate 
158*0Sstevel@tonic-gate 	if (iu_cancel_timer(tq, ifsp->if_async.as_tid, NULL) == 1) {
159*0Sstevel@tonic-gate 		ifsp->if_dflags &= ~DHCP_IF_BUSY;
160*0Sstevel@tonic-gate 		ifsp->if_async.as_tid = -1;
161*0Sstevel@tonic-gate 		(void) release_ifs(ifsp);
162*0Sstevel@tonic-gate 		return;
163*0Sstevel@tonic-gate 	}
164*0Sstevel@tonic-gate 
165*0Sstevel@tonic-gate 	/*
166*0Sstevel@tonic-gate 	 * if we can't cancel this timer, we'll just leave the
167*0Sstevel@tonic-gate 	 * interface busy and when the timeout finally fires, we'll
168*0Sstevel@tonic-gate 	 * mark it free, which will just cause a minor nuisance.
169*0Sstevel@tonic-gate 	 */
170*0Sstevel@tonic-gate 
171*0Sstevel@tonic-gate 	dhcpmsg(MSG_WARNING, "async_finish: cannot cancel async timer");
172*0Sstevel@tonic-gate }
173*0Sstevel@tonic-gate 
174*0Sstevel@tonic-gate /*
175*0Sstevel@tonic-gate  * async_cancel(): cancels a pending asynchronous command
176*0Sstevel@tonic-gate  *
177*0Sstevel@tonic-gate  *   input: struct ifslist *: the interface with the pending async command
178*0Sstevel@tonic-gate  *  output: int: 1 if cancellation was successful, 0 on failure
179*0Sstevel@tonic-gate  */
180*0Sstevel@tonic-gate 
181*0Sstevel@tonic-gate int
182*0Sstevel@tonic-gate async_cancel(struct ifslist *ifsp)
183*0Sstevel@tonic-gate {
184*0Sstevel@tonic-gate 	boolean_t do_reset = B_FALSE;
185*0Sstevel@tonic-gate 
186*0Sstevel@tonic-gate 	/*
187*0Sstevel@tonic-gate 	 * we decide how to cancel the command depending on our
188*0Sstevel@tonic-gate 	 * current state, since commands such as EXTEND may in fact
189*0Sstevel@tonic-gate 	 * cause us to enter back into SELECTING (if a NAK results
190*0Sstevel@tonic-gate 	 * from the EXTEND).
191*0Sstevel@tonic-gate 	 */
192*0Sstevel@tonic-gate 
193*0Sstevel@tonic-gate 	switch (ifsp->if_state) {
194*0Sstevel@tonic-gate 
195*0Sstevel@tonic-gate 	case BOUND:
196*0Sstevel@tonic-gate 	case INFORMATION:
197*0Sstevel@tonic-gate 		break;
198*0Sstevel@tonic-gate 
199*0Sstevel@tonic-gate 	case RENEWING:					/* FALLTHRU */
200*0Sstevel@tonic-gate 	case REBINDING:					/* FALLTHRU */
201*0Sstevel@tonic-gate 	case INFORM_SENT:
202*0Sstevel@tonic-gate 
203*0Sstevel@tonic-gate 		/*
204*0Sstevel@tonic-gate 		 * these states imply that we've sent a packet and we're
205*0Sstevel@tonic-gate 		 * awaiting an ACK or NAK.  just cancel the wait.
206*0Sstevel@tonic-gate 		 */
207*0Sstevel@tonic-gate 
208*0Sstevel@tonic-gate 		if (unregister_acknak(ifsp) == 0)
209*0Sstevel@tonic-gate 			return (0);
210*0Sstevel@tonic-gate 
211*0Sstevel@tonic-gate 		break;
212*0Sstevel@tonic-gate 
213*0Sstevel@tonic-gate 	case INIT:					/* FALLTHRU */
214*0Sstevel@tonic-gate 	case SELECTING:					/* FALLTHRU */
215*0Sstevel@tonic-gate 	case REQUESTING:				/* FALLTHRU */
216*0Sstevel@tonic-gate 	case INIT_REBOOT:
217*0Sstevel@tonic-gate 
218*0Sstevel@tonic-gate 		/*
219*0Sstevel@tonic-gate 		 * these states imply we're still trying to get a lease.
220*0Sstevel@tonic-gate 		 * just return to a clean slate (INIT) -- but not until
221*0Sstevel@tonic-gate 		 * after we've finished the asynchronous command!
222*0Sstevel@tonic-gate 		 */
223*0Sstevel@tonic-gate 
224*0Sstevel@tonic-gate 		do_reset = B_TRUE;
225*0Sstevel@tonic-gate 		break;
226*0Sstevel@tonic-gate 
227*0Sstevel@tonic-gate 	default:
228*0Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "async_cancel: cancellation in unexpected "
229*0Sstevel@tonic-gate 		    "state %d", ifsp->if_state);
230*0Sstevel@tonic-gate 		return (0);
231*0Sstevel@tonic-gate 	}
232*0Sstevel@tonic-gate 
233*0Sstevel@tonic-gate 	async_finish(ifsp);
234*0Sstevel@tonic-gate 	dhcpmsg(MSG_DEBUG, "async_cancel: asynchronous command (%d) aborted",
235*0Sstevel@tonic-gate 	    ifsp->if_async.as_cmd);
236*0Sstevel@tonic-gate 	if (do_reset)
237*0Sstevel@tonic-gate 		reset_ifs(ifsp);
238*0Sstevel@tonic-gate 
239*0Sstevel@tonic-gate 	return (1);
240*0Sstevel@tonic-gate }
241*0Sstevel@tonic-gate 
242*0Sstevel@tonic-gate /*
243*0Sstevel@tonic-gate  * async_timeout(): expires stale asynchronous commands
244*0Sstevel@tonic-gate  *
245*0Sstevel@tonic-gate  *   input: iu_tq_t *: the timer queue on which the timeout went off
246*0Sstevel@tonic-gate  *	    void *: the interface with the pending async command
247*0Sstevel@tonic-gate  *  output: void
248*0Sstevel@tonic-gate  */
249*0Sstevel@tonic-gate 
250*0Sstevel@tonic-gate static void
251*0Sstevel@tonic-gate async_timeout(iu_tq_t *tq, void *arg)
252*0Sstevel@tonic-gate {
253*0Sstevel@tonic-gate 	struct ifslist		*ifsp = (struct ifslist *)arg;
254*0Sstevel@tonic-gate 
255*0Sstevel@tonic-gate 	if (check_ifs(ifsp) == 0) {
256*0Sstevel@tonic-gate 		(void) release_ifs(ifsp);
257*0Sstevel@tonic-gate 		return;
258*0Sstevel@tonic-gate 	}
259*0Sstevel@tonic-gate 
260*0Sstevel@tonic-gate 	/* we've expired now */
261*0Sstevel@tonic-gate 	ifsp->if_async.as_tid = -1;
262*0Sstevel@tonic-gate 
263*0Sstevel@tonic-gate 	/*
264*0Sstevel@tonic-gate 	 * if the command was generated internally to the agent, try
265*0Sstevel@tonic-gate 	 * to cancel it immediately.  otherwise, if the user has gone
266*0Sstevel@tonic-gate 	 * away, we cancel it in async_pending().  otherwise, we let
267*0Sstevel@tonic-gate 	 * it live.
268*0Sstevel@tonic-gate 	 */
269*0Sstevel@tonic-gate 
270*0Sstevel@tonic-gate 	if (!ifsp->if_async.as_user) {
271*0Sstevel@tonic-gate 		(void) async_cancel(ifsp);
272*0Sstevel@tonic-gate 		return;
273*0Sstevel@tonic-gate 	}
274*0Sstevel@tonic-gate 
275*0Sstevel@tonic-gate 	if (async_pending(ifsp)) {
276*0Sstevel@tonic-gate 
277*0Sstevel@tonic-gate 		ifsp->if_async.as_tid = iu_schedule_timer(tq, DHCP_ASYNC_WAIT,
278*0Sstevel@tonic-gate 		    async_timeout, ifsp);
279*0Sstevel@tonic-gate 
280*0Sstevel@tonic-gate 		if (ifsp->if_async.as_tid != -1) {
281*0Sstevel@tonic-gate 			hold_ifs(ifsp);
282*0Sstevel@tonic-gate 			dhcpmsg(MSG_DEBUG, "async_timeout: asynchronous "
283*0Sstevel@tonic-gate 			    "command %d still pending", ifsp->if_async.as_cmd);
284*0Sstevel@tonic-gate 			return;
285*0Sstevel@tonic-gate 		}
286*0Sstevel@tonic-gate 
287*0Sstevel@tonic-gate 		/*
288*0Sstevel@tonic-gate 		 * what can we do but cancel it?  we can't get called
289*0Sstevel@tonic-gate 		 * back again and otherwise we'll end up in the
290*0Sstevel@tonic-gate 		 * twilight zone with the interface permanently busy
291*0Sstevel@tonic-gate 		 */
292*0Sstevel@tonic-gate 
293*0Sstevel@tonic-gate 		ipc_action_finish(ifsp, DHCP_IPC_E_INT);
294*0Sstevel@tonic-gate 		(void) async_cancel(ifsp);
295*0Sstevel@tonic-gate 	}
296*0Sstevel@tonic-gate }
297