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 }