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