1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * CDDL HEADER START 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*7c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*7c478bd9Sstevel@tonic-gate * with the License. 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*7c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*7c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 12*7c478bd9Sstevel@tonic-gate * and limitations under the License. 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*7c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*7c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*7c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*7c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*7c478bd9Sstevel@tonic-gate * 20*7c478bd9Sstevel@tonic-gate * CDDL HEADER END 21*7c478bd9Sstevel@tonic-gate */ 22*7c478bd9Sstevel@tonic-gate /* 23*7c478bd9Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 25*7c478bd9Sstevel@tonic-gate */ 26*7c478bd9Sstevel@tonic-gate 27*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*7c478bd9Sstevel@tonic-gate 29*7c478bd9Sstevel@tonic-gate /* 30*7c478bd9Sstevel@tonic-gate * method.c - method execution functions 31*7c478bd9Sstevel@tonic-gate * 32*7c478bd9Sstevel@tonic-gate * This file contains the routines needed to run a method: a fork(2)-exec(2) 33*7c478bd9Sstevel@tonic-gate * invocation monitored using either the contract filesystem or waitpid(2). 34*7c478bd9Sstevel@tonic-gate * (Plain fork1(2) support is provided in fork.c.) 35*7c478bd9Sstevel@tonic-gate * 36*7c478bd9Sstevel@tonic-gate * Contract Transfer 37*7c478bd9Sstevel@tonic-gate * When we restart a service, we want to transfer any contracts that the old 38*7c478bd9Sstevel@tonic-gate * service's contract inherited. This means that (a) we must not abandon the 39*7c478bd9Sstevel@tonic-gate * old contract when the service dies and (b) we must write the id of the old 40*7c478bd9Sstevel@tonic-gate * contract into the terms of the new contract. There should be limits to 41*7c478bd9Sstevel@tonic-gate * (a), though, since we don't want to keep the contract around forever. To 42*7c478bd9Sstevel@tonic-gate * this end we'll say that services in the offline state may have a contract 43*7c478bd9Sstevel@tonic-gate * to be transfered and services in the disabled or maintenance states cannot. 44*7c478bd9Sstevel@tonic-gate * This means that when a service transitions from online (or degraded) to 45*7c478bd9Sstevel@tonic-gate * offline, the contract should be preserved, and when the service transitions 46*7c478bd9Sstevel@tonic-gate * from offline to online (i.e., the start method), we'll transfer inherited 47*7c478bd9Sstevel@tonic-gate * contracts. 48*7c478bd9Sstevel@tonic-gate */ 49*7c478bd9Sstevel@tonic-gate 50*7c478bd9Sstevel@tonic-gate #include <sys/contract/process.h> 51*7c478bd9Sstevel@tonic-gate #include <sys/ctfs.h> 52*7c478bd9Sstevel@tonic-gate #include <sys/stat.h> 53*7c478bd9Sstevel@tonic-gate #include <sys/time.h> 54*7c478bd9Sstevel@tonic-gate #include <sys/types.h> 55*7c478bd9Sstevel@tonic-gate #include <sys/uio.h> 56*7c478bd9Sstevel@tonic-gate #include <sys/wait.h> 57*7c478bd9Sstevel@tonic-gate #include <alloca.h> 58*7c478bd9Sstevel@tonic-gate #include <assert.h> 59*7c478bd9Sstevel@tonic-gate #include <errno.h> 60*7c478bd9Sstevel@tonic-gate #include <fcntl.h> 61*7c478bd9Sstevel@tonic-gate #include <libcontract.h> 62*7c478bd9Sstevel@tonic-gate #include <libcontract_priv.h> 63*7c478bd9Sstevel@tonic-gate #include <libgen.h> 64*7c478bd9Sstevel@tonic-gate #include <librestart.h> 65*7c478bd9Sstevel@tonic-gate #include <libscf.h> 66*7c478bd9Sstevel@tonic-gate #include <limits.h> 67*7c478bd9Sstevel@tonic-gate #include <port.h> 68*7c478bd9Sstevel@tonic-gate #include <sac.h> 69*7c478bd9Sstevel@tonic-gate #include <signal.h> 70*7c478bd9Sstevel@tonic-gate #include <stdlib.h> 71*7c478bd9Sstevel@tonic-gate #include <string.h> 72*7c478bd9Sstevel@tonic-gate #include <strings.h> 73*7c478bd9Sstevel@tonic-gate #include <unistd.h> 74*7c478bd9Sstevel@tonic-gate 75*7c478bd9Sstevel@tonic-gate #include "startd.h" 76*7c478bd9Sstevel@tonic-gate 77*7c478bd9Sstevel@tonic-gate #define SBIN_SH "/sbin/sh" 78*7c478bd9Sstevel@tonic-gate 79*7c478bd9Sstevel@tonic-gate /* 80*7c478bd9Sstevel@tonic-gate * Mapping from restart_on method-type to contract events. Must correspond to 81*7c478bd9Sstevel@tonic-gate * enum method_restart_t. 82*7c478bd9Sstevel@tonic-gate */ 83*7c478bd9Sstevel@tonic-gate static uint_t method_events[] = { 84*7c478bd9Sstevel@tonic-gate /* METHOD_RESTART_ALL */ 85*7c478bd9Sstevel@tonic-gate CT_PR_EV_HWERR | CT_PR_EV_SIGNAL | CT_PR_EV_CORE | CT_PR_EV_EMPTY, 86*7c478bd9Sstevel@tonic-gate /* METHOD_RESTART_EXTERNAL_FAULT */ 87*7c478bd9Sstevel@tonic-gate CT_PR_EV_HWERR | CT_PR_EV_SIGNAL, 88*7c478bd9Sstevel@tonic-gate /* METHOD_RESTART_ANY_FAULT */ 89*7c478bd9Sstevel@tonic-gate CT_PR_EV_HWERR | CT_PR_EV_SIGNAL | CT_PR_EV_CORE 90*7c478bd9Sstevel@tonic-gate }; 91*7c478bd9Sstevel@tonic-gate 92*7c478bd9Sstevel@tonic-gate /* 93*7c478bd9Sstevel@tonic-gate * method_record_start(restarter_inst_t *) 94*7c478bd9Sstevel@tonic-gate * Record a service start for rate limiting. Place the current time 95*7c478bd9Sstevel@tonic-gate * in the circular array of instance starts. 96*7c478bd9Sstevel@tonic-gate */ 97*7c478bd9Sstevel@tonic-gate static void 98*7c478bd9Sstevel@tonic-gate method_record_start(restarter_inst_t *inst) 99*7c478bd9Sstevel@tonic-gate { 100*7c478bd9Sstevel@tonic-gate int index = inst->ri_start_index++ % RINST_START_TIMES; 101*7c478bd9Sstevel@tonic-gate 102*7c478bd9Sstevel@tonic-gate inst->ri_start_time[index] = gethrtime(); 103*7c478bd9Sstevel@tonic-gate } 104*7c478bd9Sstevel@tonic-gate 105*7c478bd9Sstevel@tonic-gate /* 106*7c478bd9Sstevel@tonic-gate * method_rate_critical(restarter_inst_t *) 107*7c478bd9Sstevel@tonic-gate * Return true if the average start interval is less than the permitted 108*7c478bd9Sstevel@tonic-gate * interval. Implicit success if insufficient measurements for an 109*7c478bd9Sstevel@tonic-gate * average exist. 110*7c478bd9Sstevel@tonic-gate */ 111*7c478bd9Sstevel@tonic-gate static int 112*7c478bd9Sstevel@tonic-gate method_rate_critical(restarter_inst_t *inst) 113*7c478bd9Sstevel@tonic-gate { 114*7c478bd9Sstevel@tonic-gate uint_t n = inst->ri_start_index; 115*7c478bd9Sstevel@tonic-gate hrtime_t avg_ns = 0; 116*7c478bd9Sstevel@tonic-gate 117*7c478bd9Sstevel@tonic-gate if (inst->ri_start_index < RINST_START_TIMES) 118*7c478bd9Sstevel@tonic-gate return (0); 119*7c478bd9Sstevel@tonic-gate 120*7c478bd9Sstevel@tonic-gate avg_ns = 121*7c478bd9Sstevel@tonic-gate (inst->ri_start_time[(n - 1) % RINST_START_TIMES] - 122*7c478bd9Sstevel@tonic-gate inst->ri_start_time[n % RINST_START_TIMES]) / 123*7c478bd9Sstevel@tonic-gate (RINST_START_TIMES - 1); 124*7c478bd9Sstevel@tonic-gate 125*7c478bd9Sstevel@tonic-gate return (avg_ns < RINST_FAILURE_RATE_NS); 126*7c478bd9Sstevel@tonic-gate } 127*7c478bd9Sstevel@tonic-gate 128*7c478bd9Sstevel@tonic-gate /* 129*7c478bd9Sstevel@tonic-gate * int method_is_transient() 130*7c478bd9Sstevel@tonic-gate * Determine if the method for the given instance is transient, 131*7c478bd9Sstevel@tonic-gate * from a contract perspective. Return 1 if it is, and 0 if it isn't. 132*7c478bd9Sstevel@tonic-gate */ 133*7c478bd9Sstevel@tonic-gate static int 134*7c478bd9Sstevel@tonic-gate method_is_transient(restarter_inst_t *inst, int type) 135*7c478bd9Sstevel@tonic-gate { 136*7c478bd9Sstevel@tonic-gate if (instance_is_transient_style(inst) || type != METHOD_START) 137*7c478bd9Sstevel@tonic-gate return (1); 138*7c478bd9Sstevel@tonic-gate else 139*7c478bd9Sstevel@tonic-gate return (0); 140*7c478bd9Sstevel@tonic-gate } 141*7c478bd9Sstevel@tonic-gate 142*7c478bd9Sstevel@tonic-gate /* 143*7c478bd9Sstevel@tonic-gate * void method_store_contract() 144*7c478bd9Sstevel@tonic-gate * Store the newly created contract id into local structures and 145*7c478bd9Sstevel@tonic-gate * the repository. If the repository connection is broken it is rebound. 146*7c478bd9Sstevel@tonic-gate */ 147*7c478bd9Sstevel@tonic-gate static void 148*7c478bd9Sstevel@tonic-gate method_store_contract(restarter_inst_t *inst, int type, ctid_t *cid) 149*7c478bd9Sstevel@tonic-gate { 150*7c478bd9Sstevel@tonic-gate int r; 151*7c478bd9Sstevel@tonic-gate boolean_t primary; 152*7c478bd9Sstevel@tonic-gate 153*7c478bd9Sstevel@tonic-gate if (errno = contract_latest(cid)) 154*7c478bd9Sstevel@tonic-gate uu_die("%s: Couldn't get new contract's id", inst->ri_i.i_fmri); 155*7c478bd9Sstevel@tonic-gate 156*7c478bd9Sstevel@tonic-gate primary = !method_is_transient(inst, type); 157*7c478bd9Sstevel@tonic-gate 158*7c478bd9Sstevel@tonic-gate if (!primary) { 159*7c478bd9Sstevel@tonic-gate if (inst->ri_i.i_transient_ctid != 0) { 160*7c478bd9Sstevel@tonic-gate log_framework(LOG_INFO, 161*7c478bd9Sstevel@tonic-gate "%s: transient ctid expected to be 0 but " 162*7c478bd9Sstevel@tonic-gate "was set to %ld\n", inst->ri_i.i_fmri, 163*7c478bd9Sstevel@tonic-gate inst->ri_i.i_transient_ctid); 164*7c478bd9Sstevel@tonic-gate } 165*7c478bd9Sstevel@tonic-gate 166*7c478bd9Sstevel@tonic-gate inst->ri_i.i_transient_ctid = *cid; 167*7c478bd9Sstevel@tonic-gate } else { 168*7c478bd9Sstevel@tonic-gate if (inst->ri_i.i_primary_ctid != 0) { 169*7c478bd9Sstevel@tonic-gate /* 170*7c478bd9Sstevel@tonic-gate * There was an old contract that we transferred. 171*7c478bd9Sstevel@tonic-gate * Remove it. 172*7c478bd9Sstevel@tonic-gate */ 173*7c478bd9Sstevel@tonic-gate method_remove_contract(inst, B_TRUE, B_FALSE); 174*7c478bd9Sstevel@tonic-gate } 175*7c478bd9Sstevel@tonic-gate 176*7c478bd9Sstevel@tonic-gate if (inst->ri_i.i_primary_ctid != 0) { 177*7c478bd9Sstevel@tonic-gate log_framework(LOG_INFO, 178*7c478bd9Sstevel@tonic-gate "%s: primary ctid expected to be 0 but " 179*7c478bd9Sstevel@tonic-gate "was set to %ld\n", inst->ri_i.i_fmri, 180*7c478bd9Sstevel@tonic-gate inst->ri_i.i_primary_ctid); 181*7c478bd9Sstevel@tonic-gate } 182*7c478bd9Sstevel@tonic-gate 183*7c478bd9Sstevel@tonic-gate inst->ri_i.i_primary_ctid = *cid; 184*7c478bd9Sstevel@tonic-gate inst->ri_i.i_primary_ctid_stopped = 0; 185*7c478bd9Sstevel@tonic-gate 186*7c478bd9Sstevel@tonic-gate contract_hash_store(*cid, inst->ri_id); 187*7c478bd9Sstevel@tonic-gate } 188*7c478bd9Sstevel@tonic-gate 189*7c478bd9Sstevel@tonic-gate again: 190*7c478bd9Sstevel@tonic-gate if (inst->ri_mi_deleted) 191*7c478bd9Sstevel@tonic-gate return; 192*7c478bd9Sstevel@tonic-gate 193*7c478bd9Sstevel@tonic-gate r = restarter_store_contract(inst->ri_m_inst, *cid, primary ? 194*7c478bd9Sstevel@tonic-gate RESTARTER_CONTRACT_PRIMARY : RESTARTER_CONTRACT_TRANSIENT); 195*7c478bd9Sstevel@tonic-gate switch (r) { 196*7c478bd9Sstevel@tonic-gate case 0: 197*7c478bd9Sstevel@tonic-gate break; 198*7c478bd9Sstevel@tonic-gate 199*7c478bd9Sstevel@tonic-gate case ECANCELED: 200*7c478bd9Sstevel@tonic-gate inst->ri_mi_deleted = B_TRUE; 201*7c478bd9Sstevel@tonic-gate break; 202*7c478bd9Sstevel@tonic-gate 203*7c478bd9Sstevel@tonic-gate case ECONNABORTED: 204*7c478bd9Sstevel@tonic-gate libscf_handle_rebind(scf_instance_handle(inst->ri_m_inst)); 205*7c478bd9Sstevel@tonic-gate /* FALLTHROUGH */ 206*7c478bd9Sstevel@tonic-gate 207*7c478bd9Sstevel@tonic-gate case EBADF: 208*7c478bd9Sstevel@tonic-gate libscf_reget_instance(inst); 209*7c478bd9Sstevel@tonic-gate goto again; 210*7c478bd9Sstevel@tonic-gate 211*7c478bd9Sstevel@tonic-gate case ENOMEM: 212*7c478bd9Sstevel@tonic-gate case EPERM: 213*7c478bd9Sstevel@tonic-gate case EACCES: 214*7c478bd9Sstevel@tonic-gate case EROFS: 215*7c478bd9Sstevel@tonic-gate uu_die("%s: Couldn't store contract id %ld", 216*7c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri, *cid); 217*7c478bd9Sstevel@tonic-gate /* NOTREACHED */ 218*7c478bd9Sstevel@tonic-gate 219*7c478bd9Sstevel@tonic-gate case EINVAL: 220*7c478bd9Sstevel@tonic-gate default: 221*7c478bd9Sstevel@tonic-gate bad_error("restarter_store_contract", r); 222*7c478bd9Sstevel@tonic-gate } 223*7c478bd9Sstevel@tonic-gate } 224*7c478bd9Sstevel@tonic-gate 225*7c478bd9Sstevel@tonic-gate /* 226*7c478bd9Sstevel@tonic-gate * void method_remove_contract() 227*7c478bd9Sstevel@tonic-gate * Remove any non-permanent contracts from internal structures and 228*7c478bd9Sstevel@tonic-gate * the repository, then abandon them. 229*7c478bd9Sstevel@tonic-gate * Returns 230*7c478bd9Sstevel@tonic-gate * 0 - success 231*7c478bd9Sstevel@tonic-gate * ECANCELED - inst was deleted from the repository 232*7c478bd9Sstevel@tonic-gate * 233*7c478bd9Sstevel@tonic-gate * If the repository connection was broken, it is rebound. 234*7c478bd9Sstevel@tonic-gate */ 235*7c478bd9Sstevel@tonic-gate void 236*7c478bd9Sstevel@tonic-gate method_remove_contract(restarter_inst_t *inst, boolean_t primary, 237*7c478bd9Sstevel@tonic-gate boolean_t abandon) 238*7c478bd9Sstevel@tonic-gate { 239*7c478bd9Sstevel@tonic-gate ctid_t * const ctidp = primary ? &inst->ri_i.i_primary_ctid : 240*7c478bd9Sstevel@tonic-gate &inst->ri_i.i_transient_ctid; 241*7c478bd9Sstevel@tonic-gate 242*7c478bd9Sstevel@tonic-gate int r; 243*7c478bd9Sstevel@tonic-gate 244*7c478bd9Sstevel@tonic-gate assert(*ctidp != 0); 245*7c478bd9Sstevel@tonic-gate 246*7c478bd9Sstevel@tonic-gate log_framework(LOG_DEBUG, "Removing %s contract %lu for %s.\n", 247*7c478bd9Sstevel@tonic-gate primary ? "primary" : "transient", *ctidp, inst->ri_i.i_fmri); 248*7c478bd9Sstevel@tonic-gate 249*7c478bd9Sstevel@tonic-gate if (abandon) 250*7c478bd9Sstevel@tonic-gate contract_abandon(*ctidp); 251*7c478bd9Sstevel@tonic-gate 252*7c478bd9Sstevel@tonic-gate again: 253*7c478bd9Sstevel@tonic-gate if (inst->ri_mi_deleted) { 254*7c478bd9Sstevel@tonic-gate r = ECANCELED; 255*7c478bd9Sstevel@tonic-gate goto out; 256*7c478bd9Sstevel@tonic-gate } 257*7c478bd9Sstevel@tonic-gate 258*7c478bd9Sstevel@tonic-gate r = restarter_remove_contract(inst->ri_m_inst, *ctidp, primary ? 259*7c478bd9Sstevel@tonic-gate RESTARTER_CONTRACT_PRIMARY : RESTARTER_CONTRACT_TRANSIENT); 260*7c478bd9Sstevel@tonic-gate switch (r) { 261*7c478bd9Sstevel@tonic-gate case 0: 262*7c478bd9Sstevel@tonic-gate break; 263*7c478bd9Sstevel@tonic-gate 264*7c478bd9Sstevel@tonic-gate case ECANCELED: 265*7c478bd9Sstevel@tonic-gate inst->ri_mi_deleted = B_TRUE; 266*7c478bd9Sstevel@tonic-gate break; 267*7c478bd9Sstevel@tonic-gate 268*7c478bd9Sstevel@tonic-gate case ECONNABORTED: 269*7c478bd9Sstevel@tonic-gate libscf_handle_rebind(scf_instance_handle(inst->ri_m_inst)); 270*7c478bd9Sstevel@tonic-gate /* FALLTHROUGH */ 271*7c478bd9Sstevel@tonic-gate 272*7c478bd9Sstevel@tonic-gate case EBADF: 273*7c478bd9Sstevel@tonic-gate libscf_reget_instance(inst); 274*7c478bd9Sstevel@tonic-gate goto again; 275*7c478bd9Sstevel@tonic-gate 276*7c478bd9Sstevel@tonic-gate case ENOMEM: 277*7c478bd9Sstevel@tonic-gate case EPERM: 278*7c478bd9Sstevel@tonic-gate case EACCES: 279*7c478bd9Sstevel@tonic-gate case EROFS: 280*7c478bd9Sstevel@tonic-gate log_error(LOG_INFO, "%s: Couldn't remove contract id %ld: " 281*7c478bd9Sstevel@tonic-gate "%s.\n", inst->ri_i.i_fmri, *ctidp, strerror(r)); 282*7c478bd9Sstevel@tonic-gate break; 283*7c478bd9Sstevel@tonic-gate 284*7c478bd9Sstevel@tonic-gate case EINVAL: 285*7c478bd9Sstevel@tonic-gate default: 286*7c478bd9Sstevel@tonic-gate bad_error("restarter_remove_contract", r); 287*7c478bd9Sstevel@tonic-gate } 288*7c478bd9Sstevel@tonic-gate 289*7c478bd9Sstevel@tonic-gate out: 290*7c478bd9Sstevel@tonic-gate if (primary) 291*7c478bd9Sstevel@tonic-gate contract_hash_remove(*ctidp); 292*7c478bd9Sstevel@tonic-gate 293*7c478bd9Sstevel@tonic-gate *ctidp = 0; 294*7c478bd9Sstevel@tonic-gate } 295*7c478bd9Sstevel@tonic-gate 296*7c478bd9Sstevel@tonic-gate /* 297*7c478bd9Sstevel@tonic-gate * int method_ready_contract(restarter_inst_t *, int, method_restart_t, int) 298*7c478bd9Sstevel@tonic-gate * 299*7c478bd9Sstevel@tonic-gate * Activate a contract template for the type method of inst. type, 300*7c478bd9Sstevel@tonic-gate * restart_on, and cte_mask dictate the critical events term of the contract. 301*7c478bd9Sstevel@tonic-gate * Returns 302*7c478bd9Sstevel@tonic-gate * 0 - success 303*7c478bd9Sstevel@tonic-gate * ECANCELED - inst has been deleted from the repository 304*7c478bd9Sstevel@tonic-gate */ 305*7c478bd9Sstevel@tonic-gate static int 306*7c478bd9Sstevel@tonic-gate method_ready_contract(restarter_inst_t *inst, int type, 307*7c478bd9Sstevel@tonic-gate method_restart_t restart_on, uint_t cte_mask) 308*7c478bd9Sstevel@tonic-gate { 309*7c478bd9Sstevel@tonic-gate int tmpl, err, istrans, iswait, ret; 310*7c478bd9Sstevel@tonic-gate uint_t cevents, fevents; 311*7c478bd9Sstevel@tonic-gate 312*7c478bd9Sstevel@tonic-gate /* 313*7c478bd9Sstevel@tonic-gate * Correctly supporting wait-style services is tricky without 314*7c478bd9Sstevel@tonic-gate * rearchitecting startd to cope with multiple event sources 315*7c478bd9Sstevel@tonic-gate * simultaneously trying to stop an instance. Until a better 316*7c478bd9Sstevel@tonic-gate * solution is implemented, we avoid this problem for 317*7c478bd9Sstevel@tonic-gate * wait-style services by making contract events fatal and 318*7c478bd9Sstevel@tonic-gate * letting the wait code alone handle stopping the service. 319*7c478bd9Sstevel@tonic-gate */ 320*7c478bd9Sstevel@tonic-gate iswait = instance_is_wait_style(inst); 321*7c478bd9Sstevel@tonic-gate istrans = method_is_transient(inst, type); 322*7c478bd9Sstevel@tonic-gate 323*7c478bd9Sstevel@tonic-gate tmpl = open64(CTFS_ROOT "/process/template", O_RDWR); 324*7c478bd9Sstevel@tonic-gate if (tmpl == -1) 325*7c478bd9Sstevel@tonic-gate uu_die("Could not create contract template"); 326*7c478bd9Sstevel@tonic-gate 327*7c478bd9Sstevel@tonic-gate /* 328*7c478bd9Sstevel@tonic-gate * We assume non-login processes are unlikely to create 329*7c478bd9Sstevel@tonic-gate * multiple process groups, and set CT_PR_PGRPONLY for all 330*7c478bd9Sstevel@tonic-gate * wait-style services' contracts. 331*7c478bd9Sstevel@tonic-gate */ 332*7c478bd9Sstevel@tonic-gate err = ct_pr_tmpl_set_param(tmpl, CT_PR_INHERIT | CT_PR_REGENT | 333*7c478bd9Sstevel@tonic-gate (iswait ? CT_PR_PGRPONLY : 0)); 334*7c478bd9Sstevel@tonic-gate assert(err == 0); 335*7c478bd9Sstevel@tonic-gate 336*7c478bd9Sstevel@tonic-gate if (istrans) { 337*7c478bd9Sstevel@tonic-gate cevents = 0; 338*7c478bd9Sstevel@tonic-gate fevents = 0; 339*7c478bd9Sstevel@tonic-gate } else { 340*7c478bd9Sstevel@tonic-gate assert(restart_on >= 0); 341*7c478bd9Sstevel@tonic-gate assert(restart_on <= METHOD_RESTART_ANY_FAULT); 342*7c478bd9Sstevel@tonic-gate cevents = method_events[restart_on] & ~cte_mask; 343*7c478bd9Sstevel@tonic-gate fevents = iswait ? 344*7c478bd9Sstevel@tonic-gate (method_events[restart_on] & ~cte_mask & CT_PR_ALLFATAL) : 345*7c478bd9Sstevel@tonic-gate 0; 346*7c478bd9Sstevel@tonic-gate } 347*7c478bd9Sstevel@tonic-gate 348*7c478bd9Sstevel@tonic-gate err = ct_tmpl_set_critical(tmpl, cevents); 349*7c478bd9Sstevel@tonic-gate assert(err == 0); 350*7c478bd9Sstevel@tonic-gate 351*7c478bd9Sstevel@tonic-gate err = ct_tmpl_set_informative(tmpl, 0); 352*7c478bd9Sstevel@tonic-gate assert(err == 0); 353*7c478bd9Sstevel@tonic-gate err = ct_pr_tmpl_set_fatal(tmpl, fevents); 354*7c478bd9Sstevel@tonic-gate assert(err == 0); 355*7c478bd9Sstevel@tonic-gate 356*7c478bd9Sstevel@tonic-gate err = ct_tmpl_set_cookie(tmpl, istrans ? METHOD_OTHER_COOKIE : 357*7c478bd9Sstevel@tonic-gate METHOD_START_COOKIE); 358*7c478bd9Sstevel@tonic-gate assert(err == 0); 359*7c478bd9Sstevel@tonic-gate 360*7c478bd9Sstevel@tonic-gate if (type == METHOD_START && inst->ri_i.i_primary_ctid != 0) { 361*7c478bd9Sstevel@tonic-gate ret = ct_pr_tmpl_set_transfer(tmpl, inst->ri_i.i_primary_ctid); 362*7c478bd9Sstevel@tonic-gate switch (ret) { 363*7c478bd9Sstevel@tonic-gate case 0: 364*7c478bd9Sstevel@tonic-gate break; 365*7c478bd9Sstevel@tonic-gate 366*7c478bd9Sstevel@tonic-gate case ENOTEMPTY: 367*7c478bd9Sstevel@tonic-gate /* No contracts for you! */ 368*7c478bd9Sstevel@tonic-gate method_remove_contract(inst, B_TRUE, B_TRUE); 369*7c478bd9Sstevel@tonic-gate if (inst->ri_mi_deleted) { 370*7c478bd9Sstevel@tonic-gate ret = ECANCELED; 371*7c478bd9Sstevel@tonic-gate goto out; 372*7c478bd9Sstevel@tonic-gate } 373*7c478bd9Sstevel@tonic-gate break; 374*7c478bd9Sstevel@tonic-gate 375*7c478bd9Sstevel@tonic-gate case EINVAL: 376*7c478bd9Sstevel@tonic-gate case ESRCH: 377*7c478bd9Sstevel@tonic-gate case EACCES: 378*7c478bd9Sstevel@tonic-gate default: 379*7c478bd9Sstevel@tonic-gate bad_error("ct_pr_tmpl_set_transfer", ret); 380*7c478bd9Sstevel@tonic-gate } 381*7c478bd9Sstevel@tonic-gate } 382*7c478bd9Sstevel@tonic-gate 383*7c478bd9Sstevel@tonic-gate err = ct_tmpl_activate(tmpl); 384*7c478bd9Sstevel@tonic-gate assert(err == 0); 385*7c478bd9Sstevel@tonic-gate 386*7c478bd9Sstevel@tonic-gate ret = 0; 387*7c478bd9Sstevel@tonic-gate 388*7c478bd9Sstevel@tonic-gate out: 389*7c478bd9Sstevel@tonic-gate err = close(tmpl); 390*7c478bd9Sstevel@tonic-gate assert(err == 0); 391*7c478bd9Sstevel@tonic-gate 392*7c478bd9Sstevel@tonic-gate return (ret); 393*7c478bd9Sstevel@tonic-gate } 394*7c478bd9Sstevel@tonic-gate 395*7c478bd9Sstevel@tonic-gate static const char *method_names[] = { "start", "stop", "refresh" }; 396*7c478bd9Sstevel@tonic-gate 397*7c478bd9Sstevel@tonic-gate static void 398*7c478bd9Sstevel@tonic-gate exec_method(const restarter_inst_t *inst, int type, const char *method, 399*7c478bd9Sstevel@tonic-gate struct method_context *mcp, uint8_t need_session) 400*7c478bd9Sstevel@tonic-gate { 401*7c478bd9Sstevel@tonic-gate char *cmd; 402*7c478bd9Sstevel@tonic-gate const char *errf; 403*7c478bd9Sstevel@tonic-gate char **nenv; 404*7c478bd9Sstevel@tonic-gate 405*7c478bd9Sstevel@tonic-gate cmd = uu_msprintf("exec %s", method); 406*7c478bd9Sstevel@tonic-gate 407*7c478bd9Sstevel@tonic-gate if (inst->ri_utmpx_prefix[0] != '\0' && inst->ri_utmpx_prefix != NULL) 408*7c478bd9Sstevel@tonic-gate (void) utmpx_mark_init(getpid(), inst->ri_utmpx_prefix); 409*7c478bd9Sstevel@tonic-gate 410*7c478bd9Sstevel@tonic-gate setlog(inst->ri_logstem); 411*7c478bd9Sstevel@tonic-gate log_instance(inst, B_FALSE, "Executing %s method (\"%s\")", 412*7c478bd9Sstevel@tonic-gate method_names[type], method); 413*7c478bd9Sstevel@tonic-gate 414*7c478bd9Sstevel@tonic-gate if (need_session) 415*7c478bd9Sstevel@tonic-gate (void) setpgrp(); 416*7c478bd9Sstevel@tonic-gate 417*7c478bd9Sstevel@tonic-gate /* Set credentials. */ 418*7c478bd9Sstevel@tonic-gate errno = restarter_set_method_context(mcp, &errf); 419*7c478bd9Sstevel@tonic-gate if (errno != 0) { 420*7c478bd9Sstevel@tonic-gate (void) fputs("svc.startd could not set context for method: ", 421*7c478bd9Sstevel@tonic-gate stderr); 422*7c478bd9Sstevel@tonic-gate 423*7c478bd9Sstevel@tonic-gate if (errno == -1) { 424*7c478bd9Sstevel@tonic-gate if (strcmp(errf, "core_set_process_path") == 0) { 425*7c478bd9Sstevel@tonic-gate (void) fputs("Could not set corefile path.\n", 426*7c478bd9Sstevel@tonic-gate stderr); 427*7c478bd9Sstevel@tonic-gate } else if (strcmp(errf, "setproject") == 0) { 428*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: a resource control " 429*7c478bd9Sstevel@tonic-gate "assignment failed\n", errf); 430*7c478bd9Sstevel@tonic-gate } else if (strcmp(errf, "pool_set_binding") == 0) { 431*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: a system error " 432*7c478bd9Sstevel@tonic-gate "occurred\n", errf); 433*7c478bd9Sstevel@tonic-gate } else { 434*7c478bd9Sstevel@tonic-gate #ifndef NDEBUG 435*7c478bd9Sstevel@tonic-gate uu_warn("%s:%d: Bad function name \"%s\" for " 436*7c478bd9Sstevel@tonic-gate "error %d from " 437*7c478bd9Sstevel@tonic-gate "restarter_set_method_context().\n", 438*7c478bd9Sstevel@tonic-gate __FILE__, __LINE__, errf, errno); 439*7c478bd9Sstevel@tonic-gate #endif 440*7c478bd9Sstevel@tonic-gate abort(); 441*7c478bd9Sstevel@tonic-gate } 442*7c478bd9Sstevel@tonic-gate 443*7c478bd9Sstevel@tonic-gate exit(1); 444*7c478bd9Sstevel@tonic-gate } 445*7c478bd9Sstevel@tonic-gate 446*7c478bd9Sstevel@tonic-gate if (errf != NULL && strcmp(errf, "pool_set_binding") == 0) { 447*7c478bd9Sstevel@tonic-gate switch (errno) { 448*7c478bd9Sstevel@tonic-gate case ENOENT: 449*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: the pool could not " 450*7c478bd9Sstevel@tonic-gate "be found\n", errf); 451*7c478bd9Sstevel@tonic-gate break; 452*7c478bd9Sstevel@tonic-gate 453*7c478bd9Sstevel@tonic-gate case EBADF: 454*7c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: the configuration " 455*7c478bd9Sstevel@tonic-gate "is invalid\n", errf); 456*7c478bd9Sstevel@tonic-gate break; 457*7c478bd9Sstevel@tonic-gate 458*7c478bd9Sstevel@tonic-gate default: 459*7c478bd9Sstevel@tonic-gate #ifndef NDEBUG 460*7c478bd9Sstevel@tonic-gate uu_warn("%s:%d: Bad error %d for function %s " 461*7c478bd9Sstevel@tonic-gate "in restarter_set_method_context().\n", 462*7c478bd9Sstevel@tonic-gate __FILE__, __LINE__, errno, errf); 463*7c478bd9Sstevel@tonic-gate #endif 464*7c478bd9Sstevel@tonic-gate abort(); 465*7c478bd9Sstevel@tonic-gate } 466*7c478bd9Sstevel@tonic-gate 467*7c478bd9Sstevel@tonic-gate exit(SMF_EXIT_ERR_CONFIG); 468*7c478bd9Sstevel@tonic-gate } 469*7c478bd9Sstevel@tonic-gate 470*7c478bd9Sstevel@tonic-gate if (errf != NULL) { 471*7c478bd9Sstevel@tonic-gate perror(errf); 472*7c478bd9Sstevel@tonic-gate 473*7c478bd9Sstevel@tonic-gate switch (errno) { 474*7c478bd9Sstevel@tonic-gate case EINVAL: 475*7c478bd9Sstevel@tonic-gate case EPERM: 476*7c478bd9Sstevel@tonic-gate case ENOENT: 477*7c478bd9Sstevel@tonic-gate case ENAMETOOLONG: 478*7c478bd9Sstevel@tonic-gate case ERANGE: 479*7c478bd9Sstevel@tonic-gate case ESRCH: 480*7c478bd9Sstevel@tonic-gate exit(SMF_EXIT_ERR_CONFIG); 481*7c478bd9Sstevel@tonic-gate /* NOTREACHED */ 482*7c478bd9Sstevel@tonic-gate 483*7c478bd9Sstevel@tonic-gate default: 484*7c478bd9Sstevel@tonic-gate exit(1); 485*7c478bd9Sstevel@tonic-gate } 486*7c478bd9Sstevel@tonic-gate } 487*7c478bd9Sstevel@tonic-gate 488*7c478bd9Sstevel@tonic-gate switch (errno) { 489*7c478bd9Sstevel@tonic-gate case ENOMEM: 490*7c478bd9Sstevel@tonic-gate (void) fputs("Out of memory.\n", stderr); 491*7c478bd9Sstevel@tonic-gate exit(1); 492*7c478bd9Sstevel@tonic-gate /* NOTREACHED */ 493*7c478bd9Sstevel@tonic-gate 494*7c478bd9Sstevel@tonic-gate case ENOENT: 495*7c478bd9Sstevel@tonic-gate (void) fputs("Missing passwd entry for user.\n", 496*7c478bd9Sstevel@tonic-gate stderr); 497*7c478bd9Sstevel@tonic-gate exit(SMF_EXIT_ERR_CONFIG); 498*7c478bd9Sstevel@tonic-gate /* NOTREACHED */ 499*7c478bd9Sstevel@tonic-gate 500*7c478bd9Sstevel@tonic-gate default: 501*7c478bd9Sstevel@tonic-gate #ifndef NDEBUG 502*7c478bd9Sstevel@tonic-gate uu_warn("%s:%d: Bad miscellaneous error %d from " 503*7c478bd9Sstevel@tonic-gate "restarter_set_method_context().\n", __FILE__, 504*7c478bd9Sstevel@tonic-gate __LINE__, errno); 505*7c478bd9Sstevel@tonic-gate #endif 506*7c478bd9Sstevel@tonic-gate abort(); 507*7c478bd9Sstevel@tonic-gate } 508*7c478bd9Sstevel@tonic-gate } 509*7c478bd9Sstevel@tonic-gate 510*7c478bd9Sstevel@tonic-gate nenv = set_smf_env(mcp->env, mcp->env_sz, NULL, inst, method); 511*7c478bd9Sstevel@tonic-gate 512*7c478bd9Sstevel@tonic-gate log_preexec(); 513*7c478bd9Sstevel@tonic-gate 514*7c478bd9Sstevel@tonic-gate (void) execle(SBIN_SH, SBIN_SH, "-c", cmd, NULL, nenv); 515*7c478bd9Sstevel@tonic-gate 516*7c478bd9Sstevel@tonic-gate exit(10); 517*7c478bd9Sstevel@tonic-gate } 518*7c478bd9Sstevel@tonic-gate 519*7c478bd9Sstevel@tonic-gate static void 520*7c478bd9Sstevel@tonic-gate write_status(restarter_inst_t *inst, const char *mname, int stat) 521*7c478bd9Sstevel@tonic-gate { 522*7c478bd9Sstevel@tonic-gate int r; 523*7c478bd9Sstevel@tonic-gate 524*7c478bd9Sstevel@tonic-gate again: 525*7c478bd9Sstevel@tonic-gate if (inst->ri_mi_deleted) 526*7c478bd9Sstevel@tonic-gate return; 527*7c478bd9Sstevel@tonic-gate 528*7c478bd9Sstevel@tonic-gate r = libscf_write_method_status(inst->ri_m_inst, mname, stat); 529*7c478bd9Sstevel@tonic-gate switch (r) { 530*7c478bd9Sstevel@tonic-gate case 0: 531*7c478bd9Sstevel@tonic-gate break; 532*7c478bd9Sstevel@tonic-gate 533*7c478bd9Sstevel@tonic-gate case ECONNABORTED: 534*7c478bd9Sstevel@tonic-gate libscf_reget_instance(inst); 535*7c478bd9Sstevel@tonic-gate goto again; 536*7c478bd9Sstevel@tonic-gate 537*7c478bd9Sstevel@tonic-gate case ECANCELED: 538*7c478bd9Sstevel@tonic-gate inst->ri_mi_deleted = 1; 539*7c478bd9Sstevel@tonic-gate break; 540*7c478bd9Sstevel@tonic-gate 541*7c478bd9Sstevel@tonic-gate case EPERM: 542*7c478bd9Sstevel@tonic-gate case EACCES: 543*7c478bd9Sstevel@tonic-gate case EROFS: 544*7c478bd9Sstevel@tonic-gate log_framework(LOG_INFO, "Could not write exit status " 545*7c478bd9Sstevel@tonic-gate "for %s method of %s: %s.\n", mname, 546*7c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri, strerror(r)); 547*7c478bd9Sstevel@tonic-gate break; 548*7c478bd9Sstevel@tonic-gate 549*7c478bd9Sstevel@tonic-gate case ENAMETOOLONG: 550*7c478bd9Sstevel@tonic-gate default: 551*7c478bd9Sstevel@tonic-gate bad_error("libscf_write_method_status", r); 552*7c478bd9Sstevel@tonic-gate } 553*7c478bd9Sstevel@tonic-gate } 554*7c478bd9Sstevel@tonic-gate 555*7c478bd9Sstevel@tonic-gate /* 556*7c478bd9Sstevel@tonic-gate * int method_run() 557*7c478bd9Sstevel@tonic-gate * Execute the type method of instp. If it requires a fork(), wait for it 558*7c478bd9Sstevel@tonic-gate * to return and return its exit code in *exit_code. Otherwise set 559*7c478bd9Sstevel@tonic-gate * *exit_code to 0 if the method succeeds & -1 if it fails. If the 560*7c478bd9Sstevel@tonic-gate * repository connection is broken, it is rebound, but inst may not be 561*7c478bd9Sstevel@tonic-gate * reset. 562*7c478bd9Sstevel@tonic-gate * Returns 563*7c478bd9Sstevel@tonic-gate * 0 - success 564*7c478bd9Sstevel@tonic-gate * EINVAL - A correct method or method context couldn't be retrieved. 565*7c478bd9Sstevel@tonic-gate * EIO - Contract kill failed. 566*7c478bd9Sstevel@tonic-gate * EFAULT - Method couldn't be executed successfully. 567*7c478bd9Sstevel@tonic-gate * ELOOP - Retry threshold exceeded. 568*7c478bd9Sstevel@tonic-gate * ECANCELED - inst was deleted from the repository before method was run 569*7c478bd9Sstevel@tonic-gate * ERANGE - Timeout retry threshold exceeded. 570*7c478bd9Sstevel@tonic-gate * EAGAIN - Failed due to external cause, retry. 571*7c478bd9Sstevel@tonic-gate */ 572*7c478bd9Sstevel@tonic-gate int 573*7c478bd9Sstevel@tonic-gate method_run(restarter_inst_t **instp, int type, int *exit_code) 574*7c478bd9Sstevel@tonic-gate { 575*7c478bd9Sstevel@tonic-gate char *method; 576*7c478bd9Sstevel@tonic-gate int ret_status; 577*7c478bd9Sstevel@tonic-gate pid_t pid; 578*7c478bd9Sstevel@tonic-gate method_restart_t restart_on; 579*7c478bd9Sstevel@tonic-gate uint_t cte_mask; 580*7c478bd9Sstevel@tonic-gate uint8_t need_session; 581*7c478bd9Sstevel@tonic-gate scf_handle_t *h; 582*7c478bd9Sstevel@tonic-gate scf_snapshot_t *snap; 583*7c478bd9Sstevel@tonic-gate const char *mname; 584*7c478bd9Sstevel@tonic-gate const char *errstr; 585*7c478bd9Sstevel@tonic-gate struct method_context *mcp; 586*7c478bd9Sstevel@tonic-gate int result = 0, timeout_fired = 0; 587*7c478bd9Sstevel@tonic-gate int sig, r; 588*7c478bd9Sstevel@tonic-gate boolean_t transient; 589*7c478bd9Sstevel@tonic-gate uint64_t timeout; 590*7c478bd9Sstevel@tonic-gate uint8_t timeout_retry; 591*7c478bd9Sstevel@tonic-gate ctid_t ctid; 592*7c478bd9Sstevel@tonic-gate int ctfd = -1; 593*7c478bd9Sstevel@tonic-gate ct_evthdl_t ctev; 594*7c478bd9Sstevel@tonic-gate uint_t evtype; 595*7c478bd9Sstevel@tonic-gate restarter_inst_t *inst = *instp; 596*7c478bd9Sstevel@tonic-gate int id = inst->ri_id; 597*7c478bd9Sstevel@tonic-gate 598*7c478bd9Sstevel@tonic-gate assert(PTHREAD_MUTEX_HELD(&inst->ri_lock)); 599*7c478bd9Sstevel@tonic-gate assert(instance_in_transition(inst)); 600*7c478bd9Sstevel@tonic-gate 601*7c478bd9Sstevel@tonic-gate if (inst->ri_mi_deleted) 602*7c478bd9Sstevel@tonic-gate return (ECANCELED); 603*7c478bd9Sstevel@tonic-gate 604*7c478bd9Sstevel@tonic-gate *exit_code = 0; 605*7c478bd9Sstevel@tonic-gate 606*7c478bd9Sstevel@tonic-gate assert(0 <= type && type <= 2); 607*7c478bd9Sstevel@tonic-gate mname = method_names[type]; 608*7c478bd9Sstevel@tonic-gate 609*7c478bd9Sstevel@tonic-gate if (type == METHOD_START) 610*7c478bd9Sstevel@tonic-gate inst->ri_pre_online_hook(); 611*7c478bd9Sstevel@tonic-gate 612*7c478bd9Sstevel@tonic-gate h = scf_instance_handle(inst->ri_m_inst); 613*7c478bd9Sstevel@tonic-gate 614*7c478bd9Sstevel@tonic-gate snap = scf_snapshot_create(h); 615*7c478bd9Sstevel@tonic-gate if (snap == NULL || 616*7c478bd9Sstevel@tonic-gate scf_instance_get_snapshot(inst->ri_m_inst, "running", snap) != 0) { 617*7c478bd9Sstevel@tonic-gate log_framework(LOG_DEBUG, 618*7c478bd9Sstevel@tonic-gate "Could not get running snapshot for %s. " 619*7c478bd9Sstevel@tonic-gate "Using editing version to run method %s.\n", 620*7c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri, mname); 621*7c478bd9Sstevel@tonic-gate scf_snapshot_destroy(snap); 622*7c478bd9Sstevel@tonic-gate snap = NULL; 623*7c478bd9Sstevel@tonic-gate } 624*7c478bd9Sstevel@tonic-gate 625*7c478bd9Sstevel@tonic-gate /* 626*7c478bd9Sstevel@tonic-gate * After this point, we may be logging to the instance log. 627*7c478bd9Sstevel@tonic-gate * Make sure we've noted where that log is as a property of 628*7c478bd9Sstevel@tonic-gate * the instance. 629*7c478bd9Sstevel@tonic-gate */ 630*7c478bd9Sstevel@tonic-gate r = libscf_note_method_log(inst->ri_m_inst, st->st_log_prefix, 631*7c478bd9Sstevel@tonic-gate inst->ri_logstem); 632*7c478bd9Sstevel@tonic-gate if (r != 0) { 633*7c478bd9Sstevel@tonic-gate log_framework(LOG_WARNING, 634*7c478bd9Sstevel@tonic-gate "%s: couldn't note log location: %s\n", 635*7c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri, strerror(r)); 636*7c478bd9Sstevel@tonic-gate } 637*7c478bd9Sstevel@tonic-gate 638*7c478bd9Sstevel@tonic-gate if ((method = libscf_get_method(h, type, inst, snap, &restart_on, 639*7c478bd9Sstevel@tonic-gate &cte_mask, &need_session, &timeout, &timeout_retry)) == NULL) { 640*7c478bd9Sstevel@tonic-gate if (errno == LIBSCF_PGROUP_ABSENT) { 641*7c478bd9Sstevel@tonic-gate log_framework(LOG_DEBUG, 642*7c478bd9Sstevel@tonic-gate "%s: instance has no method property group '%s'.\n", 643*7c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri, mname); 644*7c478bd9Sstevel@tonic-gate if (type == METHOD_REFRESH) 645*7c478bd9Sstevel@tonic-gate log_instance(inst, B_TRUE, "No '%s' method " 646*7c478bd9Sstevel@tonic-gate "defined. Treating as :true.", mname); 647*7c478bd9Sstevel@tonic-gate else 648*7c478bd9Sstevel@tonic-gate log_instance(inst, B_TRUE, "Method property " 649*7c478bd9Sstevel@tonic-gate "group '%s' is not present.", mname); 650*7c478bd9Sstevel@tonic-gate scf_snapshot_destroy(snap); 651*7c478bd9Sstevel@tonic-gate return (0); 652*7c478bd9Sstevel@tonic-gate } else if (errno == LIBSCF_PROPERTY_ABSENT) { 653*7c478bd9Sstevel@tonic-gate log_framework(LOG_DEBUG, 654*7c478bd9Sstevel@tonic-gate "%s: instance has no '%s/exec' method property.\n", 655*7c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri, mname); 656*7c478bd9Sstevel@tonic-gate log_instance(inst, B_TRUE, "Method property '%s/exec " 657*7c478bd9Sstevel@tonic-gate "is not present.", mname); 658*7c478bd9Sstevel@tonic-gate scf_snapshot_destroy(snap); 659*7c478bd9Sstevel@tonic-gate return (0); 660*7c478bd9Sstevel@tonic-gate } else { 661*7c478bd9Sstevel@tonic-gate log_error(LOG_WARNING, 662*7c478bd9Sstevel@tonic-gate "%s: instance libscf_get_method failed\n", 663*7c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri); 664*7c478bd9Sstevel@tonic-gate scf_snapshot_destroy(snap); 665*7c478bd9Sstevel@tonic-gate return (EINVAL); 666*7c478bd9Sstevel@tonic-gate } 667*7c478bd9Sstevel@tonic-gate } 668*7c478bd9Sstevel@tonic-gate 669*7c478bd9Sstevel@tonic-gate /* open service contract if stopping a non-transient service */ 670*7c478bd9Sstevel@tonic-gate if (type == METHOD_STOP && (!instance_is_transient_style(inst))) { 671*7c478bd9Sstevel@tonic-gate if (inst->ri_i.i_primary_ctid == 0) { 672*7c478bd9Sstevel@tonic-gate /* service is not running, nothing to stop */ 673*7c478bd9Sstevel@tonic-gate log_framework(LOG_DEBUG, "%s: instance has no primary " 674*7c478bd9Sstevel@tonic-gate "contract, no service to stop.\n", 675*7c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri); 676*7c478bd9Sstevel@tonic-gate scf_snapshot_destroy(snap); 677*7c478bd9Sstevel@tonic-gate return (0); 678*7c478bd9Sstevel@tonic-gate } 679*7c478bd9Sstevel@tonic-gate if ((ctfd = contract_open(inst->ri_i.i_primary_ctid, "process", 680*7c478bd9Sstevel@tonic-gate "events", O_RDONLY)) < 0) { 681*7c478bd9Sstevel@tonic-gate result = EFAULT; 682*7c478bd9Sstevel@tonic-gate log_instance(inst, B_TRUE, "Could not open service " 683*7c478bd9Sstevel@tonic-gate "contract %ld. Stop method not run.\n", 684*7c478bd9Sstevel@tonic-gate inst->ri_i.i_primary_ctid); 685*7c478bd9Sstevel@tonic-gate goto out; 686*7c478bd9Sstevel@tonic-gate } 687*7c478bd9Sstevel@tonic-gate } 688*7c478bd9Sstevel@tonic-gate 689*7c478bd9Sstevel@tonic-gate if (restarter_is_null_method(method)) { 690*7c478bd9Sstevel@tonic-gate log_framework(LOG_DEBUG, "%s: null method succeeds\n", 691*7c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri); 692*7c478bd9Sstevel@tonic-gate 693*7c478bd9Sstevel@tonic-gate log_instance(inst, B_TRUE, "Executing %s method (null)", mname); 694*7c478bd9Sstevel@tonic-gate 695*7c478bd9Sstevel@tonic-gate if (type == METHOD_START) 696*7c478bd9Sstevel@tonic-gate write_status(inst, mname, 0); 697*7c478bd9Sstevel@tonic-gate goto out; 698*7c478bd9Sstevel@tonic-gate } 699*7c478bd9Sstevel@tonic-gate 700*7c478bd9Sstevel@tonic-gate sig = restarter_is_kill_method(method); 701*7c478bd9Sstevel@tonic-gate if (sig >= 0) { 702*7c478bd9Sstevel@tonic-gate 703*7c478bd9Sstevel@tonic-gate if (inst->ri_i.i_primary_ctid == 0) { 704*7c478bd9Sstevel@tonic-gate log_error(LOG_ERR, "%s: :kill with no contract\n", 705*7c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri); 706*7c478bd9Sstevel@tonic-gate result = EINVAL; 707*7c478bd9Sstevel@tonic-gate goto out; 708*7c478bd9Sstevel@tonic-gate } 709*7c478bd9Sstevel@tonic-gate 710*7c478bd9Sstevel@tonic-gate log_framework(LOG_DEBUG, 711*7c478bd9Sstevel@tonic-gate "%s: :killing contract with signal %d\n", 712*7c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri, sig); 713*7c478bd9Sstevel@tonic-gate 714*7c478bd9Sstevel@tonic-gate log_instance(inst, B_TRUE, "Executing %s method (:kill)", 715*7c478bd9Sstevel@tonic-gate mname); 716*7c478bd9Sstevel@tonic-gate 717*7c478bd9Sstevel@tonic-gate if (contract_kill(inst->ri_i.i_primary_ctid, sig, 718*7c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri) != 0) { 719*7c478bd9Sstevel@tonic-gate result = EIO; 720*7c478bd9Sstevel@tonic-gate goto out; 721*7c478bd9Sstevel@tonic-gate } else 722*7c478bd9Sstevel@tonic-gate goto assured_kill; 723*7c478bd9Sstevel@tonic-gate } 724*7c478bd9Sstevel@tonic-gate 725*7c478bd9Sstevel@tonic-gate log_framework(LOG_DEBUG, "%s: forking to run method %s\n", 726*7c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri, method); 727*7c478bd9Sstevel@tonic-gate 728*7c478bd9Sstevel@tonic-gate errstr = restarter_get_method_context(RESTARTER_METHOD_CONTEXT_VERSION, 729*7c478bd9Sstevel@tonic-gate inst->ri_m_inst, snap, mname, method, &mcp); 730*7c478bd9Sstevel@tonic-gate 731*7c478bd9Sstevel@tonic-gate if (errstr != NULL) { 732*7c478bd9Sstevel@tonic-gate log_error(LOG_WARNING, "%s: %s\n", inst->ri_i.i_fmri, errstr); 733*7c478bd9Sstevel@tonic-gate result = EINVAL; 734*7c478bd9Sstevel@tonic-gate goto out; 735*7c478bd9Sstevel@tonic-gate } 736*7c478bd9Sstevel@tonic-gate 737*7c478bd9Sstevel@tonic-gate r = method_ready_contract(inst, type, restart_on, cte_mask); 738*7c478bd9Sstevel@tonic-gate if (r != 0) { 739*7c478bd9Sstevel@tonic-gate assert(r == ECANCELED); 740*7c478bd9Sstevel@tonic-gate assert(inst->ri_mi_deleted); 741*7c478bd9Sstevel@tonic-gate restarter_free_method_context(mcp); 742*7c478bd9Sstevel@tonic-gate result = ECANCELED; 743*7c478bd9Sstevel@tonic-gate goto out; 744*7c478bd9Sstevel@tonic-gate } 745*7c478bd9Sstevel@tonic-gate 746*7c478bd9Sstevel@tonic-gate /* 747*7c478bd9Sstevel@tonic-gate * Validate safety of method contexts, to save children work. 748*7c478bd9Sstevel@tonic-gate */ 749*7c478bd9Sstevel@tonic-gate if (!restarter_rm_libs_loadable()) 750*7c478bd9Sstevel@tonic-gate log_framework(LOG_DEBUG, "%s: method contexts limited " 751*7c478bd9Sstevel@tonic-gate "to root-accessible libraries\n", inst->ri_i.i_fmri); 752*7c478bd9Sstevel@tonic-gate 753*7c478bd9Sstevel@tonic-gate /* 754*7c478bd9Sstevel@tonic-gate * If the service is restarting too quickly, send it to 755*7c478bd9Sstevel@tonic-gate * maintenance. 756*7c478bd9Sstevel@tonic-gate */ 757*7c478bd9Sstevel@tonic-gate if (type == METHOD_START) { 758*7c478bd9Sstevel@tonic-gate method_record_start(inst); 759*7c478bd9Sstevel@tonic-gate if (method_rate_critical(inst)) { 760*7c478bd9Sstevel@tonic-gate log_instance(inst, B_TRUE, "Restarting too quickly, " 761*7c478bd9Sstevel@tonic-gate "changing state to maintenance"); 762*7c478bd9Sstevel@tonic-gate result = ELOOP; 763*7c478bd9Sstevel@tonic-gate goto out; 764*7c478bd9Sstevel@tonic-gate } 765*7c478bd9Sstevel@tonic-gate } 766*7c478bd9Sstevel@tonic-gate 767*7c478bd9Sstevel@tonic-gate pid = startd_fork1(NULL); 768*7c478bd9Sstevel@tonic-gate if (pid == 0) 769*7c478bd9Sstevel@tonic-gate exec_method(inst, type, method, mcp, need_session); 770*7c478bd9Sstevel@tonic-gate 771*7c478bd9Sstevel@tonic-gate if (pid == -1) { 772*7c478bd9Sstevel@tonic-gate log_error(LOG_WARNING, 773*7c478bd9Sstevel@tonic-gate "%s: Couldn't fork to execute method %s\n", 774*7c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri, method); 775*7c478bd9Sstevel@tonic-gate result = EFAULT; 776*7c478bd9Sstevel@tonic-gate goto out; 777*7c478bd9Sstevel@tonic-gate } 778*7c478bd9Sstevel@tonic-gate 779*7c478bd9Sstevel@tonic-gate restarter_free_method_context(mcp); 780*7c478bd9Sstevel@tonic-gate 781*7c478bd9Sstevel@tonic-gate /* 782*7c478bd9Sstevel@tonic-gate * Get the contract id, decide whether it is primary or transient, and 783*7c478bd9Sstevel@tonic-gate * stash it in inst & the repository. 784*7c478bd9Sstevel@tonic-gate */ 785*7c478bd9Sstevel@tonic-gate method_store_contract(inst, type, &ctid); 786*7c478bd9Sstevel@tonic-gate 787*7c478bd9Sstevel@tonic-gate /* 788*7c478bd9Sstevel@tonic-gate * Similarly for the start method PID. 789*7c478bd9Sstevel@tonic-gate */ 790*7c478bd9Sstevel@tonic-gate if (type == METHOD_START && !inst->ri_mi_deleted) 791*7c478bd9Sstevel@tonic-gate (void) libscf_write_start_pid(inst->ri_m_inst, pid); 792*7c478bd9Sstevel@tonic-gate 793*7c478bd9Sstevel@tonic-gate if (instance_is_wait_style(inst) && type == METHOD_START) { 794*7c478bd9Sstevel@tonic-gate /* Wait style instances don't get timeouts on start methods. */ 795*7c478bd9Sstevel@tonic-gate if (wait_register(pid, inst->ri_i.i_fmri, 1, 0)) { 796*7c478bd9Sstevel@tonic-gate log_error(LOG_WARNING, 797*7c478bd9Sstevel@tonic-gate "%s: couldn't register %ld for wait\n", 798*7c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri, pid); 799*7c478bd9Sstevel@tonic-gate result = EFAULT; 800*7c478bd9Sstevel@tonic-gate goto contract_out; 801*7c478bd9Sstevel@tonic-gate } 802*7c478bd9Sstevel@tonic-gate write_status(inst, mname, 0); 803*7c478bd9Sstevel@tonic-gate 804*7c478bd9Sstevel@tonic-gate } else { 805*7c478bd9Sstevel@tonic-gate int r, err; 806*7c478bd9Sstevel@tonic-gate time_t start_time; 807*7c478bd9Sstevel@tonic-gate time_t end_time; 808*7c478bd9Sstevel@tonic-gate 809*7c478bd9Sstevel@tonic-gate /* 810*7c478bd9Sstevel@tonic-gate * Because on upgrade/live-upgrade we may have no chance 811*7c478bd9Sstevel@tonic-gate * to override faulty timeout values on the way to 812*7c478bd9Sstevel@tonic-gate * manifest import, all services on the path to manifest 813*7c478bd9Sstevel@tonic-gate * import are treated the same as INFINITE timeout services. 814*7c478bd9Sstevel@tonic-gate */ 815*7c478bd9Sstevel@tonic-gate 816*7c478bd9Sstevel@tonic-gate start_time = time(NULL); 817*7c478bd9Sstevel@tonic-gate if (timeout != METHOD_TIMEOUT_INFINITE && !is_timeout_ovr(inst)) 818*7c478bd9Sstevel@tonic-gate timeout_insert(inst, ctid, timeout); 819*7c478bd9Sstevel@tonic-gate else 820*7c478bd9Sstevel@tonic-gate timeout = METHOD_TIMEOUT_INFINITE; 821*7c478bd9Sstevel@tonic-gate 822*7c478bd9Sstevel@tonic-gate /* Unlock the instance while waiting for the method. */ 823*7c478bd9Sstevel@tonic-gate MUTEX_UNLOCK(&inst->ri_lock); 824*7c478bd9Sstevel@tonic-gate 825*7c478bd9Sstevel@tonic-gate do 826*7c478bd9Sstevel@tonic-gate r = waitpid(pid, &ret_status, NULL); 827*7c478bd9Sstevel@tonic-gate while (r == -1 && errno == EINTR); 828*7c478bd9Sstevel@tonic-gate if (r == -1) 829*7c478bd9Sstevel@tonic-gate err = errno; 830*7c478bd9Sstevel@tonic-gate 831*7c478bd9Sstevel@tonic-gate /* Re-grab the lock. */ 832*7c478bd9Sstevel@tonic-gate inst = inst_lookup_by_id(id); 833*7c478bd9Sstevel@tonic-gate 834*7c478bd9Sstevel@tonic-gate /* 835*7c478bd9Sstevel@tonic-gate * inst can't be removed, as the removal thread waits 836*7c478bd9Sstevel@tonic-gate * for completion of this one. 837*7c478bd9Sstevel@tonic-gate */ 838*7c478bd9Sstevel@tonic-gate assert(inst != NULL); 839*7c478bd9Sstevel@tonic-gate *instp = inst; 840*7c478bd9Sstevel@tonic-gate 841*7c478bd9Sstevel@tonic-gate if (inst->ri_timeout != NULL && inst->ri_timeout->te_fired) 842*7c478bd9Sstevel@tonic-gate timeout_fired = 1; 843*7c478bd9Sstevel@tonic-gate 844*7c478bd9Sstevel@tonic-gate timeout_remove(inst, ctid); 845*7c478bd9Sstevel@tonic-gate 846*7c478bd9Sstevel@tonic-gate log_framework(LOG_DEBUG, 847*7c478bd9Sstevel@tonic-gate "%s method for %s exited with status %d.\n", mname, 848*7c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri, WEXITSTATUS(ret_status)); 849*7c478bd9Sstevel@tonic-gate 850*7c478bd9Sstevel@tonic-gate if (r == -1) { 851*7c478bd9Sstevel@tonic-gate log_error(LOG_WARNING, 852*7c478bd9Sstevel@tonic-gate "Couldn't waitpid() for %s method of %s (%s).\n", 853*7c478bd9Sstevel@tonic-gate mname, inst->ri_i.i_fmri, strerror(err)); 854*7c478bd9Sstevel@tonic-gate result = EFAULT; 855*7c478bd9Sstevel@tonic-gate goto contract_out; 856*7c478bd9Sstevel@tonic-gate } 857*7c478bd9Sstevel@tonic-gate 858*7c478bd9Sstevel@tonic-gate if (type == METHOD_START) 859*7c478bd9Sstevel@tonic-gate write_status(inst, mname, ret_status); 860*7c478bd9Sstevel@tonic-gate 861*7c478bd9Sstevel@tonic-gate /* return ERANGE if this service doesn't retry on timeout */ 862*7c478bd9Sstevel@tonic-gate if (timeout_fired == 1 && timeout_retry == 0) { 863*7c478bd9Sstevel@tonic-gate result = ERANGE; 864*7c478bd9Sstevel@tonic-gate goto contract_out; 865*7c478bd9Sstevel@tonic-gate } 866*7c478bd9Sstevel@tonic-gate 867*7c478bd9Sstevel@tonic-gate if (!WIFEXITED(ret_status)) { 868*7c478bd9Sstevel@tonic-gate /* 869*7c478bd9Sstevel@tonic-gate * If method didn't exit itself (it was killed by an 870*7c478bd9Sstevel@tonic-gate * external entity, etc.), consider the entire 871*7c478bd9Sstevel@tonic-gate * method_run as failed. 872*7c478bd9Sstevel@tonic-gate */ 873*7c478bd9Sstevel@tonic-gate if (WIFSIGNALED(ret_status)) { 874*7c478bd9Sstevel@tonic-gate char buf[SIG2STR_MAX]; 875*7c478bd9Sstevel@tonic-gate (void) sig2str(WTERMSIG(ret_status), buf); 876*7c478bd9Sstevel@tonic-gate 877*7c478bd9Sstevel@tonic-gate log_error(LOG_WARNING, "%s: Method \"%s\" " 878*7c478bd9Sstevel@tonic-gate "failed due to signal %s.\n", 879*7c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri, method, buf); 880*7c478bd9Sstevel@tonic-gate log_instance(inst, B_TRUE, "Method \"%s\" " 881*7c478bd9Sstevel@tonic-gate "failed due to signal %s", mname, buf); 882*7c478bd9Sstevel@tonic-gate } else { 883*7c478bd9Sstevel@tonic-gate log_error(LOG_WARNING, "%s: Method \"%s\" " 884*7c478bd9Sstevel@tonic-gate "failed with exit status %d.\n", 885*7c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri, method, 886*7c478bd9Sstevel@tonic-gate WEXITSTATUS(ret_status)); 887*7c478bd9Sstevel@tonic-gate log_instance(inst, B_TRUE, "Method \"%s\" " 888*7c478bd9Sstevel@tonic-gate "failed with exit status %d", mname, 889*7c478bd9Sstevel@tonic-gate WEXITSTATUS(ret_status)); 890*7c478bd9Sstevel@tonic-gate } 891*7c478bd9Sstevel@tonic-gate result = EAGAIN; 892*7c478bd9Sstevel@tonic-gate goto contract_out; 893*7c478bd9Sstevel@tonic-gate } 894*7c478bd9Sstevel@tonic-gate 895*7c478bd9Sstevel@tonic-gate *exit_code = WEXITSTATUS(ret_status); 896*7c478bd9Sstevel@tonic-gate if (*exit_code != 0) { 897*7c478bd9Sstevel@tonic-gate log_error(LOG_WARNING, 898*7c478bd9Sstevel@tonic-gate "%s: Method \"%s\" failed with exit status %d.\n", 899*7c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri, method, WEXITSTATUS(ret_status)); 900*7c478bd9Sstevel@tonic-gate } 901*7c478bd9Sstevel@tonic-gate 902*7c478bd9Sstevel@tonic-gate log_instance(inst, B_TRUE, "Method \"%s\" exited with status " 903*7c478bd9Sstevel@tonic-gate "%d", mname, *exit_code); 904*7c478bd9Sstevel@tonic-gate 905*7c478bd9Sstevel@tonic-gate if (*exit_code != 0) 906*7c478bd9Sstevel@tonic-gate goto contract_out; 907*7c478bd9Sstevel@tonic-gate 908*7c478bd9Sstevel@tonic-gate end_time = time(NULL); 909*7c478bd9Sstevel@tonic-gate 910*7c478bd9Sstevel@tonic-gate /* Give service contract remaining seconds to empty */ 911*7c478bd9Sstevel@tonic-gate if (timeout != METHOD_TIMEOUT_INFINITE) 912*7c478bd9Sstevel@tonic-gate timeout -= (end_time - start_time); 913*7c478bd9Sstevel@tonic-gate } 914*7c478bd9Sstevel@tonic-gate 915*7c478bd9Sstevel@tonic-gate assured_kill: 916*7c478bd9Sstevel@tonic-gate /* 917*7c478bd9Sstevel@tonic-gate * For stop methods, assure that the service contract has emptied 918*7c478bd9Sstevel@tonic-gate * before returning. 919*7c478bd9Sstevel@tonic-gate */ 920*7c478bd9Sstevel@tonic-gate if (type == METHOD_STOP && (!instance_is_transient_style(inst)) && 921*7c478bd9Sstevel@tonic-gate !(contract_is_empty(inst->ri_i.i_primary_ctid))) { 922*7c478bd9Sstevel@tonic-gate 923*7c478bd9Sstevel@tonic-gate if (timeout != METHOD_TIMEOUT_INFINITE) 924*7c478bd9Sstevel@tonic-gate timeout_insert(inst, inst->ri_i.i_primary_ctid, 925*7c478bd9Sstevel@tonic-gate timeout); 926*7c478bd9Sstevel@tonic-gate 927*7c478bd9Sstevel@tonic-gate for (;;) { 928*7c478bd9Sstevel@tonic-gate do { 929*7c478bd9Sstevel@tonic-gate r = ct_event_read_critical(ctfd, &ctev); 930*7c478bd9Sstevel@tonic-gate } while (r == EINTR); 931*7c478bd9Sstevel@tonic-gate if (r != 0) 932*7c478bd9Sstevel@tonic-gate break; 933*7c478bd9Sstevel@tonic-gate 934*7c478bd9Sstevel@tonic-gate evtype = ct_event_get_type(ctev); 935*7c478bd9Sstevel@tonic-gate ct_event_free(ctev); 936*7c478bd9Sstevel@tonic-gate if (evtype == CT_PR_EV_EMPTY) 937*7c478bd9Sstevel@tonic-gate break; 938*7c478bd9Sstevel@tonic-gate } 939*7c478bd9Sstevel@tonic-gate if (r) { 940*7c478bd9Sstevel@tonic-gate result = EFAULT; 941*7c478bd9Sstevel@tonic-gate log_instance(inst, B_TRUE, "Error reading service " 942*7c478bd9Sstevel@tonic-gate "contract %ld.\n", inst->ri_i.i_primary_ctid); 943*7c478bd9Sstevel@tonic-gate } 944*7c478bd9Sstevel@tonic-gate 945*7c478bd9Sstevel@tonic-gate if (timeout != METHOD_TIMEOUT_INFINITE) 946*7c478bd9Sstevel@tonic-gate if (inst->ri_timeout->te_fired) 947*7c478bd9Sstevel@tonic-gate result = EFAULT; 948*7c478bd9Sstevel@tonic-gate 949*7c478bd9Sstevel@tonic-gate timeout_remove(inst, inst->ri_i.i_primary_ctid); 950*7c478bd9Sstevel@tonic-gate } 951*7c478bd9Sstevel@tonic-gate 952*7c478bd9Sstevel@tonic-gate contract_out: 953*7c478bd9Sstevel@tonic-gate /* Abandon contracts for transient methods & methods that fail. */ 954*7c478bd9Sstevel@tonic-gate transient = method_is_transient(inst, type); 955*7c478bd9Sstevel@tonic-gate if ((transient || *exit_code != 0 || result != 0) && 956*7c478bd9Sstevel@tonic-gate (restarter_is_kill_method(method) < 0)) 957*7c478bd9Sstevel@tonic-gate method_remove_contract(inst, !transient, B_TRUE); 958*7c478bd9Sstevel@tonic-gate 959*7c478bd9Sstevel@tonic-gate out: 960*7c478bd9Sstevel@tonic-gate if (ctfd >= 0) 961*7c478bd9Sstevel@tonic-gate (void) close(ctfd); 962*7c478bd9Sstevel@tonic-gate scf_snapshot_destroy(snap); 963*7c478bd9Sstevel@tonic-gate free(method); 964*7c478bd9Sstevel@tonic-gate return (result); 965*7c478bd9Sstevel@tonic-gate } 966*7c478bd9Sstevel@tonic-gate 967*7c478bd9Sstevel@tonic-gate /* 968*7c478bd9Sstevel@tonic-gate * The method thread executes a service method to effect a state transition. 969*7c478bd9Sstevel@tonic-gate * The next_state of info->sf_id should be non-_NONE on entrance, and it will 970*7c478bd9Sstevel@tonic-gate * be _NONE on exit (state will either be what next_state was (on success), or 971*7c478bd9Sstevel@tonic-gate * it will be _MAINT (on error)). 972*7c478bd9Sstevel@tonic-gate * 973*7c478bd9Sstevel@tonic-gate * There are six classes of methods to consider: start & other (stop, refresh) 974*7c478bd9Sstevel@tonic-gate * for each of "normal" services, wait services, and transient services. For 975*7c478bd9Sstevel@tonic-gate * each, the method must be fetched from the repository & executed. fork()ed 976*7c478bd9Sstevel@tonic-gate * methods must be waited on, except for the start method of wait services 977*7c478bd9Sstevel@tonic-gate * (which must be registered with the wait subsystem via wait_register()). If 978*7c478bd9Sstevel@tonic-gate * the method succeeded (returned 0), then for start methods its contract 979*7c478bd9Sstevel@tonic-gate * should be recorded as the primary contract for the service. For other 980*7c478bd9Sstevel@tonic-gate * methods, it should be abandoned. If the method fails, then depending on 981*7c478bd9Sstevel@tonic-gate * the failure, either the method should be reexecuted or the service should 982*7c478bd9Sstevel@tonic-gate * be put into maintenance. Either way the contract should be abandoned. 983*7c478bd9Sstevel@tonic-gate */ 984*7c478bd9Sstevel@tonic-gate void * 985*7c478bd9Sstevel@tonic-gate method_thread(void *arg) 986*7c478bd9Sstevel@tonic-gate { 987*7c478bd9Sstevel@tonic-gate fork_info_t *info = arg; 988*7c478bd9Sstevel@tonic-gate restarter_inst_t *inst; 989*7c478bd9Sstevel@tonic-gate scf_handle_t *local_handle; 990*7c478bd9Sstevel@tonic-gate scf_instance_t *s_inst = NULL; 991*7c478bd9Sstevel@tonic-gate int r, exit_code; 992*7c478bd9Sstevel@tonic-gate boolean_t retryable; 993*7c478bd9Sstevel@tonic-gate const char *aux; 994*7c478bd9Sstevel@tonic-gate 995*7c478bd9Sstevel@tonic-gate assert(0 <= info->sf_method_type && info->sf_method_type <= 2); 996*7c478bd9Sstevel@tonic-gate 997*7c478bd9Sstevel@tonic-gate /* Get (and lock) the restarter_inst_t. */ 998*7c478bd9Sstevel@tonic-gate inst = inst_lookup_by_id(info->sf_id); 999*7c478bd9Sstevel@tonic-gate 1000*7c478bd9Sstevel@tonic-gate assert(inst->ri_method_thread != 0); 1001*7c478bd9Sstevel@tonic-gate assert(instance_in_transition(inst) == 1); 1002*7c478bd9Sstevel@tonic-gate 1003*7c478bd9Sstevel@tonic-gate /* 1004*7c478bd9Sstevel@tonic-gate * We cannot leave this function with inst in transition, because 1005*7c478bd9Sstevel@tonic-gate * protocol.c withholds messages for inst otherwise. 1006*7c478bd9Sstevel@tonic-gate */ 1007*7c478bd9Sstevel@tonic-gate 1008*7c478bd9Sstevel@tonic-gate log_framework(LOG_DEBUG, "method_thread() running %s method for %s.\n", 1009*7c478bd9Sstevel@tonic-gate method_names[info->sf_method_type], inst->ri_i.i_fmri); 1010*7c478bd9Sstevel@tonic-gate 1011*7c478bd9Sstevel@tonic-gate local_handle = libscf_handle_create_bound_loop(); 1012*7c478bd9Sstevel@tonic-gate 1013*7c478bd9Sstevel@tonic-gate rebind_retry: 1014*7c478bd9Sstevel@tonic-gate /* get scf_instance_t */ 1015*7c478bd9Sstevel@tonic-gate switch (r = libscf_fmri_get_instance(local_handle, inst->ri_i.i_fmri, 1016*7c478bd9Sstevel@tonic-gate &s_inst)) { 1017*7c478bd9Sstevel@tonic-gate case 0: 1018*7c478bd9Sstevel@tonic-gate break; 1019*7c478bd9Sstevel@tonic-gate 1020*7c478bd9Sstevel@tonic-gate case ECONNABORTED: 1021*7c478bd9Sstevel@tonic-gate libscf_handle_rebind(local_handle); 1022*7c478bd9Sstevel@tonic-gate goto rebind_retry; 1023*7c478bd9Sstevel@tonic-gate 1024*7c478bd9Sstevel@tonic-gate case ENOENT: 1025*7c478bd9Sstevel@tonic-gate /* 1026*7c478bd9Sstevel@tonic-gate * It's not there, but we need to call this so protocol.c 1027*7c478bd9Sstevel@tonic-gate * doesn't think it's in transition anymore. 1028*7c478bd9Sstevel@tonic-gate */ 1029*7c478bd9Sstevel@tonic-gate (void) restarter_instance_update_states(local_handle, inst, 1030*7c478bd9Sstevel@tonic-gate inst->ri_i.i_state, RESTARTER_STATE_NONE, RERR_NONE, 1031*7c478bd9Sstevel@tonic-gate NULL); 1032*7c478bd9Sstevel@tonic-gate goto out; 1033*7c478bd9Sstevel@tonic-gate 1034*7c478bd9Sstevel@tonic-gate case EINVAL: 1035*7c478bd9Sstevel@tonic-gate case ENOTSUP: 1036*7c478bd9Sstevel@tonic-gate default: 1037*7c478bd9Sstevel@tonic-gate bad_error("libscf_fmri_get_instance", r); 1038*7c478bd9Sstevel@tonic-gate } 1039*7c478bd9Sstevel@tonic-gate 1040*7c478bd9Sstevel@tonic-gate inst->ri_m_inst = s_inst; 1041*7c478bd9Sstevel@tonic-gate inst->ri_mi_deleted = B_FALSE; 1042*7c478bd9Sstevel@tonic-gate 1043*7c478bd9Sstevel@tonic-gate retry: 1044*7c478bd9Sstevel@tonic-gate if (info->sf_method_type == METHOD_START) 1045*7c478bd9Sstevel@tonic-gate log_transition(inst, START_REQUESTED); 1046*7c478bd9Sstevel@tonic-gate 1047*7c478bd9Sstevel@tonic-gate r = method_run(&inst, info->sf_method_type, &exit_code); 1048*7c478bd9Sstevel@tonic-gate 1049*7c478bd9Sstevel@tonic-gate if (r == 0 && exit_code == 0) { 1050*7c478bd9Sstevel@tonic-gate /* Success! */ 1051*7c478bd9Sstevel@tonic-gate assert(inst->ri_i.i_next_state != RESTARTER_STATE_NONE); 1052*7c478bd9Sstevel@tonic-gate 1053*7c478bd9Sstevel@tonic-gate /* 1054*7c478bd9Sstevel@tonic-gate * When a stop method succeeds, remove the primary contract of 1055*7c478bd9Sstevel@tonic-gate * the service, unless we're going to offline, in which case 1056*7c478bd9Sstevel@tonic-gate * retain the contract so we can transfer inherited contracts to 1057*7c478bd9Sstevel@tonic-gate * the replacement service. 1058*7c478bd9Sstevel@tonic-gate */ 1059*7c478bd9Sstevel@tonic-gate 1060*7c478bd9Sstevel@tonic-gate if (info->sf_method_type == METHOD_STOP && 1061*7c478bd9Sstevel@tonic-gate inst->ri_i.i_primary_ctid != 0) { 1062*7c478bd9Sstevel@tonic-gate if (inst->ri_i.i_next_state == RESTARTER_STATE_OFFLINE) 1063*7c478bd9Sstevel@tonic-gate inst->ri_i.i_primary_ctid_stopped = 1; 1064*7c478bd9Sstevel@tonic-gate else 1065*7c478bd9Sstevel@tonic-gate method_remove_contract(inst, B_TRUE, B_TRUE); 1066*7c478bd9Sstevel@tonic-gate } 1067*7c478bd9Sstevel@tonic-gate /* 1068*7c478bd9Sstevel@tonic-gate * We don't care whether the handle was rebound because this is 1069*7c478bd9Sstevel@tonic-gate * the last thing we do with it. 1070*7c478bd9Sstevel@tonic-gate */ 1071*7c478bd9Sstevel@tonic-gate (void) restarter_instance_update_states(local_handle, inst, 1072*7c478bd9Sstevel@tonic-gate inst->ri_i.i_next_state, RESTARTER_STATE_NONE, 1073*7c478bd9Sstevel@tonic-gate info->sf_event_type, NULL); 1074*7c478bd9Sstevel@tonic-gate 1075*7c478bd9Sstevel@tonic-gate (void) update_fault_count(inst, FAULT_COUNT_RESET); 1076*7c478bd9Sstevel@tonic-gate 1077*7c478bd9Sstevel@tonic-gate goto out; 1078*7c478bd9Sstevel@tonic-gate } 1079*7c478bd9Sstevel@tonic-gate 1080*7c478bd9Sstevel@tonic-gate /* Failure. Retry or go to maintenance. */ 1081*7c478bd9Sstevel@tonic-gate 1082*7c478bd9Sstevel@tonic-gate if (r != 0 && r != EAGAIN) { 1083*7c478bd9Sstevel@tonic-gate retryable = B_FALSE; 1084*7c478bd9Sstevel@tonic-gate } else { 1085*7c478bd9Sstevel@tonic-gate switch (exit_code) { 1086*7c478bd9Sstevel@tonic-gate case SMF_EXIT_ERR_CONFIG: 1087*7c478bd9Sstevel@tonic-gate case SMF_EXIT_ERR_NOSMF: 1088*7c478bd9Sstevel@tonic-gate case SMF_EXIT_ERR_PERM: 1089*7c478bd9Sstevel@tonic-gate case SMF_EXIT_ERR_FATAL: 1090*7c478bd9Sstevel@tonic-gate retryable = B_FALSE; 1091*7c478bd9Sstevel@tonic-gate break; 1092*7c478bd9Sstevel@tonic-gate 1093*7c478bd9Sstevel@tonic-gate default: 1094*7c478bd9Sstevel@tonic-gate retryable = B_TRUE; 1095*7c478bd9Sstevel@tonic-gate } 1096*7c478bd9Sstevel@tonic-gate } 1097*7c478bd9Sstevel@tonic-gate 1098*7c478bd9Sstevel@tonic-gate if (retryable && update_fault_count(inst, FAULT_COUNT_INCR) != 1) 1099*7c478bd9Sstevel@tonic-gate goto retry; 1100*7c478bd9Sstevel@tonic-gate 1101*7c478bd9Sstevel@tonic-gate /* maintenance */ 1102*7c478bd9Sstevel@tonic-gate if (r == ELOOP) 1103*7c478bd9Sstevel@tonic-gate log_transition(inst, START_FAILED_REPEATEDLY); 1104*7c478bd9Sstevel@tonic-gate else if (r == ERANGE) 1105*7c478bd9Sstevel@tonic-gate log_transition(inst, START_FAILED_TIMEOUT_FATAL); 1106*7c478bd9Sstevel@tonic-gate else if (exit_code == SMF_EXIT_ERR_CONFIG) 1107*7c478bd9Sstevel@tonic-gate log_transition(inst, START_FAILED_CONFIGURATION); 1108*7c478bd9Sstevel@tonic-gate else if (exit_code == SMF_EXIT_ERR_FATAL) 1109*7c478bd9Sstevel@tonic-gate log_transition(inst, START_FAILED_FATAL); 1110*7c478bd9Sstevel@tonic-gate else 1111*7c478bd9Sstevel@tonic-gate log_transition(inst, START_FAILED_OTHER); 1112*7c478bd9Sstevel@tonic-gate 1113*7c478bd9Sstevel@tonic-gate if (r == ELOOP) 1114*7c478bd9Sstevel@tonic-gate aux = "restarting_too_quickly"; 1115*7c478bd9Sstevel@tonic-gate else if (retryable) 1116*7c478bd9Sstevel@tonic-gate aux = "fault_threshold_reached"; 1117*7c478bd9Sstevel@tonic-gate else 1118*7c478bd9Sstevel@tonic-gate aux = "method_failed"; 1119*7c478bd9Sstevel@tonic-gate 1120*7c478bd9Sstevel@tonic-gate (void) restarter_instance_update_states(local_handle, inst, 1121*7c478bd9Sstevel@tonic-gate RESTARTER_STATE_MAINT, RESTARTER_STATE_NONE, RERR_FAULT, 1122*7c478bd9Sstevel@tonic-gate (char *)aux); 1123*7c478bd9Sstevel@tonic-gate 1124*7c478bd9Sstevel@tonic-gate if (!method_is_transient(inst, info->sf_method_type) && 1125*7c478bd9Sstevel@tonic-gate inst->ri_i.i_primary_ctid != 0) 1126*7c478bd9Sstevel@tonic-gate method_remove_contract(inst, B_TRUE, B_TRUE); 1127*7c478bd9Sstevel@tonic-gate 1128*7c478bd9Sstevel@tonic-gate out: 1129*7c478bd9Sstevel@tonic-gate inst->ri_method_thread = 0; 1130*7c478bd9Sstevel@tonic-gate MUTEX_UNLOCK(&inst->ri_lock); 1131*7c478bd9Sstevel@tonic-gate (void) pthread_cond_broadcast(&inst->ri_method_cv); 1132*7c478bd9Sstevel@tonic-gate 1133*7c478bd9Sstevel@tonic-gate scf_instance_destroy(s_inst); 1134*7c478bd9Sstevel@tonic-gate scf_handle_destroy(local_handle); 1135*7c478bd9Sstevel@tonic-gate startd_free(info, sizeof (fork_info_t)); 1136*7c478bd9Sstevel@tonic-gate return (NULL); 1137*7c478bd9Sstevel@tonic-gate } 1138