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