Home | History | Annotate | Download | only in dhcpagent
      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