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