Print this page
2831 svc.startd and svc.configd waste memory.
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/lib/librestart/common/librestart.c
+++ new/usr/src/lib/librestart/common/librestart.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.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
↓ open down ↓ |
13 lines elided |
↑ open up ↑ |
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 22 /*
23 23 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24 + * Copyright (c) 2012, Joyent, Inc. All rights reserved.
24 25 */
25 26
26 27 #include <libintl.h>
27 28 #include <librestart.h>
28 29 #include <librestart_priv.h>
29 30 #include <libscf.h>
30 31 #include <libscf_priv.h>
31 32
32 33 #include <assert.h>
33 34 #include <ctype.h>
34 35 #include <dlfcn.h>
35 36 #include <errno.h>
36 37 #include <exec_attr.h>
37 38 #include <grp.h>
38 39 #include <libsysevent.h>
39 40 #include <libuutil.h>
40 41 #include <limits.h>
41 42 #include <link.h>
42 43 #include <malloc.h>
43 44 #include <pool.h>
44 45 #include <priv.h>
45 46 #include <project.h>
46 47 #include <pthread.h>
47 48 #include <pwd.h>
48 49 #include <secdb.h>
49 50 #include <signal.h>
50 51 #include <stdlib.h>
51 52 #include <string.h>
52 53 #include <syslog.h>
53 54 #include <sys/corectl.h>
54 55 #include <sys/machelf.h>
55 56 #include <sys/task.h>
56 57 #include <sys/types.h>
57 58 #include <time.h>
58 59 #include <unistd.h>
59 60 #include <ucontext.h>
60 61
61 62 #define min(a, b) ((a) > (b) ? (b) : (a))
62 63
63 64 #define MKW_TRUE ":true"
64 65 #define MKW_KILL ":kill"
65 66 #define MKW_KILL_PROC ":kill_process"
66 67
67 68 #define ALLOCFAIL ((char *)"Allocation failure.")
68 69 #define RCBROKEN ((char *)"Repository connection broken.")
69 70
70 71 #define MAX_COMMIT_RETRIES 10
71 72 #define MAX_COMMIT_RETRY_INT (5 * 1000000) /* 5 seconds */
72 73 #define INITIAL_COMMIT_RETRY_INT (10000) /* 1/100th second */
73 74
74 75 /*
75 76 * bad_fail() catches bugs in this and lower layers by reporting supposedly
76 77 * impossible function failures. The NDEBUG case keeps the strings out of the
77 78 * library but still calls abort() so we can root-cause from the coredump.
78 79 */
79 80 #ifndef NDEBUG
80 81 #define bad_fail(func, err) { \
81 82 (void) fprintf(stderr, \
82 83 "At %s:%d, %s() failed with unexpected error %d. Aborting.\n", \
83 84 __FILE__, __LINE__, (func), (err)); \
84 85 abort(); \
85 86 }
86 87 #else
87 88 #define bad_fail(func, err) abort()
88 89 #endif
89 90
90 91 struct restarter_event_handle {
91 92 char *reh_restarter_name;
92 93 char *reh_delegate_channel_name;
93 94 evchan_t *reh_delegate_channel;
94 95 char *reh_delegate_subscriber_id;
95 96 char *reh_master_channel_name;
96 97 evchan_t *reh_master_channel;
97 98 char *reh_master_subscriber_id;
98 99 int (*reh_handler)(restarter_event_t *);
99 100 };
100 101
101 102 struct restarter_event {
102 103 sysevent_t *re_sysevent;
103 104 restarter_event_type_t re_type;
104 105 char *re_instance_name;
105 106 restarter_event_handle_t *re_event_handle;
106 107 restarter_instance_state_t re_state;
107 108 restarter_instance_state_t re_next_state;
108 109 };
109 110
110 111 /*
111 112 * Long reasons must all parse/read correctly in the following contexts:
112 113 *
113 114 * "A service instance transitioned state: %s."
114 115 * "A service failed: %s."
115 116 * "Reason: %s."
116 117 * "The service transitioned state (%s) and ..."
117 118 *
118 119 * With the exception of restart_str_none they must also fit the following
119 120 * moulds:
120 121 *
121 122 * "An instance transitioned because %s, and ..."
122 123 * "An instance transitioned to <new-state> because %s, and ..."
123 124 *
124 125 * Note that whoever is rendering the long message must provide the
125 126 * terminal punctuation - don't include it here. Similarly, do not
126 127 * provide an initial capital letter in reason-long.
127 128 *
128 129 * The long reason strings are Volatile - within the grammatical constraints
129 130 * above we may improve them as need be. The intention is that a consumer
130 131 * may blindly render the string along the lines of the above examples,
131 132 * but has no other guarantees as to the exact wording. Long reasons
132 133 * are localized.
133 134 *
134 135 * We define revisions of the set of short reason strings in use. Within
135 136 * a given revision, all short reasons are Committed. Consumers must check
136 137 * the revision in use before relying on the semantics of the short reason
137 138 * codes - if the version exceeds that which they are familiar with they should
138 139 * fail gracefully. Having checked for version compatability, a consumer
139 140 * is assured that
140 141 *
141 142 * "short_reason_A iff semantic_A", provided:
142 143 *
143 144 * . the restarter uses this short reason code at all,
144 145 * . the short reason is not "none" (which a restarter could
145 146 * specifiy for any transition semantics)
146 147 *
147 148 * To split/refine such a Committed semantic_A into further cases,
148 149 * we are required to bump the revision number. This should be an
149 150 * infrequent occurence. If you bump the revision number you may
150 151 * need to make corresponding changes in any source that calls
151 152 * restarter_str_version (e.g., FMA event generation).
152 153 *
153 154 * To add additional reasons to the set you must also bump the version
154 155 * number.
155 156 */
156 157
157 158 /*
158 159 * The following describes revision 0 of the set of transition reasons.
159 160 * Read the preceding block comment before making any changes.
160 161 */
161 162 static const struct restarter_state_transition_reason restarter_str[] = {
162 163 /*
163 164 * Any transition for which the restarter has not provided a reason.
164 165 */
165 166 {
166 167 restarter_str_none,
167 168 "none",
168 169 "the restarter gave no reason"
169 170 },
170 171
171 172 /*
172 173 * A transition to maintenance state due to a
173 174 * 'svcadm mark maintenance <fmri>'. *Not* used if the libscf
174 175 * interface smf_maintain_instance(3SCF) is used to request maintenance.
175 176 */
176 177 {
177 178 restarter_str_administrative_request,
178 179 "administrative_request",
179 180 "maintenance was requested by an administrator"
180 181 },
181 182
182 183 /*
183 184 * A transition to maintenance state if a repository inconsistency
184 185 * exists when the service/instance state is first read by startd
185 186 * into the graph engine (this can also happen during startd restart).
186 187 */
187 188 {
188 189 restarter_str_bad_repo_state,
189 190 "bad_repo_state",
190 191 "an SMF repository inconsistecy exists"
191 192 },
192 193
193 194 /*
194 195 * A transition 'maintenance -> uninitialized' resulting always
195 196 * from 'svcadm clear <fmri>'. *Not* used if the libscf interface
196 197 * smf_restore_instance(3SCF) is used.
197 198 */
198 199 {
199 200 restarter_str_clear_request,
200 201 "clear_request",
201 202 "maintenance clear was requested by an administrator"
202 203 },
203 204
204 205 /*
205 206 * A transition 'online -> offline' due to a process core dump.
206 207 */
207 208 {
208 209 restarter_str_ct_ev_core,
209 210 "ct_ev_core",
210 211 "a process dumped core"
211 212 },
212 213
213 214 /*
214 215 * A transition 'online -> offline' due to an empty process contract,
215 216 * i.e., the last process in a contract type service has exited.
216 217 */
217 218 {
218 219 restarter_str_ct_ev_exit,
219 220 "ct_ev_exit",
220 221 "all processes in the service have exited"
221 222 },
222 223
223 224 /*
224 225 * A transition 'online -> offline' due to a hardware error.
225 226 */
226 227 {
227 228 restarter_str_ct_ev_hwerr,
228 229 "ct_ev_hwerr",
229 230 "a process was killed due to uncorrectable hardware error"
230 231 },
231 232
232 233 /*
233 234 * A transition 'online -> offline' due to a process in the service
234 235 * having received a fatal signal originating from outside the
235 236 * service process contract.
236 237 */
237 238 {
238 239 restarter_str_ct_ev_signal,
239 240 "ct_ev_signal",
240 241 "a process received a fatal signal from outside the service"
241 242 },
242 243
243 244 /*
244 245 * A transition 'offline -> online' when all dependencies for the
245 246 * service have been met.
246 247 */
247 248 {
248 249 restarter_str_dependencies_satisfied,
249 250 "dependencies_satisfied",
250 251 "all dependencies have been satisfied"
251 252 },
252 253
253 254 /*
254 255 * A transition 'online -> offline' because some dependency for the
255 256 * service is no-longer met.
256 257 */
257 258 {
258 259 restarter_str_dependency_activity,
259 260 "dependency_activity",
260 261 "a dependency activity required a stop"
261 262 },
262 263
263 264 /*
264 265 * A transition to maintenance state due to a cycle in the
265 266 * service dependencies.
266 267 */
267 268 {
268 269 restarter_str_dependency_cycle,
269 270 "dependency_cycle",
270 271 "a dependency cycle exists"
271 272 },
272 273
273 274 /*
274 275 * A transition 'online -> offline -> disabled' due to a
275 276 * 'svcadm disable [-t] <fmri>' or smf_disable_instance(3SCF) call.
276 277 */
277 278 {
278 279 restarter_str_disable_request,
279 280 "disable_request",
280 281 "a disable was requested"
281 282 },
282 283
283 284 /*
284 285 * A transition 'disabled -> offline' due to a
285 286 * 'svcadm enable [-t] <fmri>' or smf_enable_instance(3SCF) call.
286 287 */
287 288 {
288 289 restarter_str_enable_request,
289 290 "enable_request",
290 291 "an enable was requested"
291 292 },
292 293
293 294 /*
294 295 * A transition to maintenance state when a method fails
295 296 * repeatedly for a retryable reason.
296 297 */
297 298 {
298 299 restarter_str_fault_threshold_reached,
299 300 "fault_threshold_reached",
300 301 "a method is failing in a retryable manner but too often"
301 302 },
302 303
303 304 /*
304 305 * A transition to uninitialized state when startd reads the service
305 306 * configuration and inserts it into the graph engine.
306 307 */
307 308 {
308 309 restarter_str_insert_in_graph,
309 310 "insert_in_graph",
310 311 "the instance was inserted in the graph"
311 312 },
312 313
313 314 /*
314 315 * A transition to maintenance state due to an invalid dependency
315 316 * declared for the service.
316 317 */
317 318 {
318 319 restarter_str_invalid_dependency,
319 320 "invalid_dependency",
320 321 "a service has an invalid dependency"
321 322 },
322 323
323 324 /*
324 325 * A transition to maintenance state because the service-declared
325 326 * restarter is invalid.
326 327 */
327 328 {
328 329 restarter_str_invalid_restarter,
329 330 "invalid_restarter",
330 331 "the service restarter is invalid"
331 332 },
332 333
333 334 /*
334 335 * A transition to maintenance state because a restarter method
335 336 * exited with one of SMF_EXIT_ERR_CONFIG, SMF_EXIT_ERR_NOSMF,
336 337 * SMF_EXIT_ERR_PERM, or SMF_EXIT_ERR_FATAL.
337 338 */
338 339 {
339 340 restarter_str_method_failed,
340 341 "method_failed",
341 342 "a start, stop or refresh method failed"
342 343 },
343 344
344 345 /*
345 346 * A transition 'uninitialized -> {disabled|offline}' after
346 347 * "insert_in_graph" to match the state configured in the
347 348 * repository.
348 349 */
349 350 {
350 351 restarter_str_per_configuration,
351 352 "per_configuration",
352 353 "the SMF repository configuration specifies this state"
353 354 },
354 355
355 356 /*
356 357 * Refresh requested - no state change.
357 358 */
358 359 {
359 360 restarter_str_refresh,
360 361 NULL,
361 362 "a refresh was requested (no change of state)"
362 363 },
363 364
364 365 /*
365 366 * A transition 'online -> offline -> online' due to a
366 367 * 'svcadm restart <fmri> or equivlaent libscf API call.
367 368 * Both the 'online -> offline' and 'offline -> online' transtions
368 369 * specify this reason.
369 370 */
370 371 {
371 372 restarter_str_restart_request,
372 373 "restart_request",
373 374 "a restart was requested"
374 375 },
375 376
376 377 /*
377 378 * A transition to maintenance state because the start method is
378 379 * being executed successfully but too frequently.
379 380 */
380 381 {
381 382 restarter_str_restarting_too_quickly,
382 383 "restarting_too_quickly",
383 384 "the instance is restarting too quickly"
384 385 },
385 386
386 387 /*
387 388 * A transition to maintenance state due a service requesting
388 389 * 'svcadm mark maintenance <fmri>' or equivalent libscf API call.
389 390 * A command line 'svcadm mark maintenance <fmri>' does not produce
390 391 * this reason - it produces administrative_request instead.
391 392 */
392 393 {
393 394 restarter_str_service_request,
394 395 "service_request",
395 396 "maintenance was requested by another service"
396 397 },
397 398
398 399 /*
399 400 * An instanced inserted into the graph at its existing state
400 401 * during a startd restart - no state change.
401 402 */
402 403 {
403 404 restarter_str_startd_restart,
404 405 NULL,
405 406 "the instance was inserted in the graph due to startd restart"
406 407 }
407 408 };
408 409
409 410 uint32_t
410 411 restarter_str_version(void)
411 412 {
412 413 return (RESTARTER_STRING_VERSION);
413 414 }
414 415
415 416 const char *
416 417 restarter_get_str_short(restarter_str_t key)
417 418 {
418 419 int i;
419 420 for (i = 0; i < sizeof (restarter_str) /
420 421 sizeof (struct restarter_state_transition_reason); i++)
421 422 if (key == restarter_str[i].str_key)
422 423 return (restarter_str[i].str_short);
423 424 return (NULL);
424 425 }
425 426
426 427 const char *
427 428 restarter_get_str_long(restarter_str_t key)
428 429 {
429 430 int i;
430 431 for (i = 0; i < sizeof (restarter_str) /
431 432 sizeof (struct restarter_state_transition_reason); i++)
432 433 if (key == restarter_str[i].str_key)
433 434 return (dgettext(TEXT_DOMAIN,
434 435 restarter_str[i].str_long));
435 436 return (NULL);
436 437 }
437 438
438 439 /*
439 440 * A static no memory error message mc_error_t structure
440 441 * to be used in cases when memory errors are to be returned
441 442 * This avoids the need to attempt to allocate memory for the
442 443 * message, therefore getting into a cycle of no memory failures.
443 444 */
444 445 mc_error_t mc_nomem_err = {
445 446 0, ENOMEM, sizeof ("Out of memory") - 1, "Out of memory"
446 447 };
447 448
448 449 static const char * const allocfail = "Allocation failure.\n";
449 450 static const char * const rcbroken = "Repository connection broken.\n";
450 451
451 452 static int method_context_safety = 0; /* Can safely call pools/projects. */
452 453
453 454 int ndebug = 1;
454 455
455 456 /* PRINTFLIKE3 */
456 457 static mc_error_t *
457 458 mc_error_create(mc_error_t *e, int type, const char *format, ...)
458 459 {
459 460 mc_error_t *le;
460 461 va_list args;
461 462 int size;
462 463
463 464 /*
464 465 * If the type is ENOMEM and format is NULL, then
465 466 * go ahead and return the default nomem error.
466 467 * Otherwise, attempt to allocate the memory and if
467 468 * that fails then there is no reason to continue.
468 469 */
469 470 if (type == ENOMEM && format == NULL)
470 471 return (&mc_nomem_err);
471 472
472 473 if (e == NULL && (le = malloc(sizeof (mc_error_t))) == NULL)
473 474 return (&mc_nomem_err);
474 475 else
475 476 le = e;
476 477
477 478 le->type = type;
478 479 le->destroy = 1;
479 480 va_start(args, format);
480 481 size = vsnprintf(NULL, 0, format, args) + 1;
481 482 if (size >= RESTARTER_ERRMSGSZ) {
482 483 if ((le = realloc(e, sizeof (mc_error_t) +
483 484 (size - RESTARTER_ERRMSGSZ))) == NULL) {
484 485 size = RESTARTER_ERRMSGSZ - 1;
485 486 le = e;
486 487 }
487 488 }
488 489
489 490 le->size = size;
490 491 (void) vsnprintf(le->msg, le->size, format, args);
491 492 va_end(args);
492 493
493 494 return (le);
494 495 }
495 496
496 497 void
497 498 restarter_mc_error_destroy(mc_error_t *mc_err)
498 499 {
499 500 if (mc_err == NULL)
500 501 return;
501 502
502 503 /*
503 504 * If the error messages was allocated then free.
504 505 */
505 506 if (mc_err->destroy) {
506 507 free(mc_err);
507 508 }
508 509 }
509 510
510 511 static void
511 512 free_restarter_event_handle(struct restarter_event_handle *h)
512 513 {
513 514 if (h == NULL)
514 515 return;
515 516
516 517 /*
517 518 * Just free the memory -- don't unbind the sysevent handle,
518 519 * as otherwise events may be lost if this is just a restarter
519 520 * restart.
520 521 */
521 522
522 523 if (h->reh_restarter_name != NULL)
523 524 free(h->reh_restarter_name);
524 525 if (h->reh_delegate_channel_name != NULL)
525 526 free(h->reh_delegate_channel_name);
526 527 if (h->reh_delegate_subscriber_id != NULL)
527 528 free(h->reh_delegate_subscriber_id);
528 529 if (h->reh_master_channel_name != NULL)
529 530 free(h->reh_master_channel_name);
530 531 if (h->reh_master_subscriber_id != NULL)
531 532 free(h->reh_master_subscriber_id);
532 533
533 534 free(h);
534 535 }
535 536
536 537 char *
537 538 _restarter_get_channel_name(const char *fmri, int type)
538 539 {
539 540 char *name;
540 541 char *chan_name = malloc(MAX_CHNAME_LEN);
541 542 char prefix_name[3];
542 543 int i;
543 544
544 545 if (chan_name == NULL)
545 546 return (NULL);
546 547
547 548 if (type == RESTARTER_CHANNEL_DELEGATE)
548 549 (void) strcpy(prefix_name, "d_");
549 550 else if (type == RESTARTER_CHANNEL_MASTER)
550 551 (void) strcpy(prefix_name, "m_");
551 552 else {
552 553 free(chan_name);
553 554 return (NULL);
554 555 }
555 556
556 557 /*
557 558 * Create a unique name
558 559 *
559 560 * Use the entire name, using a replacement of the /
560 561 * characters to get a better name.
561 562 *
562 563 * Remove the svc:/ from the beginning as this really
563 564 * isn't going to provide any uniqueness...
564 565 *
565 566 * An fmri name greater than MAX_CHNAME_LEN is going
566 567 * to be rejected as too long for the chan_name below
567 568 * in the snprintf call.
568 569 */
569 570 if ((name = strdup(strchr(fmri, '/') + 1)) == NULL) {
570 571 free(chan_name);
571 572 return (NULL);
572 573 }
573 574 i = 0;
574 575 while (name[i]) {
575 576 if (name[i] == '/') {
576 577 name[i] = '_';
577 578 }
578 579
579 580 i++;
580 581 }
581 582
582 583 /*
583 584 * Should check for [a-z],[A-Z],[0-9],.,_,-,:
584 585 */
585 586
586 587 if (snprintf(chan_name, MAX_CHNAME_LEN, "com.sun:scf:%s%s",
587 588 prefix_name, name) > MAX_CHNAME_LEN) {
588 589 free(chan_name);
589 590 chan_name = NULL;
590 591 }
591 592
592 593 free(name);
593 594 return (chan_name);
594 595 }
595 596
596 597 int
597 598 cb(sysevent_t *syse, void *cookie)
598 599 {
599 600 restarter_event_handle_t *h = (restarter_event_handle_t *)cookie;
600 601 restarter_event_t *e;
601 602 nvlist_t *attr_list = NULL;
602 603 int ret = 0;
603 604
604 605 e = uu_zalloc(sizeof (restarter_event_t));
605 606 if (e == NULL)
606 607 uu_die(allocfail);
607 608 e->re_event_handle = h;
608 609 e->re_sysevent = syse;
609 610
610 611 if (sysevent_get_attr_list(syse, &attr_list) != 0)
611 612 uu_die(allocfail);
612 613
613 614 if ((nvlist_lookup_uint32(attr_list, RESTARTER_NAME_TYPE,
614 615 &(e->re_type)) != 0) ||
615 616 (nvlist_lookup_string(attr_list,
616 617 RESTARTER_NAME_INSTANCE, &(e->re_instance_name)) != 0)) {
617 618 uu_warn("%s: Can't decode nvlist for event %p\n",
618 619 h->reh_restarter_name, (void *)syse);
619 620
620 621 ret = 0;
621 622 } else {
622 623 ret = h->reh_handler(e);
623 624 }
624 625
625 626 uu_free(e);
626 627 nvlist_free(attr_list);
627 628 return (ret);
628 629 }
629 630
630 631 /*
631 632 * restarter_bind_handle(uint32_t, char *, int (*)(restarter_event_t *), int,
632 633 * restarter_event_handle_t **)
633 634 *
634 635 * Bind to a delegated restarter event channel.
635 636 * Each delegated restarter gets its own channel for resource management.
636 637 *
637 638 * Returns 0 on success or
638 639 * ENOTSUP version mismatch
639 640 * EINVAL restarter_name or event_handle is NULL
640 641 * ENOMEM out of memory, too many channels, or too many subscriptions
641 642 * EBUSY sysevent_evc_bind() could not establish binding
642 643 * EFAULT internal sysevent_evc_bind()/sysevent_evc_subscribe() error
643 644 * EMFILE out of file descriptors
644 645 * EPERM insufficient privilege for sysevent_evc_bind()
645 646 * EEXIST already subscribed
646 647 */
647 648 int
648 649 restarter_bind_handle(uint32_t version, const char *restarter_name,
649 650 int (*event_handler)(restarter_event_t *), int flags,
650 651 restarter_event_handle_t **rehp)
651 652 {
652 653 restarter_event_handle_t *h;
653 654 size_t sz;
654 655 int err;
655 656
656 657 if (version != RESTARTER_EVENT_VERSION)
657 658 return (ENOTSUP);
658 659
659 660 if (restarter_name == NULL || event_handler == NULL)
660 661 return (EINVAL);
661 662
662 663 if (flags & RESTARTER_FLAG_DEBUG)
663 664 ndebug++;
664 665
665 666 if ((h = uu_zalloc(sizeof (restarter_event_handle_t))) == NULL)
666 667 return (ENOMEM);
667 668
668 669 h->reh_delegate_subscriber_id = malloc(MAX_SUBID_LEN);
669 670 h->reh_master_subscriber_id = malloc(MAX_SUBID_LEN);
670 671 h->reh_restarter_name = strdup(restarter_name);
671 672 if (h->reh_delegate_subscriber_id == NULL ||
672 673 h->reh_master_subscriber_id == NULL ||
673 674 h->reh_restarter_name == NULL) {
674 675 free_restarter_event_handle(h);
675 676 return (ENOMEM);
676 677 }
677 678
678 679 sz = strlcpy(h->reh_delegate_subscriber_id, "del", MAX_SUBID_LEN);
679 680 assert(sz < MAX_SUBID_LEN);
680 681 sz = strlcpy(h->reh_master_subscriber_id, "master", MAX_SUBID_LEN);
681 682 assert(sz < MAX_SUBID_LEN);
682 683
683 684 h->reh_delegate_channel_name =
684 685 _restarter_get_channel_name(restarter_name,
685 686 RESTARTER_CHANNEL_DELEGATE);
686 687 h->reh_master_channel_name =
687 688 _restarter_get_channel_name(restarter_name,
688 689 RESTARTER_CHANNEL_MASTER);
689 690
690 691 if (h->reh_delegate_channel_name == NULL ||
691 692 h->reh_master_channel_name == NULL) {
692 693 free_restarter_event_handle(h);
693 694 return (ENOMEM);
694 695 }
695 696
696 697 if (sysevent_evc_bind(h->reh_delegate_channel_name,
697 698 &h->reh_delegate_channel, EVCH_CREAT|EVCH_HOLD_PEND) != 0) {
698 699 err = errno;
699 700 assert(err != EINVAL);
700 701 assert(err != ENOENT);
701 702 free_restarter_event_handle(h);
702 703 return (err);
703 704 }
704 705
705 706 if (sysevent_evc_bind(h->reh_master_channel_name,
706 707 &h->reh_master_channel, EVCH_CREAT|EVCH_HOLD_PEND) != 0) {
707 708 err = errno;
708 709 assert(err != EINVAL);
709 710 assert(err != ENOENT);
710 711 free_restarter_event_handle(h);
711 712 return (err);
712 713 }
713 714
714 715 h->reh_handler = event_handler;
715 716
716 717 assert(strlen(restarter_name) <= MAX_CLASS_LEN - 1);
717 718 assert(strlen(h->reh_delegate_subscriber_id) <= MAX_SUBID_LEN - 1);
718 719 assert(strlen(h->reh_master_subscriber_id) <= MAX_SUBID_LEN - 1);
719 720
720 721 if (sysevent_evc_subscribe(h->reh_delegate_channel,
721 722 h->reh_delegate_subscriber_id, EC_ALL, cb, h, EVCH_SUB_KEEP) != 0) {
722 723 err = errno;
723 724 assert(err != EINVAL);
724 725 free_restarter_event_handle(h);
725 726 return (err);
726 727 }
727 728
728 729 *rehp = h;
729 730 return (0);
730 731 }
731 732
732 733 restarter_event_handle_t *
733 734 restarter_event_get_handle(restarter_event_t *e)
734 735 {
735 736 assert(e != NULL && e->re_event_handle != NULL);
736 737 return (e->re_event_handle);
737 738 }
738 739
739 740 restarter_event_type_t
740 741 restarter_event_get_type(restarter_event_t *e)
741 742 {
742 743 assert(e != NULL);
743 744 return (e->re_type);
744 745 }
745 746
746 747 ssize_t
747 748 restarter_event_get_instance(restarter_event_t *e, char *inst, size_t sz)
748 749 {
749 750 assert(e != NULL && inst != NULL);
750 751 return ((ssize_t)strlcpy(inst, e->re_instance_name, sz));
751 752 }
752 753
753 754 int
754 755 restarter_event_get_current_states(restarter_event_t *e,
755 756 restarter_instance_state_t *state, restarter_instance_state_t *next_state)
756 757 {
757 758 if (e == NULL)
758 759 return (-1);
759 760 *state = e->re_state;
760 761 *next_state = e->re_next_state;
761 762 return (0);
762 763 }
763 764
764 765 /*
765 766 * restarter_event_publish_retry() is a wrapper around sysevent_evc_publish().
766 767 * In case, the event cannot be sent at the first attempt (sysevent_evc_publish
767 768 * returned EAGAIN - sysevent queue full), this function retries a few time
768 769 * and return ENOSPC if it reaches the retry limit.
769 770 *
770 771 * The arguments to this function map the arguments of sysevent_evc_publish().
771 772 *
772 773 * On success, return 0. On error, return
773 774 *
774 775 * EFAULT - internal sysevent_evc_publish() error
775 776 * ENOMEM - internal sysevent_evc_publish() error
776 777 * EBADF - scp is invalid (sysevent_evc_publish() returned EINVAL)
777 778 * ENOSPC - sysevent queue full (sysevent_evc_publish() returned EAGAIN)
778 779 */
779 780 int
780 781 restarter_event_publish_retry(evchan_t *scp, const char *class,
781 782 const char *subclass, const char *vendor, const char *pub_name,
782 783 nvlist_t *attr_list, uint32_t flags)
783 784 {
784 785 int retries, ret;
785 786 useconds_t retry_int = INITIAL_COMMIT_RETRY_INT;
786 787
787 788 for (retries = 0; retries < MAX_COMMIT_RETRIES; retries++) {
788 789 ret = sysevent_evc_publish(scp, class, subclass, vendor,
789 790 pub_name, attr_list, flags);
790 791 if (ret == 0)
791 792 break;
792 793
793 794 switch (ret) {
794 795 case EAGAIN:
795 796 /* Queue is full */
796 797 (void) usleep(retry_int);
797 798
798 799 retry_int = min(retry_int * 2, MAX_COMMIT_RETRY_INT);
799 800 break;
800 801
801 802 case EINVAL:
802 803 ret = EBADF;
803 804 /* FALLTHROUGH */
804 805
805 806 case EFAULT:
806 807 case ENOMEM:
807 808 return (ret);
808 809
809 810 case EOVERFLOW:
810 811 default:
811 812 /* internal error - abort */
812 813 bad_fail("sysevent_evc_publish", ret);
813 814 }
814 815 }
815 816
816 817 if (retries == MAX_COMMIT_RETRIES)
817 818 ret = ENOSPC;
818 819
819 820 return (ret);
820 821 }
821 822
822 823 /*
823 824 * Commit the state, next state, and auxiliary state into the repository.
824 825 * Let the graph engine know about the state change and error. On success,
825 826 * return 0. On error, return
826 827 * EPROTO - librestart compiled against different libscf
827 828 * ENOMEM - out of memory
828 829 * - repository server out of resources
829 830 * ENOTACTIVE - repository server not running
830 831 * ECONNABORTED - repository connection established, but then broken
831 832 * - unknown libscf error
832 833 * ENOENT - inst does not exist in the repository
833 834 * EPERM - insufficient permissions
834 835 * EACCESS - backend access denied
835 836 * EROFS - backend is readonly
836 837 * EFAULT - internal sysevent_evc_publish() error
837 838 * EBADF - h is invalid (sysevent_evc_publish() returned EINVAL)
838 839 * ENOSPC - sysevent queue full (sysevent_evc_publish() returned EAGAIN)
839 840 */
840 841 int
841 842 restarter_set_states(restarter_event_handle_t *h, const char *inst,
842 843 restarter_instance_state_t cur_state,
843 844 restarter_instance_state_t new_cur_state,
844 845 restarter_instance_state_t next_state,
845 846 restarter_instance_state_t new_next_state, restarter_error_t e,
846 847 restarter_str_t aux)
847 848 {
848 849 nvlist_t *attr;
849 850 scf_handle_t *scf_h;
850 851 instance_data_t id;
851 852 int ret = 0;
852 853 const char *p = restarter_get_str_short(aux);
853 854
854 855 assert(h->reh_master_channel != NULL);
855 856 assert(h->reh_master_channel_name != NULL);
856 857 assert(h->reh_master_subscriber_id != NULL);
857 858
858 859 if ((scf_h = scf_handle_create(SCF_VERSION)) == NULL) {
859 860 switch (scf_error()) {
860 861 case SCF_ERROR_VERSION_MISMATCH:
861 862 return (EPROTO);
862 863
863 864 case SCF_ERROR_NO_MEMORY:
864 865 return (ENOMEM);
865 866
866 867 default:
867 868 bad_fail("scf_handle_create", scf_error());
868 869 }
869 870 }
870 871
871 872 if (scf_handle_bind(scf_h) == -1) {
872 873 scf_handle_destroy(scf_h);
873 874 switch (scf_error()) {
874 875 case SCF_ERROR_NO_SERVER:
875 876 return (ENOTACTIVE);
876 877
877 878 case SCF_ERROR_NO_RESOURCES:
878 879 return (ENOMEM);
879 880
880 881 case SCF_ERROR_INVALID_ARGUMENT:
881 882 case SCF_ERROR_IN_USE:
882 883 default:
883 884 bad_fail("scf_handle_bind", scf_error());
884 885 }
885 886 }
886 887
887 888 if (nvlist_alloc(&attr, NV_UNIQUE_NAME, 0) != 0 ||
888 889 nvlist_add_int32(attr, RESTARTER_NAME_STATE, new_cur_state) != 0 ||
889 890 nvlist_add_int32(attr, RESTARTER_NAME_NEXT_STATE, new_next_state)
890 891 != 0 ||
891 892 nvlist_add_int32(attr, RESTARTER_NAME_ERROR, e) != 0 ||
892 893 nvlist_add_string(attr, RESTARTER_NAME_INSTANCE, inst) != 0 ||
893 894 nvlist_add_int32(attr, RESTARTER_NAME_REASON, aux) != 0) {
894 895 ret = ENOMEM;
895 896 } else {
896 897 id.i_fmri = inst;
897 898 id.i_state = cur_state;
898 899 id.i_next_state = next_state;
899 900
900 901 ret = _restarter_commit_states(scf_h, &id, new_cur_state,
901 902 new_next_state, p);
902 903
903 904 if (ret == 0) {
904 905 ret = restarter_event_publish_retry(
905 906 h->reh_master_channel, "master", "state_change",
906 907 "com.sun", "librestart", attr, EVCH_NOSLEEP);
907 908 }
908 909 }
909 910
910 911 nvlist_free(attr);
911 912 (void) scf_handle_unbind(scf_h);
912 913 scf_handle_destroy(scf_h);
913 914
914 915 return (ret);
915 916 }
916 917
917 918 restarter_instance_state_t
918 919 restarter_string_to_state(char *string)
919 920 {
920 921 assert(string != NULL);
921 922
922 923 if (strcmp(string, SCF_STATE_STRING_NONE) == 0)
923 924 return (RESTARTER_STATE_NONE);
924 925 else if (strcmp(string, SCF_STATE_STRING_UNINIT) == 0)
925 926 return (RESTARTER_STATE_UNINIT);
926 927 else if (strcmp(string, SCF_STATE_STRING_MAINT) == 0)
927 928 return (RESTARTER_STATE_MAINT);
928 929 else if (strcmp(string, SCF_STATE_STRING_OFFLINE) == 0)
929 930 return (RESTARTER_STATE_OFFLINE);
930 931 else if (strcmp(string, SCF_STATE_STRING_DISABLED) == 0)
931 932 return (RESTARTER_STATE_DISABLED);
932 933 else if (strcmp(string, SCF_STATE_STRING_ONLINE) == 0)
933 934 return (RESTARTER_STATE_ONLINE);
934 935 else if (strcmp(string, SCF_STATE_STRING_DEGRADED) == 0)
935 936 return (RESTARTER_STATE_DEGRADED);
936 937 else {
937 938 return (RESTARTER_STATE_NONE);
938 939 }
939 940 }
940 941
941 942 ssize_t
942 943 restarter_state_to_string(restarter_instance_state_t state, char *string,
943 944 size_t len)
944 945 {
945 946 assert(string != NULL);
946 947
947 948 if (state == RESTARTER_STATE_NONE)
948 949 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_NONE, len));
949 950 else if (state == RESTARTER_STATE_UNINIT)
950 951 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_UNINIT, len));
951 952 else if (state == RESTARTER_STATE_MAINT)
952 953 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_MAINT, len));
953 954 else if (state == RESTARTER_STATE_OFFLINE)
954 955 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_OFFLINE,
955 956 len));
956 957 else if (state == RESTARTER_STATE_DISABLED)
957 958 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_DISABLED,
958 959 len));
959 960 else if (state == RESTARTER_STATE_ONLINE)
960 961 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_ONLINE, len));
961 962 else if (state == RESTARTER_STATE_DEGRADED)
962 963 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_DEGRADED,
963 964 len));
964 965 else
965 966 return ((ssize_t)strlcpy(string, "unknown", len));
966 967 }
967 968
968 969 /*
969 970 * Sets pg to the name property group of s_inst. If it doesn't exist, it is
970 971 * added.
971 972 *
972 973 * Fails with
973 974 * ECONNABORTED - repository disconnection or unknown libscf error
974 975 * EBADF - inst is not set
975 976 * ECANCELED - inst is deleted
976 977 * EPERM - permission is denied
977 978 * EACCES - backend denied access
978 979 * EROFS - backend readonly
979 980 */
980 981 static int
981 982 instance_get_or_add_pg(scf_instance_t *inst, const char *name,
982 983 const char *type, uint32_t flags, scf_propertygroup_t *pg)
983 984 {
984 985 again:
985 986 if (scf_instance_get_pg(inst, name, pg) == 0)
986 987 return (0);
987 988
988 989 switch (scf_error()) {
989 990 case SCF_ERROR_CONNECTION_BROKEN:
990 991 default:
991 992 return (ECONNABORTED);
992 993
993 994 case SCF_ERROR_NOT_SET:
994 995 return (EBADF);
995 996
996 997 case SCF_ERROR_DELETED:
997 998 return (ECANCELED);
998 999
999 1000 case SCF_ERROR_NOT_FOUND:
1000 1001 break;
1001 1002
1002 1003 case SCF_ERROR_HANDLE_MISMATCH:
1003 1004 case SCF_ERROR_INVALID_ARGUMENT:
1004 1005 bad_fail("scf_instance_get_pg", scf_error());
1005 1006 }
1006 1007
1007 1008 if (scf_instance_add_pg(inst, name, type, flags, pg) == 0)
1008 1009 return (0);
1009 1010
1010 1011 switch (scf_error()) {
1011 1012 case SCF_ERROR_CONNECTION_BROKEN:
1012 1013 default:
1013 1014 return (ECONNABORTED);
1014 1015
1015 1016 case SCF_ERROR_DELETED:
1016 1017 return (ECANCELED);
1017 1018
1018 1019 case SCF_ERROR_EXISTS:
1019 1020 goto again;
1020 1021
1021 1022 case SCF_ERROR_PERMISSION_DENIED:
1022 1023 return (EPERM);
1023 1024
1024 1025 case SCF_ERROR_BACKEND_ACCESS:
1025 1026 return (EACCES);
1026 1027
1027 1028 case SCF_ERROR_BACKEND_READONLY:
1028 1029 return (EROFS);
1029 1030
1030 1031 case SCF_ERROR_HANDLE_MISMATCH:
1031 1032 case SCF_ERROR_INVALID_ARGUMENT:
1032 1033 case SCF_ERROR_NOT_SET: /* should be caught above */
1033 1034 bad_fail("scf_instance_add_pg", scf_error());
1034 1035 }
1035 1036
1036 1037 return (0);
1037 1038 }
1038 1039
1039 1040 /*
1040 1041 * Fails with
1041 1042 * ECONNABORTED
1042 1043 * ECANCELED - pg was deleted
1043 1044 */
1044 1045 static int
1045 1046 tx_set_value(scf_transaction_t *tx, scf_transaction_entry_t *ent,
1046 1047 const char *pname, scf_type_t ty, scf_value_t *val)
1047 1048 {
1048 1049 int r;
1049 1050
1050 1051 for (;;) {
1051 1052 if (scf_transaction_property_change_type(tx, ent, pname,
1052 1053 ty) == 0)
1053 1054 break;
1054 1055
1055 1056 switch (scf_error()) {
1056 1057 case SCF_ERROR_CONNECTION_BROKEN:
1057 1058 default:
1058 1059 return (ECONNABORTED);
1059 1060
1060 1061 case SCF_ERROR_DELETED:
1061 1062 return (ECANCELED);
1062 1063
1063 1064 case SCF_ERROR_NOT_FOUND:
1064 1065 break;
1065 1066
1066 1067 case SCF_ERROR_HANDLE_MISMATCH:
1067 1068 case SCF_ERROR_INVALID_ARGUMENT:
1068 1069 case SCF_ERROR_IN_USE:
1069 1070 case SCF_ERROR_NOT_SET:
1070 1071 bad_fail("scf_transaction_property_change_type",
1071 1072 scf_error());
1072 1073 }
1073 1074
1074 1075 if (scf_transaction_property_new(tx, ent, pname, ty) == 0)
1075 1076 break;
1076 1077
1077 1078 switch (scf_error()) {
1078 1079 case SCF_ERROR_CONNECTION_BROKEN:
1079 1080 default:
1080 1081 return (ECONNABORTED);
1081 1082
1082 1083 case SCF_ERROR_DELETED:
1083 1084 return (ECANCELED);
1084 1085
1085 1086 case SCF_ERROR_EXISTS:
1086 1087 break;
1087 1088
1088 1089 case SCF_ERROR_HANDLE_MISMATCH:
1089 1090 case SCF_ERROR_INVALID_ARGUMENT:
1090 1091 case SCF_ERROR_IN_USE:
1091 1092 case SCF_ERROR_NOT_SET:
1092 1093 bad_fail("scf_transaction_property_new", scf_error());
1093 1094 }
1094 1095 }
1095 1096
1096 1097 r = scf_entry_add_value(ent, val);
1097 1098 assert(r == 0);
1098 1099
1099 1100 return (0);
1100 1101 }
1101 1102
1102 1103 /*
1103 1104 * Commit new_state, new_next_state, and aux to the repository for id. If
1104 1105 * successful, also set id's state and next-state as given, and return 0.
1105 1106 * Fails with
1106 1107 * ENOMEM - out of memory
1107 1108 * ECONNABORTED - repository connection broken
1108 1109 * - unknown libscf error
1109 1110 * EINVAL - id->i_fmri is invalid or not an instance FMRI
1110 1111 * ENOENT - id->i_fmri does not exist
1111 1112 * EPERM - insufficient permissions
1112 1113 * EACCES - backend access denied
1113 1114 * EROFS - backend is readonly
1114 1115 */
1115 1116 int
1116 1117 _restarter_commit_states(scf_handle_t *h, instance_data_t *id,
1117 1118 restarter_instance_state_t new_state,
1118 1119 restarter_instance_state_t new_state_next, const char *aux)
1119 1120 {
1120 1121 char str_state[MAX_SCF_STATE_STRING_SZ];
1121 1122 char str_new_state[MAX_SCF_STATE_STRING_SZ];
1122 1123 char str_state_next[MAX_SCF_STATE_STRING_SZ];
1123 1124 char str_new_state_next[MAX_SCF_STATE_STRING_SZ];
1124 1125 int ret = 0, r;
1125 1126 struct timeval now;
1126 1127 ssize_t sz;
1127 1128
1128 1129 scf_transaction_t *t = NULL;
1129 1130 scf_transaction_entry_t *t_state = NULL, *t_state_next = NULL;
1130 1131 scf_transaction_entry_t *t_stime = NULL, *t_aux = NULL;
1131 1132 scf_value_t *v_state = NULL, *v_state_next = NULL, *v_stime = NULL;
1132 1133 scf_value_t *v_aux = NULL;
1133 1134 scf_instance_t *s_inst = NULL;
1134 1135 scf_propertygroup_t *pg = NULL;
1135 1136
1136 1137 assert(new_state != RESTARTER_STATE_NONE);
1137 1138
1138 1139 if ((s_inst = scf_instance_create(h)) == NULL ||
1139 1140 (pg = scf_pg_create(h)) == NULL ||
1140 1141 (t = scf_transaction_create(h)) == NULL ||
1141 1142 (t_state = scf_entry_create(h)) == NULL ||
1142 1143 (t_state_next = scf_entry_create(h)) == NULL ||
1143 1144 (t_stime = scf_entry_create(h)) == NULL ||
1144 1145 (t_aux = scf_entry_create(h)) == NULL ||
1145 1146 (v_state = scf_value_create(h)) == NULL ||
1146 1147 (v_state_next = scf_value_create(h)) == NULL ||
1147 1148 (v_stime = scf_value_create(h)) == NULL ||
1148 1149 (v_aux = scf_value_create(h)) == NULL) {
1149 1150 ret = ENOMEM;
1150 1151 goto out;
1151 1152 }
1152 1153
1153 1154 sz = restarter_state_to_string(new_state, str_new_state,
1154 1155 sizeof (str_new_state));
1155 1156 assert(sz < sizeof (str_new_state));
1156 1157 sz = restarter_state_to_string(new_state_next, str_new_state_next,
1157 1158 sizeof (str_new_state_next));
1158 1159 assert(sz < sizeof (str_new_state_next));
1159 1160 sz = restarter_state_to_string(id->i_state, str_state,
1160 1161 sizeof (str_state));
1161 1162 assert(sz < sizeof (str_state));
1162 1163 sz = restarter_state_to_string(id->i_next_state, str_state_next,
1163 1164 sizeof (str_state_next));
1164 1165 assert(sz < sizeof (str_state_next));
1165 1166
1166 1167 ret = gettimeofday(&now, NULL);
1167 1168 assert(ret != -1);
1168 1169
1169 1170 if (scf_handle_decode_fmri(h, id->i_fmri, NULL, NULL, s_inst,
1170 1171 NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) {
1171 1172 switch (scf_error()) {
1172 1173 case SCF_ERROR_CONNECTION_BROKEN:
1173 1174 default:
1174 1175 ret = ECONNABORTED;
1175 1176 break;
1176 1177
1177 1178 case SCF_ERROR_INVALID_ARGUMENT:
1178 1179 case SCF_ERROR_CONSTRAINT_VIOLATED:
1179 1180 ret = EINVAL;
1180 1181 break;
1181 1182
1182 1183 case SCF_ERROR_NOT_FOUND:
1183 1184 ret = ENOENT;
1184 1185 break;
1185 1186
1186 1187 case SCF_ERROR_HANDLE_MISMATCH:
1187 1188 bad_fail("scf_handle_decode_fmri", scf_error());
1188 1189 }
1189 1190 goto out;
1190 1191 }
1191 1192
1192 1193
1193 1194 if (scf_value_set_astring(v_state, str_new_state) != 0 ||
1194 1195 scf_value_set_astring(v_state_next, str_new_state_next) != 0)
1195 1196 bad_fail("scf_value_set_astring", scf_error());
1196 1197
1197 1198 if (aux) {
1198 1199 if (scf_value_set_astring(v_aux, aux) != 0)
1199 1200 bad_fail("scf_value_set_astring", scf_error());
1200 1201 }
1201 1202
1202 1203 if (scf_value_set_time(v_stime, now.tv_sec, now.tv_usec * 1000) != 0)
1203 1204 bad_fail("scf_value_set_time", scf_error());
1204 1205
1205 1206 add_pg:
1206 1207 switch (r = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
1207 1208 SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg)) {
1208 1209 case 0:
1209 1210 break;
1210 1211
1211 1212 case ECONNABORTED:
1212 1213 case EPERM:
1213 1214 case EACCES:
1214 1215 case EROFS:
1215 1216 ret = r;
1216 1217 goto out;
1217 1218
1218 1219 case ECANCELED:
1219 1220 ret = ENOENT;
1220 1221 goto out;
1221 1222
1222 1223 case EBADF:
1223 1224 default:
1224 1225 bad_fail("instance_get_or_add_pg", r);
1225 1226 }
1226 1227
1227 1228 for (;;) {
1228 1229 if (scf_transaction_start(t, pg) != 0) {
1229 1230 switch (scf_error()) {
1230 1231 case SCF_ERROR_CONNECTION_BROKEN:
1231 1232 default:
1232 1233 ret = ECONNABORTED;
1233 1234 goto out;
1234 1235
1235 1236 case SCF_ERROR_NOT_SET:
1236 1237 goto add_pg;
1237 1238
1238 1239 case SCF_ERROR_PERMISSION_DENIED:
1239 1240 ret = EPERM;
1240 1241 goto out;
1241 1242
1242 1243 case SCF_ERROR_BACKEND_ACCESS:
1243 1244 ret = EACCES;
1244 1245 goto out;
1245 1246
1246 1247 case SCF_ERROR_BACKEND_READONLY:
1247 1248 ret = EROFS;
1248 1249 goto out;
1249 1250
1250 1251 case SCF_ERROR_HANDLE_MISMATCH:
1251 1252 case SCF_ERROR_IN_USE:
1252 1253 bad_fail("scf_transaction_start", scf_error());
1253 1254 }
1254 1255 }
1255 1256
1256 1257 if ((r = tx_set_value(t, t_state, SCF_PROPERTY_STATE,
1257 1258 SCF_TYPE_ASTRING, v_state)) != 0 ||
1258 1259 (r = tx_set_value(t, t_state_next, SCF_PROPERTY_NEXT_STATE,
1259 1260 SCF_TYPE_ASTRING, v_state_next)) != 0 ||
1260 1261 (r = tx_set_value(t, t_stime, SCF_PROPERTY_STATE_TIMESTAMP,
1261 1262 SCF_TYPE_TIME, v_stime)) != 0) {
1262 1263 switch (r) {
1263 1264 case ECONNABORTED:
1264 1265 ret = ECONNABORTED;
1265 1266 goto out;
1266 1267
1267 1268 case ECANCELED:
1268 1269 scf_transaction_reset(t);
1269 1270 goto add_pg;
1270 1271
1271 1272 default:
1272 1273 bad_fail("tx_set_value", r);
1273 1274 }
1274 1275 }
1275 1276
1276 1277 if (aux) {
1277 1278 if ((r = tx_set_value(t, t_aux, SCF_PROPERTY_AUX_STATE,
1278 1279 SCF_TYPE_ASTRING, v_aux)) != 0) {
1279 1280 switch (r) {
1280 1281 case ECONNABORTED:
1281 1282 ret = ECONNABORTED;
1282 1283 goto out;
1283 1284
1284 1285 case ECANCELED:
1285 1286 scf_transaction_reset(t);
1286 1287 goto add_pg;
1287 1288
1288 1289 default:
1289 1290 bad_fail("tx_set_value", r);
1290 1291 }
1291 1292 }
1292 1293 }
1293 1294
1294 1295 ret = scf_transaction_commit(t);
1295 1296 if (ret == 1)
1296 1297 break;
1297 1298 if (ret == -1) {
1298 1299 switch (scf_error()) {
1299 1300 case SCF_ERROR_CONNECTION_BROKEN:
1300 1301 default:
1301 1302 ret = ECONNABORTED;
1302 1303 goto out;
1303 1304
1304 1305 case SCF_ERROR_PERMISSION_DENIED:
1305 1306 ret = EPERM;
1306 1307 goto out;
1307 1308
1308 1309 case SCF_ERROR_BACKEND_ACCESS:
1309 1310 ret = EACCES;
1310 1311 goto out;
1311 1312
1312 1313 case SCF_ERROR_BACKEND_READONLY:
1313 1314 ret = EROFS;
1314 1315 goto out;
1315 1316
1316 1317 case SCF_ERROR_NOT_SET:
1317 1318 bad_fail("scf_transaction_commit", scf_error());
1318 1319 }
1319 1320 }
1320 1321
1321 1322 scf_transaction_reset(t);
1322 1323 if (scf_pg_update(pg) == -1) {
1323 1324 switch (scf_error()) {
1324 1325 case SCF_ERROR_CONNECTION_BROKEN:
1325 1326 default:
1326 1327 ret = ECONNABORTED;
1327 1328 goto out;
1328 1329
1329 1330 case SCF_ERROR_NOT_SET:
1330 1331 goto add_pg;
1331 1332 }
1332 1333 }
1333 1334 }
1334 1335
1335 1336 id->i_state = new_state;
1336 1337 id->i_next_state = new_state_next;
1337 1338 ret = 0;
1338 1339
1339 1340 out:
1340 1341 scf_transaction_destroy(t);
1341 1342 scf_entry_destroy(t_state);
1342 1343 scf_entry_destroy(t_state_next);
1343 1344 scf_entry_destroy(t_stime);
1344 1345 scf_entry_destroy(t_aux);
1345 1346 scf_value_destroy(v_state);
1346 1347 scf_value_destroy(v_state_next);
1347 1348 scf_value_destroy(v_stime);
1348 1349 scf_value_destroy(v_aux);
1349 1350 scf_pg_destroy(pg);
1350 1351 scf_instance_destroy(s_inst);
1351 1352
1352 1353 return (ret);
1353 1354 }
1354 1355
1355 1356 /*
1356 1357 * Fails with
1357 1358 * EINVAL - type is invalid
1358 1359 * ENOMEM
1359 1360 * ECONNABORTED - repository connection broken
1360 1361 * EBADF - s_inst is not set
1361 1362 * ECANCELED - s_inst is deleted
1362 1363 * EPERM - permission denied
1363 1364 * EACCES - backend access denied
1364 1365 * EROFS - backend readonly
1365 1366 */
1366 1367 int
1367 1368 restarter_remove_contract(scf_instance_t *s_inst, ctid_t contract_id,
1368 1369 restarter_contract_type_t type)
1369 1370 {
1370 1371 scf_handle_t *h;
1371 1372 scf_transaction_t *t = NULL;
1372 1373 scf_transaction_entry_t *t_cid = NULL;
1373 1374 scf_propertygroup_t *pg = NULL;
1374 1375 scf_property_t *prop = NULL;
1375 1376 scf_value_t *val;
1376 1377 scf_iter_t *iter = NULL;
1377 1378 const char *pname;
1378 1379 int ret = 0, primary;
1379 1380 uint64_t c;
1380 1381
1381 1382 switch (type) {
1382 1383 case RESTARTER_CONTRACT_PRIMARY:
1383 1384 primary = 1;
1384 1385 break;
1385 1386 case RESTARTER_CONTRACT_TRANSIENT:
1386 1387 primary = 0;
1387 1388 break;
1388 1389 default:
1389 1390 return (EINVAL);
1390 1391 }
1391 1392
1392 1393 h = scf_instance_handle(s_inst);
1393 1394
1394 1395 pg = scf_pg_create(h);
1395 1396 prop = scf_property_create(h);
1396 1397 iter = scf_iter_create(h);
1397 1398 t = scf_transaction_create(h);
1398 1399
1399 1400 if (pg == NULL || prop == NULL || iter == NULL || t == NULL) {
1400 1401 ret = ENOMEM;
1401 1402 goto remove_contract_cleanup;
1402 1403 }
1403 1404
1404 1405 add:
1405 1406 scf_transaction_destroy_children(t);
1406 1407 ret = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
1407 1408 SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg);
1408 1409 if (ret != 0)
1409 1410 goto remove_contract_cleanup;
1410 1411
1411 1412 pname = primary? SCF_PROPERTY_CONTRACT :
1412 1413 SCF_PROPERTY_TRANSIENT_CONTRACT;
1413 1414
1414 1415 for (;;) {
1415 1416 if (scf_transaction_start(t, pg) != 0) {
1416 1417 switch (scf_error()) {
1417 1418 case SCF_ERROR_CONNECTION_BROKEN:
1418 1419 default:
1419 1420 ret = ECONNABORTED;
1420 1421 goto remove_contract_cleanup;
1421 1422
1422 1423 case SCF_ERROR_DELETED:
1423 1424 goto add;
1424 1425
1425 1426 case SCF_ERROR_PERMISSION_DENIED:
1426 1427 ret = EPERM;
1427 1428 goto remove_contract_cleanup;
1428 1429
1429 1430 case SCF_ERROR_BACKEND_ACCESS:
1430 1431 ret = EACCES;
1431 1432 goto remove_contract_cleanup;
1432 1433
1433 1434 case SCF_ERROR_BACKEND_READONLY:
1434 1435 ret = EROFS;
1435 1436 goto remove_contract_cleanup;
1436 1437
1437 1438 case SCF_ERROR_HANDLE_MISMATCH:
1438 1439 case SCF_ERROR_IN_USE:
1439 1440 case SCF_ERROR_NOT_SET:
1440 1441 bad_fail("scf_transaction_start", scf_error());
1441 1442 }
1442 1443 }
1443 1444
1444 1445 t_cid = scf_entry_create(h);
1445 1446
1446 1447 if (scf_pg_get_property(pg, pname, prop) == 0) {
1447 1448 replace:
1448 1449 if (scf_transaction_property_change_type(t, t_cid,
1449 1450 pname, SCF_TYPE_COUNT) != 0) {
1450 1451 switch (scf_error()) {
1451 1452 case SCF_ERROR_CONNECTION_BROKEN:
1452 1453 default:
1453 1454 ret = ECONNABORTED;
1454 1455 goto remove_contract_cleanup;
1455 1456
1456 1457 case SCF_ERROR_DELETED:
1457 1458 scf_entry_destroy(t_cid);
1458 1459 goto add;
1459 1460
1460 1461 case SCF_ERROR_NOT_FOUND:
1461 1462 goto new;
1462 1463
1463 1464 case SCF_ERROR_HANDLE_MISMATCH:
1464 1465 case SCF_ERROR_INVALID_ARGUMENT:
1465 1466 case SCF_ERROR_IN_USE:
1466 1467 case SCF_ERROR_NOT_SET:
1467 1468 bad_fail(
1468 1469 "scf_transaction_property_changetype",
1469 1470 scf_error());
1470 1471 }
1471 1472 }
1472 1473
1473 1474 if (scf_property_is_type(prop, SCF_TYPE_COUNT) == 0) {
1474 1475 if (scf_iter_property_values(iter, prop) != 0) {
1475 1476 switch (scf_error()) {
1476 1477 case SCF_ERROR_CONNECTION_BROKEN:
1477 1478 default:
1478 1479 ret = ECONNABORTED;
1479 1480 goto remove_contract_cleanup;
1480 1481
1481 1482 case SCF_ERROR_NOT_SET:
1482 1483 case SCF_ERROR_HANDLE_MISMATCH:
1483 1484 bad_fail(
1484 1485 "scf_iter_property_values",
1485 1486 scf_error());
1486 1487 }
1487 1488 }
1488 1489
1489 1490 next_val:
1490 1491 val = scf_value_create(h);
1491 1492 if (val == NULL) {
1492 1493 assert(scf_error() ==
1493 1494 SCF_ERROR_NO_MEMORY);
1494 1495 ret = ENOMEM;
1495 1496 goto remove_contract_cleanup;
1496 1497 }
1497 1498
1498 1499 ret = scf_iter_next_value(iter, val);
1499 1500 if (ret == -1) {
1500 1501 switch (scf_error()) {
1501 1502 case SCF_ERROR_CONNECTION_BROKEN:
1502 1503 ret = ECONNABORTED;
1503 1504 goto remove_contract_cleanup;
1504 1505
1505 1506 case SCF_ERROR_DELETED:
1506 1507 scf_value_destroy(val);
1507 1508 goto add;
1508 1509
1509 1510 case SCF_ERROR_HANDLE_MISMATCH:
1510 1511 case SCF_ERROR_INVALID_ARGUMENT:
1511 1512 case SCF_ERROR_PERMISSION_DENIED:
1512 1513 default:
1513 1514 bad_fail("scf_iter_next_value",
1514 1515 scf_error());
1515 1516 }
1516 1517 }
1517 1518
1518 1519 if (ret == 1) {
1519 1520 ret = scf_value_get_count(val, &c);
1520 1521 assert(ret == 0);
1521 1522
1522 1523 if (c != contract_id) {
1523 1524 ret = scf_entry_add_value(t_cid,
1524 1525 val);
1525 1526 assert(ret == 0);
1526 1527 } else {
1527 1528 scf_value_destroy(val);
1528 1529 }
1529 1530
1530 1531 goto next_val;
1531 1532 }
1532 1533
1533 1534 scf_value_destroy(val);
1534 1535 } else {
1535 1536 switch (scf_error()) {
1536 1537 case SCF_ERROR_CONNECTION_BROKEN:
1537 1538 default:
1538 1539 ret = ECONNABORTED;
1539 1540 goto remove_contract_cleanup;
1540 1541
1541 1542 case SCF_ERROR_TYPE_MISMATCH:
1542 1543 break;
1543 1544
1544 1545 case SCF_ERROR_INVALID_ARGUMENT:
1545 1546 case SCF_ERROR_NOT_SET:
1546 1547 bad_fail("scf_property_is_type",
1547 1548 scf_error());
1548 1549 }
1549 1550 }
1550 1551 } else {
1551 1552 switch (scf_error()) {
1552 1553 case SCF_ERROR_CONNECTION_BROKEN:
1553 1554 default:
1554 1555 ret = ECONNABORTED;
1555 1556 goto remove_contract_cleanup;
1556 1557
1557 1558 case SCF_ERROR_DELETED:
1558 1559 scf_entry_destroy(t_cid);
1559 1560 goto add;
1560 1561
1561 1562 case SCF_ERROR_NOT_FOUND:
1562 1563 break;
1563 1564
1564 1565 case SCF_ERROR_HANDLE_MISMATCH:
1565 1566 case SCF_ERROR_INVALID_ARGUMENT:
1566 1567 case SCF_ERROR_NOT_SET:
1567 1568 bad_fail("scf_pg_get_property", scf_error());
1568 1569 }
1569 1570
1570 1571 new:
1571 1572 if (scf_transaction_property_new(t, t_cid, pname,
1572 1573 SCF_TYPE_COUNT) != 0) {
1573 1574 switch (scf_error()) {
1574 1575 case SCF_ERROR_CONNECTION_BROKEN:
1575 1576 default:
1576 1577 ret = ECONNABORTED;
1577 1578 goto remove_contract_cleanup;
1578 1579
1579 1580 case SCF_ERROR_DELETED:
1580 1581 scf_entry_destroy(t_cid);
1581 1582 goto add;
1582 1583
1583 1584 case SCF_ERROR_EXISTS:
1584 1585 goto replace;
1585 1586
1586 1587 case SCF_ERROR_HANDLE_MISMATCH:
1587 1588 case SCF_ERROR_INVALID_ARGUMENT:
1588 1589 case SCF_ERROR_NOT_SET:
1589 1590 bad_fail("scf_transaction_property_new",
1590 1591 scf_error());
1591 1592 }
1592 1593 }
1593 1594 }
1594 1595
1595 1596 ret = scf_transaction_commit(t);
1596 1597 if (ret == -1) {
1597 1598 switch (scf_error()) {
1598 1599 case SCF_ERROR_CONNECTION_BROKEN:
1599 1600 default:
1600 1601 ret = ECONNABORTED;
1601 1602 goto remove_contract_cleanup;
1602 1603
1603 1604 case SCF_ERROR_DELETED:
1604 1605 goto add;
1605 1606
1606 1607 case SCF_ERROR_PERMISSION_DENIED:
1607 1608 ret = EPERM;
1608 1609 goto remove_contract_cleanup;
1609 1610
1610 1611 case SCF_ERROR_BACKEND_ACCESS:
1611 1612 ret = EACCES;
1612 1613 goto remove_contract_cleanup;
1613 1614
1614 1615 case SCF_ERROR_BACKEND_READONLY:
1615 1616 ret = EROFS;
1616 1617 goto remove_contract_cleanup;
1617 1618
1618 1619 case SCF_ERROR_NOT_SET:
1619 1620 bad_fail("scf_transaction_commit", scf_error());
1620 1621 }
1621 1622 }
1622 1623 if (ret == 1) {
1623 1624 ret = 0;
1624 1625 break;
1625 1626 }
1626 1627
1627 1628 scf_transaction_destroy_children(t);
1628 1629 if (scf_pg_update(pg) == -1) {
1629 1630 switch (scf_error()) {
1630 1631 case SCF_ERROR_CONNECTION_BROKEN:
1631 1632 default:
1632 1633 ret = ECONNABORTED;
1633 1634 goto remove_contract_cleanup;
1634 1635
1635 1636 case SCF_ERROR_DELETED:
1636 1637 goto add;
1637 1638
1638 1639 case SCF_ERROR_NOT_SET:
1639 1640 bad_fail("scf_pg_update", scf_error());
1640 1641 }
1641 1642 }
1642 1643 }
1643 1644
1644 1645 remove_contract_cleanup:
1645 1646 scf_transaction_destroy_children(t);
1646 1647 scf_transaction_destroy(t);
1647 1648 scf_iter_destroy(iter);
1648 1649 scf_property_destroy(prop);
1649 1650 scf_pg_destroy(pg);
1650 1651
1651 1652 return (ret);
1652 1653 }
1653 1654
1654 1655 /*
1655 1656 * Fails with
1656 1657 * EINVAL - type is invalid
1657 1658 * ENOMEM
1658 1659 * ECONNABORTED - repository disconnection
1659 1660 * EBADF - s_inst is not set
1660 1661 * ECANCELED - s_inst is deleted
1661 1662 * EPERM
1662 1663 * EACCES
1663 1664 * EROFS
1664 1665 */
1665 1666 int
1666 1667 restarter_store_contract(scf_instance_t *s_inst, ctid_t contract_id,
1667 1668 restarter_contract_type_t type)
1668 1669 {
1669 1670 scf_handle_t *h;
1670 1671 scf_transaction_t *t = NULL;
1671 1672 scf_transaction_entry_t *t_cid = NULL;
1672 1673 scf_value_t *val;
1673 1674 scf_propertygroup_t *pg = NULL;
1674 1675 scf_property_t *prop = NULL;
1675 1676 scf_iter_t *iter = NULL;
1676 1677 const char *pname;
1677 1678 int ret = 0, primary;
1678 1679
1679 1680 if (type == RESTARTER_CONTRACT_PRIMARY)
1680 1681 primary = 1;
1681 1682 else if (type == RESTARTER_CONTRACT_TRANSIENT)
1682 1683 primary = 0;
1683 1684 else
1684 1685 return (EINVAL);
1685 1686
1686 1687 h = scf_instance_handle(s_inst);
1687 1688
1688 1689 pg = scf_pg_create(h);
1689 1690 prop = scf_property_create(h);
1690 1691 iter = scf_iter_create(h);
1691 1692 t = scf_transaction_create(h);
1692 1693
1693 1694 if (pg == NULL || prop == NULL || iter == NULL || t == NULL) {
1694 1695 ret = ENOMEM;
1695 1696 goto out;
1696 1697 }
1697 1698
1698 1699 add:
1699 1700 scf_transaction_destroy_children(t);
1700 1701 ret = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
1701 1702 SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg);
1702 1703 if (ret != 0)
1703 1704 goto out;
1704 1705
1705 1706 pname = primary ? SCF_PROPERTY_CONTRACT :
1706 1707 SCF_PROPERTY_TRANSIENT_CONTRACT;
1707 1708
1708 1709 for (;;) {
1709 1710 if (scf_transaction_start(t, pg) != 0) {
1710 1711 switch (scf_error()) {
1711 1712 case SCF_ERROR_CONNECTION_BROKEN:
1712 1713 default:
1713 1714 ret = ECONNABORTED;
1714 1715 goto out;
1715 1716
1716 1717 case SCF_ERROR_DELETED:
1717 1718 goto add;
1718 1719
1719 1720 case SCF_ERROR_PERMISSION_DENIED:
1720 1721 ret = EPERM;
1721 1722 goto out;
1722 1723
1723 1724 case SCF_ERROR_BACKEND_ACCESS:
1724 1725 ret = EACCES;
1725 1726 goto out;
1726 1727
1727 1728 case SCF_ERROR_BACKEND_READONLY:
1728 1729 ret = EROFS;
1729 1730 goto out;
1730 1731
1731 1732 case SCF_ERROR_HANDLE_MISMATCH:
1732 1733 case SCF_ERROR_IN_USE:
1733 1734 case SCF_ERROR_NOT_SET:
1734 1735 bad_fail("scf_transaction_start", scf_error());
1735 1736 }
1736 1737 }
1737 1738
1738 1739 t_cid = scf_entry_create(h);
1739 1740 if (t_cid == NULL) {
1740 1741 ret = ENOMEM;
1741 1742 goto out;
1742 1743 }
1743 1744
1744 1745 if (scf_pg_get_property(pg, pname, prop) == 0) {
1745 1746 replace:
1746 1747 if (scf_transaction_property_change_type(t, t_cid,
1747 1748 pname, SCF_TYPE_COUNT) != 0) {
1748 1749 switch (scf_error()) {
1749 1750 case SCF_ERROR_CONNECTION_BROKEN:
1750 1751 default:
1751 1752 ret = ECONNABORTED;
1752 1753 goto out;
1753 1754
1754 1755 case SCF_ERROR_DELETED:
1755 1756 scf_entry_destroy(t_cid);
1756 1757 goto add;
1757 1758
1758 1759 case SCF_ERROR_NOT_FOUND:
1759 1760 goto new;
1760 1761
1761 1762 case SCF_ERROR_HANDLE_MISMATCH:
1762 1763 case SCF_ERROR_INVALID_ARGUMENT:
1763 1764 case SCF_ERROR_IN_USE:
1764 1765 case SCF_ERROR_NOT_SET:
1765 1766 bad_fail(
1766 1767 "scf_transaction_propert_change_type",
1767 1768 scf_error());
1768 1769 }
1769 1770 }
1770 1771
1771 1772 if (scf_property_is_type(prop, SCF_TYPE_COUNT) == 0) {
1772 1773 if (scf_iter_property_values(iter, prop) != 0) {
1773 1774 switch (scf_error()) {
1774 1775 case SCF_ERROR_CONNECTION_BROKEN:
1775 1776 default:
1776 1777 ret = ECONNABORTED;
1777 1778 goto out;
1778 1779
1779 1780 case SCF_ERROR_NOT_SET:
1780 1781 case SCF_ERROR_HANDLE_MISMATCH:
1781 1782 bad_fail(
1782 1783 "scf_iter_property_values",
1783 1784 scf_error());
1784 1785 }
1785 1786 }
1786 1787
1787 1788 next_val:
1788 1789 val = scf_value_create(h);
1789 1790 if (val == NULL) {
1790 1791 assert(scf_error() ==
1791 1792 SCF_ERROR_NO_MEMORY);
1792 1793 ret = ENOMEM;
1793 1794 goto out;
1794 1795 }
1795 1796
1796 1797 ret = scf_iter_next_value(iter, val);
1797 1798 if (ret == -1) {
1798 1799 switch (scf_error()) {
1799 1800 case SCF_ERROR_CONNECTION_BROKEN:
1800 1801 default:
1801 1802 ret = ECONNABORTED;
1802 1803 goto out;
1803 1804
1804 1805 case SCF_ERROR_DELETED:
1805 1806 scf_value_destroy(val);
1806 1807 goto add;
1807 1808
1808 1809 case SCF_ERROR_HANDLE_MISMATCH:
1809 1810 case SCF_ERROR_INVALID_ARGUMENT:
1810 1811 case SCF_ERROR_PERMISSION_DENIED:
1811 1812 bad_fail(
1812 1813 "scf_iter_next_value",
1813 1814 scf_error());
1814 1815 }
1815 1816 }
1816 1817
1817 1818 if (ret == 1) {
1818 1819 ret = scf_entry_add_value(t_cid, val);
1819 1820 assert(ret == 0);
1820 1821
1821 1822 goto next_val;
1822 1823 }
1823 1824
1824 1825 scf_value_destroy(val);
1825 1826 } else {
1826 1827 switch (scf_error()) {
1827 1828 case SCF_ERROR_CONNECTION_BROKEN:
1828 1829 default:
1829 1830 ret = ECONNABORTED;
1830 1831 goto out;
1831 1832
1832 1833 case SCF_ERROR_TYPE_MISMATCH:
1833 1834 break;
1834 1835
1835 1836 case SCF_ERROR_INVALID_ARGUMENT:
1836 1837 case SCF_ERROR_NOT_SET:
1837 1838 bad_fail("scf_property_is_type",
1838 1839 scf_error());
1839 1840 }
1840 1841 }
1841 1842 } else {
1842 1843 switch (scf_error()) {
1843 1844 case SCF_ERROR_CONNECTION_BROKEN:
1844 1845 default:
1845 1846 ret = ECONNABORTED;
1846 1847 goto out;
1847 1848
1848 1849 case SCF_ERROR_DELETED:
1849 1850 scf_entry_destroy(t_cid);
1850 1851 goto add;
1851 1852
1852 1853 case SCF_ERROR_NOT_FOUND:
1853 1854 break;
1854 1855
1855 1856 case SCF_ERROR_HANDLE_MISMATCH:
1856 1857 case SCF_ERROR_INVALID_ARGUMENT:
1857 1858 case SCF_ERROR_NOT_SET:
1858 1859 bad_fail("scf_pg_get_property", scf_error());
1859 1860 }
1860 1861
1861 1862 new:
1862 1863 if (scf_transaction_property_new(t, t_cid, pname,
1863 1864 SCF_TYPE_COUNT) != 0) {
1864 1865 switch (scf_error()) {
1865 1866 case SCF_ERROR_CONNECTION_BROKEN:
1866 1867 default:
1867 1868 ret = ECONNABORTED;
1868 1869 goto out;
1869 1870
1870 1871 case SCF_ERROR_DELETED:
1871 1872 scf_entry_destroy(t_cid);
1872 1873 goto add;
1873 1874
1874 1875 case SCF_ERROR_EXISTS:
1875 1876 goto replace;
1876 1877
1877 1878 case SCF_ERROR_HANDLE_MISMATCH:
1878 1879 case SCF_ERROR_INVALID_ARGUMENT:
1879 1880 case SCF_ERROR_NOT_SET:
1880 1881 bad_fail("scf_transaction_property_new",
1881 1882 scf_error());
1882 1883 }
1883 1884 }
1884 1885 }
1885 1886
1886 1887 val = scf_value_create(h);
1887 1888 if (val == NULL) {
1888 1889 assert(scf_error() == SCF_ERROR_NO_MEMORY);
1889 1890 ret = ENOMEM;
1890 1891 goto out;
1891 1892 }
1892 1893
1893 1894 scf_value_set_count(val, contract_id);
1894 1895 ret = scf_entry_add_value(t_cid, val);
1895 1896 assert(ret == 0);
1896 1897
1897 1898 ret = scf_transaction_commit(t);
1898 1899 if (ret == -1) {
1899 1900 switch (scf_error()) {
1900 1901 case SCF_ERROR_CONNECTION_BROKEN:
1901 1902 default:
1902 1903 ret = ECONNABORTED;
1903 1904 goto out;
1904 1905
1905 1906 case SCF_ERROR_DELETED:
1906 1907 goto add;
1907 1908
1908 1909 case SCF_ERROR_PERMISSION_DENIED:
1909 1910 ret = EPERM;
1910 1911 goto out;
1911 1912
1912 1913 case SCF_ERROR_BACKEND_ACCESS:
1913 1914 ret = EACCES;
1914 1915 goto out;
1915 1916
1916 1917 case SCF_ERROR_BACKEND_READONLY:
1917 1918 ret = EROFS;
1918 1919 goto out;
1919 1920
1920 1921 case SCF_ERROR_NOT_SET:
1921 1922 bad_fail("scf_transaction_commit", scf_error());
1922 1923 }
1923 1924 }
1924 1925 if (ret == 1) {
1925 1926 ret = 0;
1926 1927 break;
1927 1928 }
1928 1929
1929 1930 scf_transaction_destroy_children(t);
1930 1931 if (scf_pg_update(pg) == -1) {
1931 1932 switch (scf_error()) {
1932 1933 case SCF_ERROR_CONNECTION_BROKEN:
1933 1934 default:
1934 1935 ret = ECONNABORTED;
1935 1936 goto out;
1936 1937
1937 1938 case SCF_ERROR_DELETED:
1938 1939 goto add;
1939 1940
1940 1941 case SCF_ERROR_NOT_SET:
1941 1942 bad_fail("scf_pg_update", scf_error());
1942 1943 }
1943 1944 }
1944 1945 }
1945 1946
1946 1947 out:
1947 1948 scf_transaction_destroy_children(t);
1948 1949 scf_transaction_destroy(t);
1949 1950 scf_iter_destroy(iter);
1950 1951 scf_property_destroy(prop);
1951 1952 scf_pg_destroy(pg);
1952 1953
1953 1954 return (ret);
1954 1955 }
1955 1956
1956 1957 int
1957 1958 restarter_rm_libs_loadable()
1958 1959 {
1959 1960 void *libhndl;
1960 1961
1961 1962 if (method_context_safety)
1962 1963 return (1);
1963 1964
1964 1965 if ((libhndl = dlopen("libpool.so", RTLD_LAZY | RTLD_LOCAL)) == NULL)
1965 1966 return (0);
1966 1967
1967 1968 (void) dlclose(libhndl);
1968 1969
1969 1970 if ((libhndl = dlopen("libproject.so", RTLD_LAZY | RTLD_LOCAL)) == NULL)
1970 1971 return (0);
1971 1972
1972 1973 (void) dlclose(libhndl);
1973 1974
1974 1975 method_context_safety = 1;
1975 1976
1976 1977 return (1);
1977 1978 }
1978 1979
1979 1980 static int
1980 1981 get_astring_val(scf_propertygroup_t *pg, const char *name, char *buf,
1981 1982 size_t bufsz, scf_property_t *prop, scf_value_t *val)
1982 1983 {
1983 1984 ssize_t szret;
1984 1985
1985 1986 if (pg == NULL)
1986 1987 return (-1);
1987 1988
1988 1989 if (scf_pg_get_property(pg, name, prop) != SCF_SUCCESS) {
1989 1990 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
1990 1991 uu_die(rcbroken);
1991 1992 return (-1);
1992 1993 }
1993 1994
1994 1995 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
1995 1996 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
1996 1997 uu_die(rcbroken);
1997 1998 return (-1);
1998 1999 }
1999 2000
2000 2001 szret = scf_value_get_astring(val, buf, bufsz);
2001 2002
2002 2003 return (szret >= 0 ? 0 : -1);
2003 2004 }
2004 2005
2005 2006 static int
2006 2007 get_boolean_val(scf_propertygroup_t *pg, const char *name, uint8_t *b,
2007 2008 scf_property_t *prop, scf_value_t *val)
2008 2009 {
2009 2010 if (scf_pg_get_property(pg, name, prop) != SCF_SUCCESS) {
2010 2011 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
2011 2012 uu_die(rcbroken);
2012 2013 return (-1);
2013 2014 }
2014 2015
2015 2016 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
2016 2017 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
2017 2018 uu_die(rcbroken);
2018 2019 return (-1);
2019 2020 }
2020 2021
2021 2022 if (scf_value_get_boolean(val, b))
2022 2023 return (-1);
2023 2024
2024 2025 return (0);
2025 2026 }
2026 2027
2027 2028 /*
2028 2029 * Try to load mcp->pwd, if it isn't already.
2029 2030 * Fails with
2030 2031 * ENOMEM - malloc() failed
2031 2032 * ENOENT - no entry found
2032 2033 * EIO - I/O error
2033 2034 * EMFILE - process out of file descriptors
2034 2035 * ENFILE - system out of file handles
2035 2036 */
2036 2037 static int
2037 2038 lookup_pwd(struct method_context *mcp)
2038 2039 {
2039 2040 struct passwd *pwdp;
2040 2041
2041 2042 if (mcp->pwbuf != NULL && mcp->pwd.pw_uid == mcp->uid)
2042 2043 return (0);
2043 2044
2044 2045 if (mcp->pwbuf == NULL) {
2045 2046 mcp->pwbufsz = sysconf(_SC_GETPW_R_SIZE_MAX);
2046 2047 assert(mcp->pwbufsz >= 0);
2047 2048 mcp->pwbuf = malloc(mcp->pwbufsz);
2048 2049 if (mcp->pwbuf == NULL)
2049 2050 return (ENOMEM);
2050 2051 }
2051 2052
2052 2053 do {
2053 2054 errno = 0;
2054 2055 pwdp = getpwuid_r(mcp->uid, &mcp->pwd, mcp->pwbuf,
2055 2056 mcp->pwbufsz);
2056 2057 } while (pwdp == NULL && errno == EINTR);
2057 2058 if (pwdp != NULL)
2058 2059 return (0);
2059 2060
2060 2061 free(mcp->pwbuf);
2061 2062 mcp->pwbuf = NULL;
2062 2063
2063 2064 switch (errno) {
2064 2065 case 0:
2065 2066 default:
2066 2067 /*
2067 2068 * Until bug 5065780 is fixed, getpwuid_r() can fail with
2068 2069 * ENOENT, particularly on the miniroot. Since the
2069 2070 * documentation is inaccurate, we'll return ENOENT for unknown
2070 2071 * errors.
2071 2072 */
2072 2073 return (ENOENT);
2073 2074
2074 2075 case EIO:
2075 2076 case EMFILE:
2076 2077 case ENFILE:
2077 2078 return (errno);
2078 2079
2079 2080 case ERANGE:
2080 2081 bad_fail("getpwuid_r", errno);
2081 2082 /* NOTREACHED */
2082 2083 }
2083 2084 }
2084 2085
2085 2086 /*
2086 2087 * Get the user id for str. Returns 0 on success or
2087 2088 * ERANGE the uid is too big
2088 2089 * EINVAL the string starts with a digit, but is not a valid uid
2089 2090 * ENOMEM out of memory
2090 2091 * ENOENT no passwd entry for str
2091 2092 * EIO an I/O error has occurred
2092 2093 * EMFILE/ENFILE out of file descriptors
2093 2094 */
2094 2095 int
2095 2096 get_uid(const char *str, struct method_context *ci, uid_t *uidp)
2096 2097 {
2097 2098 if (isdigit(str[0])) {
2098 2099 uid_t uid;
2099 2100 char *cp;
2100 2101
2101 2102 errno = 0;
2102 2103 uid = strtol(str, &cp, 10);
2103 2104
2104 2105 if (uid == 0 && errno != 0) {
2105 2106 assert(errno != EINVAL);
2106 2107 return (errno);
2107 2108 }
2108 2109
2109 2110 for (; *cp != '\0'; ++cp)
2110 2111 if (*cp != ' ' || *cp != '\t')
2111 2112 return (EINVAL);
2112 2113
2113 2114 if (uid > UID_MAX)
2114 2115 return (EINVAL);
2115 2116
2116 2117 *uidp = uid;
2117 2118 return (0);
2118 2119 } else {
2119 2120 struct passwd *pwdp;
2120 2121
2121 2122 if (ci->pwbuf == NULL) {
2122 2123 ci->pwbufsz = sysconf(_SC_GETPW_R_SIZE_MAX);
2123 2124 ci->pwbuf = malloc(ci->pwbufsz);
2124 2125 if (ci->pwbuf == NULL)
2125 2126 return (ENOMEM);
2126 2127 }
2127 2128
2128 2129 do {
2129 2130 errno = 0;
2130 2131 pwdp =
2131 2132 getpwnam_r(str, &ci->pwd, ci->pwbuf, ci->pwbufsz);
2132 2133 } while (pwdp == NULL && errno == EINTR);
2133 2134
2134 2135 if (pwdp != NULL) {
2135 2136 *uidp = ci->pwd.pw_uid;
2136 2137 return (0);
2137 2138 } else {
2138 2139 free(ci->pwbuf);
2139 2140 ci->pwbuf = NULL;
2140 2141 switch (errno) {
2141 2142 case 0:
2142 2143 return (ENOENT);
2143 2144
2144 2145 case ENOENT:
2145 2146 case EIO:
2146 2147 case EMFILE:
2147 2148 case ENFILE:
2148 2149 return (errno);
2149 2150
2150 2151 case ERANGE:
2151 2152 default:
2152 2153 bad_fail("getpwnam_r", errno);
2153 2154 /* NOTREACHED */
2154 2155 }
2155 2156 }
2156 2157 }
2157 2158 }
2158 2159
2159 2160 gid_t
2160 2161 get_gid(const char *str)
2161 2162 {
2162 2163 if (isdigit(str[0])) {
2163 2164 gid_t gid;
2164 2165 char *cp;
2165 2166
2166 2167 errno = 0;
2167 2168 gid = strtol(str, &cp, 10);
2168 2169
2169 2170 if (gid == 0 && errno != 0)
2170 2171 return ((gid_t)-1);
2171 2172
2172 2173 for (; *cp != '\0'; ++cp)
2173 2174 if (*cp != ' ' || *cp != '\t')
2174 2175 return ((gid_t)-1);
2175 2176
2176 2177 return (gid);
2177 2178 } else {
2178 2179 struct group grp, *ret;
2179 2180 char *buffer;
2180 2181 size_t buflen;
2181 2182
2182 2183 buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
2183 2184 buffer = malloc(buflen);
2184 2185 if (buffer == NULL)
2185 2186 uu_die(allocfail);
2186 2187
2187 2188 errno = 0;
2188 2189 ret = getgrnam_r(str, &grp, buffer, buflen);
2189 2190 free(buffer);
2190 2191
2191 2192 return (ret == NULL ? (gid_t)-1 : grp.gr_gid);
2192 2193 }
2193 2194 }
2194 2195
2195 2196 /*
2196 2197 * Fails with
2197 2198 * ENOMEM - out of memory
2198 2199 * ENOENT - no passwd entry
2199 2200 * no project entry
2200 2201 * EIO - an I/O error occurred
2201 2202 * EMFILE - the process is out of file descriptors
2202 2203 * ENFILE - the system is out of file handles
2203 2204 * ERANGE - the project id is out of range
2204 2205 * EINVAL - str is invalid
2205 2206 * E2BIG - the project entry was too big
2206 2207 * -1 - the name service switch is misconfigured
2207 2208 */
2208 2209 int
2209 2210 get_projid(const char *str, struct method_context *cip)
2210 2211 {
2211 2212 int ret;
2212 2213 void *buf;
2213 2214 const size_t bufsz = PROJECT_BUFSZ;
2214 2215 struct project proj, *pp;
2215 2216
2216 2217 if (strcmp(str, ":default") == 0) {
2217 2218 if (cip->uid == 0) {
2218 2219 /* Don't change project for root services */
2219 2220 cip->project = NULL;
2220 2221 return (0);
2221 2222 }
2222 2223
2223 2224 switch (ret = lookup_pwd(cip)) {
2224 2225 case 0:
2225 2226 break;
2226 2227
2227 2228 case ENOMEM:
2228 2229 case ENOENT:
2229 2230 case EIO:
2230 2231 case EMFILE:
2231 2232 case ENFILE:
2232 2233 return (ret);
2233 2234
2234 2235 default:
2235 2236 bad_fail("lookup_pwd", ret);
2236 2237 }
2237 2238
2238 2239 buf = malloc(bufsz);
2239 2240 if (buf == NULL)
2240 2241 return (ENOMEM);
2241 2242
2242 2243 do {
2243 2244 errno = 0;
2244 2245 pp = getdefaultproj(cip->pwd.pw_name, &proj, buf,
2245 2246 bufsz);
2246 2247 } while (pp == NULL && errno == EINTR);
2247 2248
2248 2249 /* to be continued ... */
2249 2250 } else {
2250 2251 projid_t projid;
2251 2252 char *cp;
2252 2253
2253 2254 if (!isdigit(str[0])) {
2254 2255 cip->project = strdup(str);
2255 2256 return (cip->project != NULL ? 0 : ENOMEM);
2256 2257 }
2257 2258
2258 2259 errno = 0;
2259 2260 projid = strtol(str, &cp, 10);
2260 2261
2261 2262 if (projid == 0 && errno != 0) {
2262 2263 assert(errno == ERANGE);
2263 2264 return (errno);
2264 2265 }
2265 2266
2266 2267 for (; *cp != '\0'; ++cp)
2267 2268 if (*cp != ' ' || *cp != '\t')
2268 2269 return (EINVAL);
2269 2270
2270 2271 if (projid > MAXPROJID)
2271 2272 return (ERANGE);
2272 2273
2273 2274 buf = malloc(bufsz);
2274 2275 if (buf == NULL)
2275 2276 return (ENOMEM);
2276 2277
2277 2278 do {
2278 2279 errno = 0;
2279 2280 pp = getprojbyid(projid, &proj, buf, bufsz);
2280 2281 } while (pp == NULL && errno == EINTR);
2281 2282 }
2282 2283
2283 2284 if (pp) {
2284 2285 cip->project = strdup(pp->pj_name);
2285 2286 free(buf);
2286 2287 return (cip->project != NULL ? 0 : ENOMEM);
2287 2288 }
2288 2289
2289 2290 free(buf);
2290 2291
2291 2292 switch (errno) {
2292 2293 case 0:
2293 2294 return (ENOENT);
2294 2295
2295 2296 case EIO:
2296 2297 case EMFILE:
2297 2298 case ENFILE:
2298 2299 return (errno);
2299 2300
2300 2301 case ERANGE:
2301 2302 return (E2BIG);
2302 2303
2303 2304 default:
2304 2305 return (-1);
2305 2306 }
2306 2307 }
2307 2308
2308 2309 /*
2309 2310 * Parse the supp_groups property value and populate ci->groups. Returns
2310 2311 * EINVAL (get_gid() failed for one of the components), E2BIG (the property has
2311 2312 * more than NGROUPS_MAX-1 groups), or 0 on success.
2312 2313 */
2313 2314 int
2314 2315 get_groups(char *str, struct method_context *ci)
2315 2316 {
2316 2317 char *cp, *end, *next;
2317 2318 uint_t i;
2318 2319
2319 2320 const char * const whitespace = " \t";
2320 2321 const char * const illegal = ", \t";
2321 2322
2322 2323 if (str[0] == '\0') {
2323 2324 ci->ngroups = 0;
2324 2325 return (0);
2325 2326 }
2326 2327
2327 2328 for (cp = str, i = 0; *cp != '\0'; ) {
2328 2329 /* skip whitespace */
2329 2330 cp += strspn(cp, whitespace);
2330 2331
2331 2332 /* find the end */
2332 2333 end = cp + strcspn(cp, illegal);
2333 2334
2334 2335 /* skip whitespace after end */
2335 2336 next = end + strspn(end, whitespace);
2336 2337
2337 2338 /* if there's a comma, it separates the fields */
2338 2339 if (*next == ',')
2339 2340 ++next;
2340 2341
2341 2342 *end = '\0';
2342 2343
2343 2344 if ((ci->groups[i] = get_gid(cp)) == (gid_t)-1) {
2344 2345 ci->ngroups = 0;
2345 2346 return (EINVAL);
2346 2347 }
2347 2348
2348 2349 ++i;
2349 2350 if (i > NGROUPS_MAX - 1) {
2350 2351 ci->ngroups = 0;
2351 2352 return (E2BIG);
2352 2353 }
2353 2354
2354 2355 cp = next;
2355 2356 }
2356 2357
2357 2358 ci->ngroups = i;
2358 2359 return (0);
2359 2360 }
2360 2361
2361 2362
2362 2363 /*
2363 2364 * Return an error message structure containing the error message
2364 2365 * with context, and the error so the caller can make a decision
2365 2366 * on what to do next.
2366 2367 *
2367 2368 * Because get_ids uses the mc_error_create() function which can
2368 2369 * reallocate the merr, this function must return the merr pointer
2369 2370 * in case it was reallocated.
2370 2371 */
2371 2372 static mc_error_t *
2372 2373 get_profile(scf_propertygroup_t *methpg, scf_propertygroup_t *instpg,
2373 2374 scf_property_t *prop, scf_value_t *val, const char *cmdline,
2374 2375 struct method_context *ci, mc_error_t *merr)
2375 2376 {
2376 2377 char *buf = ci->vbuf;
2377 2378 ssize_t buf_sz = ci->vbuf_sz;
2378 2379 char cmd[PATH_MAX];
2379 2380 char *cp, *value;
2380 2381 const char *cmdp;
2381 2382 execattr_t *eap;
2382 2383 mc_error_t *err = merr;
2383 2384 int r;
2384 2385
2385 2386 if (!(get_astring_val(methpg, SCF_PROPERTY_PROFILE, buf, buf_sz, prop,
2386 2387 val) == 0 || get_astring_val(instpg, SCF_PROPERTY_PROFILE, buf,
2387 2388 buf_sz, prop, val) == 0))
2388 2389 return (mc_error_create(merr, scf_error(),
2389 2390 "Method context requires a profile, but the \"%s\" "
2390 2391 "property could not be read. scf_error is %s",
2391 2392 SCF_PROPERTY_PROFILE, scf_strerror(scf_error())));
2392 2393
2393 2394 /* Extract the command from the command line. */
2394 2395 cp = strpbrk(cmdline, " \t");
2395 2396
2396 2397 if (cp == NULL) {
2397 2398 cmdp = cmdline;
2398 2399 } else {
2399 2400 (void) strncpy(cmd, cmdline, cp - cmdline);
2400 2401 cmd[cp - cmdline] = '\0';
2401 2402 cmdp = cmd;
2402 2403 }
2403 2404
2404 2405 /* Require that cmdp[0] == '/'? */
2405 2406
2406 2407 eap = getexecprof(buf, KV_COMMAND, cmdp, GET_ONE);
2407 2408 if (eap == NULL)
2408 2409 return (mc_error_create(merr, ENOENT,
2409 2410 "Could not find the execution profile \"%s\", "
2410 2411 "command %s.", buf, cmdp));
2411 2412
2412 2413 /* Based on pfexec.c */
2413 2414
2414 2415 /* Get the euid first so we don't override ci->pwd for the uid. */
2415 2416 if ((value = kva_match(eap->attr, EXECATTR_EUID_KW)) != NULL) {
2416 2417 if ((r = get_uid(value, ci, &ci->euid)) != 0) {
2417 2418 ci->euid = (uid_t)-1;
2418 2419 err = mc_error_create(merr, r,
2419 2420 "Could not interpret profile euid value \"%s\", "
2420 2421 "from the execution profile \"%s\", error %d.",
2421 2422 value, buf, r);
2422 2423 goto out;
2423 2424 }
2424 2425 }
2425 2426
2426 2427 if ((value = kva_match(eap->attr, EXECATTR_UID_KW)) != NULL) {
2427 2428 if ((r = get_uid(value, ci, &ci->uid)) != 0) {
2428 2429 ci->euid = ci->uid = (uid_t)-1;
2429 2430 err = mc_error_create(merr, r,
2430 2431 "Could not interpret profile uid value \"%s\", "
2431 2432 "from the execution profile \"%s\", error %d.",
2432 2433 value, buf, r);
2433 2434 goto out;
2434 2435 }
2435 2436 ci->euid = ci->uid;
2436 2437 }
2437 2438
2438 2439 if ((value = kva_match(eap->attr, EXECATTR_GID_KW)) != NULL) {
2439 2440 ci->egid = ci->gid = get_gid(value);
2440 2441 if (ci->gid == (gid_t)-1) {
2441 2442 err = mc_error_create(merr, EINVAL,
2442 2443 "Could not interpret profile gid value \"%s\", "
2443 2444 "from the execution profile \"%s\".", value, buf);
2444 2445 goto out;
2445 2446 }
2446 2447 }
2447 2448
2448 2449 if ((value = kva_match(eap->attr, EXECATTR_EGID_KW)) != NULL) {
2449 2450 ci->egid = get_gid(value);
2450 2451 if (ci->egid == (gid_t)-1) {
2451 2452 err = mc_error_create(merr, EINVAL,
2452 2453 "Could not interpret profile egid value \"%s\", "
2453 2454 "from the execution profile \"%s\".", value, buf);
2454 2455 goto out;
2455 2456 }
2456 2457 }
2457 2458
2458 2459 if ((value = kva_match(eap->attr, EXECATTR_LPRIV_KW)) != NULL) {
2459 2460 ci->lpriv_set = priv_str_to_set(value, ",", NULL);
2460 2461 if (ci->lpriv_set == NULL) {
2461 2462 if (errno != EINVAL)
2462 2463 err = mc_error_create(merr, ENOMEM,
2463 2464 ALLOCFAIL);
2464 2465 else
2465 2466 err = mc_error_create(merr, EINVAL,
2466 2467 "Could not interpret profile "
2467 2468 "limitprivs value \"%s\", from "
2468 2469 "the execution profile \"%s\".",
2469 2470 value, buf);
2470 2471 goto out;
2471 2472 }
2472 2473 }
2473 2474
2474 2475 if ((value = kva_match(eap->attr, EXECATTR_IPRIV_KW)) != NULL) {
2475 2476 ci->priv_set = priv_str_to_set(value, ",", NULL);
2476 2477 if (ci->priv_set == NULL) {
2477 2478 if (errno != EINVAL)
2478 2479 err = mc_error_create(merr, ENOMEM,
2479 2480 ALLOCFAIL);
2480 2481 else
2481 2482 err = mc_error_create(merr, EINVAL,
2482 2483 "Could not interpret profile privs value "
2483 2484 "\"%s\", from the execution profile "
2484 2485 "\"%s\".", value, buf);
2485 2486 goto out;
2486 2487 }
2487 2488 }
2488 2489
2489 2490 out:
2490 2491 free_execattr(eap);
2491 2492
2492 2493 return (err);
2493 2494 }
2494 2495
2495 2496 /*
2496 2497 * Return an error message structure containing the error message
2497 2498 * with context, and the error so the caller can make a decision
2498 2499 * on what to do next.
2499 2500 *
2500 2501 * Because get_ids uses the mc_error_create() function which can
2501 2502 * reallocate the merr, this function must return the merr pointer
2502 2503 * in case it was reallocated.
2503 2504 */
2504 2505 static mc_error_t *
2505 2506 get_ids(scf_propertygroup_t *methpg, scf_propertygroup_t *instpg,
2506 2507 scf_property_t *prop, scf_value_t *val, struct method_context *ci,
2507 2508 mc_error_t *merr)
2508 2509 {
2509 2510 char *vbuf = ci->vbuf;
2510 2511 ssize_t vbuf_sz = ci->vbuf_sz;
2511 2512 int r;
2512 2513
2513 2514 /*
2514 2515 * This should never happen because the caller should fall through
2515 2516 * another path of just setting the ids to defaults, instead of
2516 2517 * attempting to get the ids here.
2517 2518 */
2518 2519 if (methpg == NULL && instpg == NULL)
2519 2520 return (mc_error_create(merr, ENOENT,
2520 2521 "No property groups to get ids from."));
2521 2522
2522 2523 if (!(get_astring_val(methpg, SCF_PROPERTY_USER,
2523 2524 vbuf, vbuf_sz, prop, val) == 0 || get_astring_val(instpg,
2524 2525 SCF_PROPERTY_USER, vbuf, vbuf_sz, prop,
2525 2526 val) == 0))
2526 2527 return (mc_error_create(merr, ENOENT,
2527 2528 "Could not get \"%s\" property.", SCF_PROPERTY_USER));
2528 2529
2529 2530 if ((r = get_uid(vbuf, ci, &ci->uid)) != 0) {
2530 2531 ci->uid = (uid_t)-1;
2531 2532 return (mc_error_create(merr, r,
2532 2533 "Could not interpret \"%s\" property value \"%s\", "
2533 2534 "error %d.", SCF_PROPERTY_USER, vbuf, r));
2534 2535 }
2535 2536
2536 2537 if (!(get_astring_val(methpg, SCF_PROPERTY_GROUP, vbuf, vbuf_sz, prop,
2537 2538 val) == 0 || get_astring_val(instpg, SCF_PROPERTY_GROUP, vbuf,
2538 2539 vbuf_sz, prop, val) == 0)) {
2539 2540 if (scf_error() == SCF_ERROR_NOT_FOUND) {
2540 2541 (void) strcpy(vbuf, ":default");
2541 2542 } else {
2542 2543 return (mc_error_create(merr, ENOENT,
2543 2544 "Could not get \"%s\" property.",
2544 2545 SCF_PROPERTY_GROUP));
2545 2546 }
2546 2547 }
2547 2548
2548 2549 if (strcmp(vbuf, ":default") != 0) {
2549 2550 ci->gid = get_gid(vbuf);
2550 2551 if (ci->gid == (gid_t)-1) {
2551 2552 return (mc_error_create(merr, ENOENT,
2552 2553 "Could not interpret \"%s\" property value \"%s\".",
2553 2554 SCF_PROPERTY_GROUP, vbuf));
2554 2555 }
2555 2556 } else {
2556 2557 switch (r = lookup_pwd(ci)) {
2557 2558 case 0:
2558 2559 ci->gid = ci->pwd.pw_gid;
2559 2560 break;
2560 2561
2561 2562 case ENOENT:
2562 2563 ci->gid = (gid_t)-1;
2563 2564 return (mc_error_create(merr, ENOENT,
2564 2565 "No passwd entry for uid \"%d\".", ci->uid));
2565 2566
2566 2567 case ENOMEM:
2567 2568 return (mc_error_create(merr, ENOMEM,
2568 2569 "Out of memory."));
2569 2570
2570 2571 case EIO:
2571 2572 case EMFILE:
2572 2573 case ENFILE:
2573 2574 return (mc_error_create(merr, ENFILE,
2574 2575 "getpwuid_r() failed, error %d.", r));
2575 2576
2576 2577 default:
2577 2578 bad_fail("lookup_pwd", r);
2578 2579 }
2579 2580 }
2580 2581
2581 2582 if (!(get_astring_val(methpg, SCF_PROPERTY_SUPP_GROUPS, vbuf, vbuf_sz,
2582 2583 prop, val) == 0 || get_astring_val(instpg,
2583 2584 SCF_PROPERTY_SUPP_GROUPS, vbuf, vbuf_sz, prop, val) == 0)) {
2584 2585 if (scf_error() == SCF_ERROR_NOT_FOUND) {
2585 2586 (void) strcpy(vbuf, ":default");
2586 2587 } else {
2587 2588 return (mc_error_create(merr, ENOENT,
2588 2589 "Could not get supplemental groups (\"%s\") "
2589 2590 "property.", SCF_PROPERTY_SUPP_GROUPS));
2590 2591 }
2591 2592 }
2592 2593
2593 2594 if (strcmp(vbuf, ":default") != 0) {
2594 2595 switch (r = get_groups(vbuf, ci)) {
2595 2596 case 0:
2596 2597 break;
2597 2598
2598 2599 case EINVAL:
2599 2600 return (mc_error_create(merr, EINVAL,
2600 2601 "Could not interpret supplemental groups (\"%s\") "
2601 2602 "property value \"%s\".", SCF_PROPERTY_SUPP_GROUPS,
2602 2603 vbuf));
2603 2604
2604 2605 case E2BIG:
2605 2606 return (mc_error_create(merr, E2BIG,
2606 2607 "Too many supplemental groups values in \"%s\".",
2607 2608 vbuf));
2608 2609
2609 2610 default:
2610 2611 bad_fail("get_groups", r);
2611 2612 }
2612 2613 } else {
2613 2614 ci->ngroups = -1;
2614 2615 }
2615 2616
2616 2617 if (!(get_astring_val(methpg, SCF_PROPERTY_PRIVILEGES, vbuf, vbuf_sz,
2617 2618 prop, val) == 0 || get_astring_val(instpg, SCF_PROPERTY_PRIVILEGES,
2618 2619 vbuf, vbuf_sz, prop, val) == 0)) {
2619 2620 if (scf_error() == SCF_ERROR_NOT_FOUND) {
2620 2621 (void) strcpy(vbuf, ":default");
2621 2622 } else {
2622 2623 return (mc_error_create(merr, ENOENT,
2623 2624 "Could not get \"%s\" property.",
2624 2625 SCF_PROPERTY_PRIVILEGES));
2625 2626 }
2626 2627 }
2627 2628
2628 2629 /*
2629 2630 * For default privs, we need to keep priv_set == NULL, as
2630 2631 * we use this test elsewhere.
2631 2632 */
2632 2633 if (strcmp(vbuf, ":default") != 0) {
2633 2634 ci->priv_set = priv_str_to_set(vbuf, ",", NULL);
2634 2635 if (ci->priv_set == NULL) {
2635 2636 if (errno != EINVAL) {
2636 2637 return (mc_error_create(merr, ENOMEM,
2637 2638 ALLOCFAIL));
2638 2639 } else {
2639 2640 return (mc_error_create(merr, EINVAL,
2640 2641 "Could not interpret \"%s\" "
2641 2642 "property value \"%s\".",
2642 2643 SCF_PROPERTY_PRIVILEGES, vbuf));
2643 2644 }
2644 2645 }
2645 2646 }
2646 2647
2647 2648 if (!(get_astring_val(methpg, SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf,
2648 2649 vbuf_sz, prop, val) == 0 || get_astring_val(instpg,
2649 2650 SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf, vbuf_sz, prop, val) == 0)) {
2650 2651 if (scf_error() == SCF_ERROR_NOT_FOUND) {
2651 2652 (void) strcpy(vbuf, ":default");
2652 2653 } else {
2653 2654 return (mc_error_create(merr, ENOENT,
2654 2655 "Could not get \"%s\" property.",
2655 2656 SCF_PROPERTY_LIMIT_PRIVILEGES));
2656 2657 }
2657 2658 }
2658 2659
2659 2660 if (strcmp(vbuf, ":default") == 0)
2660 2661 /*
2661 2662 * L must default to all privileges so root NPA services see
2662 2663 * iE = all. "zone" is all privileges available in the current
2663 2664 * zone, equivalent to "all" in the global zone.
2664 2665 */
2665 2666 (void) strcpy(vbuf, "zone");
2666 2667
2667 2668 ci->lpriv_set = priv_str_to_set(vbuf, ",", NULL);
2668 2669 if (ci->lpriv_set == NULL) {
2669 2670 if (errno != EINVAL) {
2670 2671 return (mc_error_create(merr, ENOMEM, ALLOCFAIL));
2671 2672 } else {
2672 2673 return (mc_error_create(merr, EINVAL,
2673 2674 "Could not interpret \"%s\" property value \"%s\".",
2674 2675 SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf));
2675 2676 }
2676 2677 }
2677 2678
2678 2679 return (merr);
2679 2680 }
2680 2681
2681 2682 static int
2682 2683 get_environment(scf_handle_t *h, scf_propertygroup_t *pg,
2683 2684 struct method_context *mcp, scf_property_t *prop, scf_value_t *val)
2684 2685 {
2685 2686 scf_iter_t *iter;
2686 2687 scf_type_t type;
2687 2688 size_t i = 0;
2688 2689 int ret;
2689 2690
2690 2691 if (scf_pg_get_property(pg, SCF_PROPERTY_ENVIRONMENT, prop) != 0) {
2691 2692 if (scf_error() == SCF_ERROR_NOT_FOUND)
2692 2693 return (ENOENT);
2693 2694 return (scf_error());
2694 2695 }
2695 2696 if (scf_property_type(prop, &type) != 0)
2696 2697 return (scf_error());
2697 2698 if (type != SCF_TYPE_ASTRING)
2698 2699 return (EINVAL);
2699 2700 if ((iter = scf_iter_create(h)) == NULL)
2700 2701 return (scf_error());
2701 2702
2702 2703 if (scf_iter_property_values(iter, prop) != 0) {
2703 2704 ret = scf_error();
2704 2705 scf_iter_destroy(iter);
2705 2706 return (ret);
2706 2707 }
2707 2708
2708 2709 mcp->env_sz = 10;
2709 2710
2710 2711 if ((mcp->env = uu_zalloc(sizeof (*mcp->env) * mcp->env_sz)) == NULL) {
2711 2712 ret = ENOMEM;
2712 2713 goto out;
2713 2714 }
2714 2715
2715 2716 while ((ret = scf_iter_next_value(iter, val)) == 1) {
2716 2717 ret = scf_value_get_as_string(val, mcp->vbuf, mcp->vbuf_sz);
2717 2718 if (ret == -1) {
2718 2719 ret = scf_error();
2719 2720 goto out;
2720 2721 }
2721 2722
2722 2723 if ((mcp->env[i] = strdup(mcp->vbuf)) == NULL) {
2723 2724 ret = ENOMEM;
2724 2725 goto out;
2725 2726 }
2726 2727
2727 2728 if (++i == mcp->env_sz) {
2728 2729 char **env;
2729 2730 mcp->env_sz *= 2;
2730 2731 env = uu_zalloc(sizeof (*mcp->env) * mcp->env_sz);
2731 2732 if (env == NULL) {
2732 2733 ret = ENOMEM;
2733 2734 goto out;
2734 2735 }
2735 2736 (void) memcpy(env, mcp->env,
2736 2737 sizeof (*mcp->env) * (mcp->env_sz / 2));
2737 2738 free(mcp->env);
2738 2739 mcp->env = env;
2739 2740 }
2740 2741 }
2741 2742
2742 2743 if (ret == -1)
2743 2744 ret = scf_error();
2744 2745
2745 2746 out:
2746 2747 scf_iter_destroy(iter);
2747 2748 return (ret);
2748 2749 }
2749 2750
2750 2751 /*
2751 2752 * Fetch method context information from the repository, allocate and fill
2752 2753 * a method_context structure, return it in *mcpp, and return NULL.
2753 2754 *
2754 2755 * If no method_context is defined, original init context is provided, where
2755 2756 * the working directory is '/', and uid/gid are 0/0. But if a method_context
2756 2757 * is defined at any level the smf_method(5) method_context defaults are used.
2757 2758 *
2758 2759 * Return an error message structure containing the error message
2759 2760 * with context, and the error so the caller can make a decision
2760 2761 * on what to do next.
2761 2762 *
2762 2763 * Error Types :
2763 2764 * E2BIG Too many values or entry is too big
2764 2765 * EINVAL Invalid value
2765 2766 * EIO an I/O error has occured
2766 2767 * ENOENT no entry for value
2767 2768 * ENOMEM out of memory
2768 2769 * ENOTSUP Version mismatch
2769 2770 * ERANGE value is out of range
2770 2771 * EMFILE/ENFILE out of file descriptors
2771 2772 *
2772 2773 * SCF_ERROR_BACKEND_ACCESS
2773 2774 * SCF_ERROR_CONNECTION_BROKEN
2774 2775 * SCF_ERROR_DELETED
2775 2776 * SCF_ERROR_CONSTRAINT_VIOLATED
2776 2777 * SCF_ERROR_HANDLE_DESTROYED
2777 2778 * SCF_ERROR_INTERNAL
2778 2779 * SCF_ERROR_INVALID_ARGUMENT
2779 2780 * SCF_ERROR_NO_MEMORY
2780 2781 * SCF_ERROR_NO_RESOURCES
2781 2782 * SCF_ERROR_NOT_BOUND
2782 2783 * SCF_ERROR_NOT_FOUND
2783 2784 * SCF_ERROR_NOT_SET
2784 2785 * SCF_ERROR_TYPE_MISMATCH
2785 2786 *
2786 2787 */
2787 2788 mc_error_t *
2788 2789 restarter_get_method_context(uint_t version, scf_instance_t *inst,
2789 2790 scf_snapshot_t *snap, const char *mname, const char *cmdline,
2790 2791 struct method_context **mcpp)
2791 2792 {
2792 2793 scf_handle_t *h;
2793 2794 scf_propertygroup_t *methpg = NULL;
2794 2795 scf_propertygroup_t *instpg = NULL;
2795 2796 scf_propertygroup_t *pg = NULL;
2796 2797 scf_property_t *prop = NULL;
2797 2798 scf_value_t *val = NULL;
2798 2799 scf_type_t ty;
2799 2800 uint8_t use_profile;
2800 2801 int ret = 0;
2801 2802 int mc_used = 0;
2802 2803 mc_error_t *err = NULL;
2803 2804 struct method_context *cip;
2804 2805
2805 2806 if ((err = malloc(sizeof (mc_error_t))) == NULL)
2806 2807 return (mc_error_create(NULL, ENOMEM, NULL));
2807 2808
2808 2809 /* Set the type to zero to track if an error occured. */
2809 2810 err->type = 0;
2810 2811
2811 2812 if (version != RESTARTER_METHOD_CONTEXT_VERSION)
2812 2813 return (mc_error_create(err, ENOTSUP,
2813 2814 "Invalid client version %d. (Expected %d)",
2814 2815 version, RESTARTER_METHOD_CONTEXT_VERSION));
2815 2816
2816 2817 /* Get the handle before we allocate anything. */
2817 2818 h = scf_instance_handle(inst);
2818 2819 if (h == NULL)
2819 2820 return (mc_error_create(err, scf_error(),
2820 2821 scf_strerror(scf_error())));
2821 2822
2822 2823 cip = malloc(sizeof (*cip));
2823 2824 if (cip == NULL)
2824 2825 return (mc_error_create(err, ENOMEM, ALLOCFAIL));
2825 2826
2826 2827 (void) memset(cip, 0, sizeof (*cip));
2827 2828 cip->uid = (uid_t)-1;
2828 2829 cip->euid = (uid_t)-1;
2829 2830 cip->gid = (gid_t)-1;
2830 2831 cip->egid = (gid_t)-1;
2831 2832
2832 2833 cip->vbuf_sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
2833 2834 assert(cip->vbuf_sz >= 0);
2834 2835 cip->vbuf = malloc(cip->vbuf_sz);
2835 2836 if (cip->vbuf == NULL) {
2836 2837 free(cip);
2837 2838 return (mc_error_create(err, ENOMEM, ALLOCFAIL));
2838 2839 }
2839 2840
2840 2841 if ((instpg = scf_pg_create(h)) == NULL ||
2841 2842 (methpg = scf_pg_create(h)) == NULL ||
2842 2843 (prop = scf_property_create(h)) == NULL ||
2843 2844 (val = scf_value_create(h)) == NULL) {
2844 2845 err = mc_error_create(err, scf_error(),
2845 2846 "Failed to create repository object: %s\n",
2846 2847 scf_strerror(scf_error()));
2847 2848 goto out;
2848 2849 }
2849 2850
2850 2851 /*
2851 2852 * The method environment, and the credentials/profile data,
2852 2853 * may be found either in the pg for the method (methpg),
2853 2854 * or in the instance/service SCF_PG_METHOD_CONTEXT pg (named
2854 2855 * instpg below).
2855 2856 */
2856 2857
2857 2858 if (scf_instance_get_pg_composed(inst, snap, mname, methpg) !=
2858 2859 SCF_SUCCESS) {
2859 2860 err = mc_error_create(err, scf_error(), "Unable to get the "
2860 2861 "\"%s\" method, %s", mname, scf_strerror(scf_error()));
2861 2862 goto out;
2862 2863 }
2863 2864
2864 2865 if (scf_instance_get_pg_composed(inst, snap, SCF_PG_METHOD_CONTEXT,
2865 2866 instpg) != SCF_SUCCESS) {
2866 2867 if (scf_error() != SCF_ERROR_NOT_FOUND) {
2867 2868 err = mc_error_create(err, scf_error(),
2868 2869 "Unable to retrieve the \"%s\" property group, %s",
2869 2870 SCF_PG_METHOD_CONTEXT, scf_strerror(scf_error()));
2870 2871 goto out;
2871 2872 }
2872 2873 scf_pg_destroy(instpg);
2873 2874 instpg = NULL;
2874 2875 } else {
2875 2876 mc_used++;
2876 2877 }
2877 2878
2878 2879 ret = get_environment(h, methpg, cip, prop, val);
2879 2880 if (ret == ENOENT && instpg != NULL) {
2880 2881 ret = get_environment(h, instpg, cip, prop, val);
2881 2882 }
2882 2883
2883 2884 switch (ret) {
2884 2885 case 0:
2885 2886 mc_used++;
2886 2887 break;
2887 2888 case ENOENT:
2888 2889 break;
2889 2890 case ENOMEM:
2890 2891 err = mc_error_create(err, ret, "Out of memory.");
2891 2892 goto out;
2892 2893 case EINVAL:
2893 2894 err = mc_error_create(err, ret, "Invalid method environment.");
2894 2895 goto out;
2895 2896 default:
2896 2897 err = mc_error_create(err, ret,
2897 2898 "Get method environment failed : %s\n", scf_strerror(ret));
2898 2899 goto out;
2899 2900 }
2900 2901
2901 2902 pg = methpg;
2902 2903
2903 2904 ret = scf_pg_get_property(pg, SCF_PROPERTY_USE_PROFILE, prop);
2904 2905 if (ret && scf_error() == SCF_ERROR_NOT_FOUND && instpg != NULL) {
2905 2906 pg = NULL;
2906 2907 ret = scf_pg_get_property(instpg, SCF_PROPERTY_USE_PROFILE,
2907 2908 prop);
2908 2909 }
2909 2910
2910 2911 if (ret) {
2911 2912 switch (scf_error()) {
2912 2913 case SCF_ERROR_NOT_FOUND:
2913 2914 /* No profile context: use default credentials */
2914 2915 cip->uid = 0;
2915 2916 cip->gid = 0;
2916 2917 break;
2917 2918
2918 2919 case SCF_ERROR_CONNECTION_BROKEN:
2919 2920 err = mc_error_create(err, SCF_ERROR_CONNECTION_BROKEN,
2920 2921 RCBROKEN);
2921 2922 goto out;
2922 2923
2923 2924 case SCF_ERROR_DELETED:
2924 2925 err = mc_error_create(err, SCF_ERROR_NOT_FOUND,
2925 2926 "Could not find property group \"%s\"",
2926 2927 pg == NULL ? SCF_PG_METHOD_CONTEXT : mname);
2927 2928 goto out;
2928 2929
2929 2930 case SCF_ERROR_HANDLE_MISMATCH:
2930 2931 case SCF_ERROR_INVALID_ARGUMENT:
2931 2932 case SCF_ERROR_NOT_SET:
2932 2933 default:
2933 2934 bad_fail("scf_pg_get_property", scf_error());
2934 2935 }
2935 2936 } else {
2936 2937 if (scf_property_type(prop, &ty) != SCF_SUCCESS) {
2937 2938 ret = scf_error();
2938 2939 switch (ret) {
2939 2940 case SCF_ERROR_CONNECTION_BROKEN:
2940 2941 err = mc_error_create(err,
2941 2942 SCF_ERROR_CONNECTION_BROKEN, RCBROKEN);
2942 2943 break;
2943 2944
2944 2945 case SCF_ERROR_DELETED:
2945 2946 err = mc_error_create(err,
2946 2947 SCF_ERROR_NOT_FOUND,
2947 2948 "Could not find property group \"%s\"",
2948 2949 pg == NULL ? SCF_PG_METHOD_CONTEXT : mname);
2949 2950 break;
2950 2951
2951 2952 case SCF_ERROR_NOT_SET:
2952 2953 default:
2953 2954 bad_fail("scf_property_type", ret);
2954 2955 }
2955 2956
2956 2957 goto out;
2957 2958 }
2958 2959
2959 2960 if (ty != SCF_TYPE_BOOLEAN) {
2960 2961 err = mc_error_create(err,
2961 2962 SCF_ERROR_TYPE_MISMATCH,
2962 2963 "\"%s\" property is not boolean in property group "
2963 2964 "\"%s\".", SCF_PROPERTY_USE_PROFILE,
2964 2965 pg == NULL ? SCF_PG_METHOD_CONTEXT : mname);
2965 2966 goto out;
2966 2967 }
2967 2968
2968 2969 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
2969 2970 ret = scf_error();
2970 2971 switch (ret) {
2971 2972 case SCF_ERROR_CONNECTION_BROKEN:
2972 2973 err = mc_error_create(err,
2973 2974 SCF_ERROR_CONNECTION_BROKEN, RCBROKEN);
2974 2975 break;
2975 2976
2976 2977 case SCF_ERROR_CONSTRAINT_VIOLATED:
2977 2978 err = mc_error_create(err,
2978 2979 SCF_ERROR_CONSTRAINT_VIOLATED,
2979 2980 "\"%s\" property has multiple values.",
2980 2981 SCF_PROPERTY_USE_PROFILE);
2981 2982 break;
2982 2983
2983 2984 case SCF_ERROR_NOT_FOUND:
2984 2985 err = mc_error_create(err,
2985 2986 SCF_ERROR_NOT_FOUND,
2986 2987 "\"%s\" property has no values.",
2987 2988 SCF_PROPERTY_USE_PROFILE);
2988 2989 break;
2989 2990 default:
2990 2991 bad_fail("scf_property_get_value", ret);
2991 2992 }
2992 2993
2993 2994 goto out;
2994 2995 }
2995 2996
2996 2997 mc_used++;
2997 2998 ret = scf_value_get_boolean(val, &use_profile);
2998 2999 assert(ret == SCF_SUCCESS);
2999 3000
3000 3001 /* get ids & privileges */
3001 3002 if (use_profile)
3002 3003 err = get_profile(pg, instpg, prop, val, cmdline,
3003 3004 cip, err);
3004 3005 else
3005 3006 err = get_ids(pg, instpg, prop, val, cip, err);
3006 3007
3007 3008 if (err->type != 0)
3008 3009 goto out;
3009 3010 }
3010 3011
3011 3012 /* get working directory */
3012 3013 if ((methpg != NULL && scf_pg_get_property(methpg,
3013 3014 SCF_PROPERTY_WORKING_DIRECTORY, prop) == SCF_SUCCESS) ||
3014 3015 (instpg != NULL && scf_pg_get_property(instpg,
3015 3016 SCF_PROPERTY_WORKING_DIRECTORY, prop) == SCF_SUCCESS)) {
3016 3017 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3017 3018 ret = scf_error();
3018 3019 switch (ret) {
3019 3020 case SCF_ERROR_CONNECTION_BROKEN:
3020 3021 err = mc_error_create(err, ret, RCBROKEN);
3021 3022 break;
3022 3023
3023 3024 case SCF_ERROR_CONSTRAINT_VIOLATED:
3024 3025 err = mc_error_create(err, ret,
3025 3026 "\"%s\" property has multiple values.",
3026 3027 SCF_PROPERTY_WORKING_DIRECTORY);
3027 3028 break;
3028 3029
3029 3030 case SCF_ERROR_NOT_FOUND:
3030 3031 err = mc_error_create(err, ret,
3031 3032 "\"%s\" property has no values.",
3032 3033 SCF_PROPERTY_WORKING_DIRECTORY);
3033 3034 break;
3034 3035
3035 3036 default:
3036 3037 bad_fail("scf_property_get_value", ret);
3037 3038 }
3038 3039
3039 3040 goto out;
3040 3041 }
3041 3042
3042 3043 mc_used++;
3043 3044 ret = scf_value_get_astring(val, cip->vbuf, cip->vbuf_sz);
3044 3045 assert(ret != -1);
3045 3046 } else {
3046 3047 ret = scf_error();
3047 3048 switch (ret) {
3048 3049 case SCF_ERROR_NOT_FOUND:
3049 3050 /* okay if missing. */
3050 3051 (void) strcpy(cip->vbuf, ":default");
3051 3052 break;
3052 3053
3053 3054 case SCF_ERROR_CONNECTION_BROKEN:
3054 3055 err = mc_error_create(err, ret, RCBROKEN);
3055 3056 goto out;
3056 3057
3057 3058 case SCF_ERROR_DELETED:
3058 3059 err = mc_error_create(err, ret,
3059 3060 "Property group could not be found");
3060 3061 goto out;
3061 3062
3062 3063 case SCF_ERROR_HANDLE_MISMATCH:
3063 3064 case SCF_ERROR_INVALID_ARGUMENT:
3064 3065 case SCF_ERROR_NOT_SET:
3065 3066 default:
3066 3067 bad_fail("scf_pg_get_property", ret);
3067 3068 }
3068 3069 }
3069 3070
3070 3071 if (strcmp(cip->vbuf, ":default") == 0 ||
3071 3072 strcmp(cip->vbuf, ":home") == 0) {
3072 3073 switch (ret = lookup_pwd(cip)) {
3073 3074 case 0:
3074 3075 break;
3075 3076
3076 3077 case ENOMEM:
3077 3078 err = mc_error_create(err, ret, "Out of memory.");
3078 3079 goto out;
3079 3080
3080 3081 case ENOENT:
3081 3082 case EIO:
3082 3083 case EMFILE:
3083 3084 case ENFILE:
3084 3085 err = mc_error_create(err, ret,
3085 3086 "Could not get passwd entry.");
3086 3087 goto out;
3087 3088
3088 3089 default:
3089 3090 bad_fail("lookup_pwd", ret);
3090 3091 }
3091 3092
3092 3093 cip->working_dir = strdup(cip->pwd.pw_dir);
3093 3094 if (cip->working_dir == NULL) {
3094 3095 err = mc_error_create(err, ENOMEM, ALLOCFAIL);
3095 3096 goto out;
3096 3097 }
3097 3098 } else {
3098 3099 cip->working_dir = strdup(cip->vbuf);
3099 3100 if (cip->working_dir == NULL) {
3100 3101 err = mc_error_create(err, ENOMEM, ALLOCFAIL);
3101 3102 goto out;
3102 3103 }
3103 3104 }
3104 3105
3105 3106 /* get (optional) corefile pattern */
3106 3107 if ((methpg != NULL && scf_pg_get_property(methpg,
3107 3108 SCF_PROPERTY_COREFILE_PATTERN, prop) == SCF_SUCCESS) ||
3108 3109 (instpg != NULL && scf_pg_get_property(instpg,
3109 3110 SCF_PROPERTY_COREFILE_PATTERN, prop) == SCF_SUCCESS)) {
3110 3111 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3111 3112 ret = scf_error();
3112 3113 switch (ret) {
3113 3114 case SCF_ERROR_CONNECTION_BROKEN:
3114 3115 err = mc_error_create(err, ret, RCBROKEN);
3115 3116 break;
3116 3117
3117 3118 case SCF_ERROR_CONSTRAINT_VIOLATED:
3118 3119 err = mc_error_create(err, ret,
3119 3120 "\"%s\" property has multiple values.",
3120 3121 SCF_PROPERTY_COREFILE_PATTERN);
3121 3122 break;
3122 3123
3123 3124 case SCF_ERROR_NOT_FOUND:
3124 3125 err = mc_error_create(err, ret,
3125 3126 "\"%s\" property has no values.",
3126 3127 SCF_PROPERTY_COREFILE_PATTERN);
3127 3128 break;
3128 3129
3129 3130 default:
3130 3131 bad_fail("scf_property_get_value", ret);
3131 3132 }
3132 3133
3133 3134 } else {
3134 3135
3135 3136 ret = scf_value_get_astring(val, cip->vbuf,
3136 3137 cip->vbuf_sz);
3137 3138 assert(ret != -1);
3138 3139
3139 3140 cip->corefile_pattern = strdup(cip->vbuf);
3140 3141 if (cip->corefile_pattern == NULL) {
3141 3142 err = mc_error_create(err, ENOMEM, ALLOCFAIL);
3142 3143 goto out;
3143 3144 }
3144 3145 }
3145 3146
3146 3147 mc_used++;
3147 3148 } else {
3148 3149 ret = scf_error();
3149 3150 switch (ret) {
3150 3151 case SCF_ERROR_NOT_FOUND:
3151 3152 /* okay if missing. */
3152 3153 break;
3153 3154
3154 3155 case SCF_ERROR_CONNECTION_BROKEN:
3155 3156 err = mc_error_create(err, ret, RCBROKEN);
3156 3157 goto out;
3157 3158
3158 3159 case SCF_ERROR_DELETED:
3159 3160 err = mc_error_create(err, ret,
3160 3161 "Property group could not be found");
3161 3162 goto out;
3162 3163
3163 3164 case SCF_ERROR_HANDLE_MISMATCH:
3164 3165 case SCF_ERROR_INVALID_ARGUMENT:
3165 3166 case SCF_ERROR_NOT_SET:
3166 3167 default:
3167 3168 bad_fail("scf_pg_get_property", ret);
3168 3169 }
3169 3170 }
3170 3171
3171 3172 if (restarter_rm_libs_loadable()) {
3172 3173 /* get project */
3173 3174 if ((methpg != NULL && scf_pg_get_property(methpg,
3174 3175 SCF_PROPERTY_PROJECT, prop) == SCF_SUCCESS) ||
3175 3176 (instpg != NULL && scf_pg_get_property(instpg,
3176 3177 SCF_PROPERTY_PROJECT, prop) == SCF_SUCCESS)) {
3177 3178 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3178 3179 ret = scf_error();
3179 3180 switch (ret) {
3180 3181 case SCF_ERROR_CONNECTION_BROKEN:
3181 3182 err = mc_error_create(err, ret,
3182 3183 RCBROKEN);
3183 3184 break;
3184 3185
3185 3186 case SCF_ERROR_CONSTRAINT_VIOLATED:
3186 3187 err = mc_error_create(err, ret,
3187 3188 "\"%s\" property has multiple "
3188 3189 "values.", SCF_PROPERTY_PROJECT);
3189 3190 break;
3190 3191
3191 3192 case SCF_ERROR_NOT_FOUND:
3192 3193 err = mc_error_create(err, ret,
3193 3194 "\"%s\" property has no values.",
3194 3195 SCF_PROPERTY_PROJECT);
3195 3196 break;
3196 3197
3197 3198 default:
3198 3199 bad_fail("scf_property_get_value", ret);
3199 3200 }
3200 3201
3201 3202 (void) strcpy(cip->vbuf, ":default");
3202 3203 } else {
3203 3204 ret = scf_value_get_astring(val, cip->vbuf,
3204 3205 cip->vbuf_sz);
3205 3206 assert(ret != -1);
3206 3207 }
3207 3208
3208 3209 mc_used++;
3209 3210 } else {
3210 3211 (void) strcpy(cip->vbuf, ":default");
3211 3212 }
3212 3213
3213 3214 switch (ret = get_projid(cip->vbuf, cip)) {
3214 3215 case 0:
3215 3216 break;
3216 3217
3217 3218 case ENOMEM:
3218 3219 err = mc_error_create(err, ret, "Out of memory.");
3219 3220 goto out;
3220 3221
3221 3222 case ENOENT:
3222 3223 err = mc_error_create(err, ret,
3223 3224 "Missing passwd or project entry for \"%s\".",
3224 3225 cip->vbuf);
3225 3226 goto out;
3226 3227
3227 3228 case EIO:
3228 3229 err = mc_error_create(err, ret, "I/O error.");
3229 3230 goto out;
3230 3231
3231 3232 case EMFILE:
3232 3233 case ENFILE:
3233 3234 err = mc_error_create(err, ret,
3234 3235 "Out of file descriptors.");
3235 3236 goto out;
3236 3237
3237 3238 case -1:
3238 3239 err = mc_error_create(err, ret,
3239 3240 "Name service switch is misconfigured.");
3240 3241 goto out;
3241 3242
3242 3243 case ERANGE:
3243 3244 case E2BIG:
3244 3245 err = mc_error_create(err, ret,
3245 3246 "Project ID \"%s\" too big.", cip->vbuf);
3246 3247 goto out;
3247 3248
3248 3249 case EINVAL:
3249 3250 err = mc_error_create(err, ret,
3250 3251 "Project ID \"%s\" is invalid.", cip->vbuf);
3251 3252 goto out;
3252 3253
3253 3254 default:
3254 3255 bad_fail("get_projid", ret);
3255 3256 }
3256 3257
3257 3258 /* get resource pool */
3258 3259 if ((methpg != NULL && scf_pg_get_property(methpg,
3259 3260 SCF_PROPERTY_RESOURCE_POOL, prop) == SCF_SUCCESS) ||
3260 3261 (instpg != NULL && scf_pg_get_property(instpg,
3261 3262 SCF_PROPERTY_RESOURCE_POOL, prop) == SCF_SUCCESS)) {
3262 3263 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3263 3264 ret = scf_error();
3264 3265 switch (ret) {
3265 3266 case SCF_ERROR_CONNECTION_BROKEN:
3266 3267 err = mc_error_create(err, ret,
3267 3268 RCBROKEN);
3268 3269 break;
3269 3270
3270 3271 case SCF_ERROR_CONSTRAINT_VIOLATED:
3271 3272 err = mc_error_create(err, ret,
3272 3273 "\"%s\" property has multiple "
3273 3274 "values.",
3274 3275 SCF_PROPERTY_RESOURCE_POOL);
3275 3276 break;
3276 3277
3277 3278 case SCF_ERROR_NOT_FOUND:
3278 3279 err = mc_error_create(err, ret,
3279 3280 "\"%s\" property has no "
3280 3281 "values.",
3281 3282 SCF_PROPERTY_RESOURCE_POOL);
3282 3283 break;
3283 3284
3284 3285 default:
3285 3286 bad_fail("scf_property_get_value", ret);
3286 3287 }
3287 3288
3288 3289 (void) strcpy(cip->vbuf, ":default");
3289 3290 } else {
3290 3291 ret = scf_value_get_astring(val, cip->vbuf,
3291 3292 cip->vbuf_sz);
3292 3293 assert(ret != -1);
3293 3294 }
3294 3295
3295 3296 mc_used++;
3296 3297 } else {
3297 3298 ret = scf_error();
3298 3299 switch (ret) {
3299 3300 case SCF_ERROR_NOT_FOUND:
3300 3301 /* okay if missing. */
3301 3302 (void) strcpy(cip->vbuf, ":default");
3302 3303 break;
3303 3304
3304 3305 case SCF_ERROR_CONNECTION_BROKEN:
3305 3306 err = mc_error_create(err, ret, RCBROKEN);
3306 3307 goto out;
3307 3308
3308 3309 case SCF_ERROR_DELETED:
3309 3310 err = mc_error_create(err, ret,
3310 3311 "property group could not be found.");
3311 3312 goto out;
3312 3313
3313 3314 case SCF_ERROR_HANDLE_MISMATCH:
3314 3315 case SCF_ERROR_INVALID_ARGUMENT:
3315 3316 case SCF_ERROR_NOT_SET:
3316 3317 default:
3317 3318 bad_fail("scf_pg_get_property", ret);
3318 3319 }
3319 3320 }
3320 3321
3321 3322 if (strcmp(cip->vbuf, ":default") != 0) {
3322 3323 cip->resource_pool = strdup(cip->vbuf);
3323 3324 if (cip->resource_pool == NULL) {
3324 3325 err = mc_error_create(err, ENOMEM, ALLOCFAIL);
3325 3326 goto out;
↓ open down ↓ |
3292 lines elided |
↑ open up ↑ |
3326 3327 }
3327 3328 }
3328 3329 }
3329 3330
3330 3331 /*
3331 3332 * A method_context was not used for any configurable
3332 3333 * elements or attributes, so reset and use the simple
3333 3334 * defaults that provide historic init behavior.
3334 3335 */
3335 3336 if (mc_used == 0) {
3337 + free(cip->pwbuf);
3338 + free(cip->vbuf);
3339 + free(cip->working_dir);
3340 +
3336 3341 (void) memset(cip, 0, sizeof (*cip));
3337 3342 cip->uid = 0;
3338 3343 cip->gid = 0;
3339 3344 cip->euid = (uid_t)-1;
3340 3345 cip->egid = (gid_t)-1;
3341 3346 }
3342 3347
3343 3348 *mcpp = cip;
3344 3349
3345 3350 out:
3346 3351 (void) scf_value_destroy(val);
3347 3352 scf_property_destroy(prop);
3348 3353 scf_pg_destroy(instpg);
3349 3354 scf_pg_destroy(methpg);
3350 3355
3351 - if (cip->pwbuf != NULL)
3356 + if (cip->pwbuf != NULL) {
3352 3357 free(cip->pwbuf);
3358 + cip->pwbuf = NULL;
3359 + }
3360 +
3353 3361 free(cip->vbuf);
3354 3362
3355 3363 if (err->type != 0) {
3356 3364 restarter_free_method_context(cip);
3357 3365 } else {
3358 3366 restarter_mc_error_destroy(err);
3359 3367 err = NULL;
3360 3368 }
3361 3369
3362 3370 return (err);
3363 3371 }
3364 3372
3365 3373 /*
3366 3374 * Modify the current process per the given method_context. On success, returns
3367 3375 * 0. Note that the environment is not modified by this function to include the
3368 3376 * environment variables in cip->env.
3369 3377 *
3370 3378 * On failure, sets *fp to NULL or the name of the function which failed,
3371 3379 * and returns one of the following error codes. The words in parentheses are
3372 3380 * the values to which *fp may be set for the error case.
3373 3381 * ENOMEM - malloc() failed
3374 3382 * EIO - an I/O error occurred (getpwuid_r, chdir)
3375 3383 * EMFILE - process is out of file descriptors (getpwuid_r)
3376 3384 * ENFILE - system is out of file handles (getpwuid_r)
3377 3385 * EINVAL - gid or egid is out of range (setregid)
3378 3386 * ngroups is too big (setgroups)
3379 3387 * project's project id is bad (setproject)
3380 3388 * uid or euid is out of range (setreuid)
3381 3389 * poolname is invalid (pool_set_binding)
3382 3390 * EPERM - insufficient privilege (setregid, initgroups, setgroups, setppriv,
3383 3391 * setproject, setreuid, settaskid)
3384 3392 * ENOENT - uid has a passwd entry but no shadow entry
3385 3393 * working_dir does not exist (chdir)
3386 3394 * uid has no passwd entry
3387 3395 * the pool could not be found (pool_set_binding)
3388 3396 * EFAULT - lpriv_set or priv_set has a bad address (setppriv)
3389 3397 * working_dir has a bad address (chdir)
3390 3398 * EACCES - could not access working_dir (chdir)
3391 3399 * in a TASK_FINAL task (setproject, settaskid)
3392 3400 * no resource pool accepting default binding exists (setproject)
3393 3401 * ELOOP - too many symbolic links in working_dir (chdir)
3394 3402 * ENAMETOOLONG - working_dir is too long (chdir)
3395 3403 * ENOLINK - working_dir is on an inaccessible remote machine (chdir)
3396 3404 * ENOTDIR - working_dir is not a directory (chdir)
3397 3405 * ESRCH - uid is not a user of project (setproject)
3398 3406 * project is invalid (setproject)
3399 3407 * the resource pool specified for project is unknown (setproject)
3400 3408 * EBADF - the configuration for the pool is invalid (pool_set_binding)
3401 3409 * -1 - core_set_process_path() failed (core_set_process_path)
3402 3410 * a resource control assignment failed (setproject)
3403 3411 * a system error occurred during pool_set_binding (pool_set_binding)
3404 3412 */
3405 3413 int
3406 3414 restarter_set_method_context(struct method_context *cip, const char **fp)
3407 3415 {
3408 3416 pid_t mypid = -1;
3409 3417 int r, ret;
3410 3418
3411 3419 cip->pwbuf = NULL;
3412 3420 *fp = NULL;
3413 3421
3414 3422 if (cip->gid != (gid_t)-1) {
3415 3423 if (setregid(cip->gid,
3416 3424 cip->egid != (gid_t)-1 ? cip->egid : cip->gid) != 0) {
3417 3425 *fp = "setregid";
3418 3426
3419 3427 ret = errno;
3420 3428 assert(ret == EINVAL || ret == EPERM);
3421 3429 goto out;
3422 3430 }
3423 3431 } else {
3424 3432 if (cip->pwbuf == NULL) {
3425 3433 switch (ret = lookup_pwd(cip)) {
3426 3434 case 0:
3427 3435 break;
3428 3436
3429 3437 case ENOMEM:
3430 3438 case ENOENT:
3431 3439 *fp = NULL;
3432 3440 goto out;
3433 3441
3434 3442 case EIO:
3435 3443 case EMFILE:
3436 3444 case ENFILE:
3437 3445 *fp = "getpwuid_r";
3438 3446 goto out;
3439 3447
3440 3448 default:
3441 3449 bad_fail("lookup_pwd", ret);
3442 3450 }
3443 3451 }
3444 3452
3445 3453 if (setregid(cip->pwd.pw_gid,
3446 3454 cip->egid != (gid_t)-1 ?
3447 3455 cip->egid : cip->pwd.pw_gid) != 0) {
3448 3456 *fp = "setregid";
3449 3457
3450 3458 ret = errno;
3451 3459 assert(ret == EINVAL || ret == EPERM);
3452 3460 goto out;
3453 3461 }
3454 3462 }
3455 3463
3456 3464 if (cip->ngroups == -1) {
3457 3465 if (cip->pwbuf == NULL) {
3458 3466 switch (ret = lookup_pwd(cip)) {
3459 3467 case 0:
3460 3468 break;
3461 3469
3462 3470 case ENOMEM:
3463 3471 case ENOENT:
3464 3472 *fp = NULL;
3465 3473 goto out;
3466 3474
3467 3475 case EIO:
3468 3476 case EMFILE:
3469 3477 case ENFILE:
3470 3478 *fp = "getpwuid_r";
3471 3479 goto out;
3472 3480
3473 3481 default:
3474 3482 bad_fail("lookup_pwd", ret);
3475 3483 }
3476 3484 }
3477 3485
3478 3486 /* Ok if cip->gid == -1 */
3479 3487 if (initgroups(cip->pwd.pw_name, cip->gid) != 0) {
3480 3488 *fp = "initgroups";
3481 3489 ret = errno;
3482 3490 assert(ret == EPERM);
3483 3491 goto out;
3484 3492 }
3485 3493 } else if (cip->ngroups > 0 &&
3486 3494 setgroups(cip->ngroups, cip->groups) != 0) {
3487 3495 *fp = "setgroups";
3488 3496
3489 3497 ret = errno;
3490 3498 assert(ret == EINVAL || ret == EPERM);
3491 3499 goto out;
3492 3500 }
3493 3501
3494 3502 if (cip->corefile_pattern != NULL) {
3495 3503 mypid = getpid();
3496 3504
3497 3505 if (core_set_process_path(cip->corefile_pattern,
3498 3506 strlen(cip->corefile_pattern) + 1, mypid) != 0) {
3499 3507 *fp = "core_set_process_path";
3500 3508 ret = -1;
3501 3509 goto out;
3502 3510 }
3503 3511 }
3504 3512
3505 3513 if (restarter_rm_libs_loadable()) {
3506 3514 if (cip->project == NULL) {
3507 3515 if (settaskid(getprojid(), TASK_NORMAL) == -1) {
3508 3516 switch (errno) {
3509 3517 case EACCES:
3510 3518 case EPERM:
3511 3519 *fp = "settaskid";
3512 3520 ret = errno;
3513 3521 goto out;
3514 3522
3515 3523 case EINVAL:
3516 3524 default:
3517 3525 bad_fail("settaskid", errno);
3518 3526 }
3519 3527 }
3520 3528 } else {
3521 3529 switch (ret = lookup_pwd(cip)) {
3522 3530 case 0:
3523 3531 break;
3524 3532
3525 3533 case ENOMEM:
3526 3534 case ENOENT:
3527 3535 *fp = NULL;
3528 3536 goto out;
3529 3537
3530 3538 case EIO:
3531 3539 case EMFILE:
3532 3540 case ENFILE:
3533 3541 *fp = "getpwuid_r";
3534 3542 goto out;
3535 3543
3536 3544 default:
3537 3545 bad_fail("lookup_pwd", ret);
3538 3546 }
3539 3547
3540 3548 *fp = "setproject";
3541 3549
3542 3550 switch (setproject(cip->project, cip->pwd.pw_name,
3543 3551 TASK_NORMAL)) {
3544 3552 case 0:
3545 3553 break;
3546 3554
3547 3555 case SETPROJ_ERR_TASK:
3548 3556 case SETPROJ_ERR_POOL:
3549 3557 ret = errno;
3550 3558 goto out;
3551 3559
3552 3560 default:
3553 3561 ret = -1;
3554 3562 goto out;
3555 3563 }
3556 3564 }
3557 3565
3558 3566 if (cip->resource_pool != NULL) {
3559 3567 if (mypid == -1)
3560 3568 mypid = getpid();
3561 3569
3562 3570 *fp = "pool_set_binding";
3563 3571
3564 3572 if (pool_set_binding(cip->resource_pool, P_PID,
3565 3573 mypid) != PO_SUCCESS) {
3566 3574 switch (pool_error()) {
3567 3575 case POE_INVALID_SEARCH:
3568 3576 ret = ENOENT;
3569 3577 break;
3570 3578
3571 3579 case POE_BADPARAM:
3572 3580 ret = EINVAL;
3573 3581 break;
3574 3582
3575 3583 case POE_INVALID_CONF:
3576 3584 ret = EBADF;
3577 3585 break;
3578 3586
3579 3587 case POE_SYSTEM:
3580 3588 ret = -1;
3581 3589 break;
3582 3590
3583 3591 default:
3584 3592 bad_fail("pool_set_binding",
3585 3593 pool_error());
3586 3594 }
3587 3595
3588 3596 goto out;
3589 3597 }
3590 3598 }
3591 3599 }
3592 3600
3593 3601 /*
3594 3602 * Now, we have to assume our ID. If the UID is 0, we want it to be
3595 3603 * privilege-aware, otherwise the limit set gets used instead of E/P.
3596 3604 * We can do this by setting P as well, which keeps
3597 3605 * PA status (see priv_can_clear_PA()).
3598 3606 */
3599 3607
3600 3608 *fp = "setppriv";
3601 3609
3602 3610 if (cip->lpriv_set != NULL) {
3603 3611 if (setppriv(PRIV_SET, PRIV_LIMIT, cip->lpriv_set) != 0) {
3604 3612 ret = errno;
3605 3613 assert(ret == EFAULT || ret == EPERM);
3606 3614 goto out;
3607 3615 }
3608 3616 }
3609 3617 if (cip->priv_set != NULL) {
3610 3618 if (setppriv(PRIV_SET, PRIV_INHERITABLE, cip->priv_set) != 0) {
3611 3619 ret = errno;
3612 3620 assert(ret == EFAULT || ret == EPERM);
3613 3621 goto out;
3614 3622 }
3615 3623 }
3616 3624
3617 3625 /*
3618 3626 * If the limit privset is already set, then must be privilege
3619 3627 * aware. Otherwise, don't assume anything, and force privilege
3620 3628 * aware status.
3621 3629 */
3622 3630
3623 3631 if (cip->lpriv_set == NULL && cip->priv_set != NULL) {
3624 3632 ret = setpflags(PRIV_AWARE, 1);
3625 3633 assert(ret == 0);
3626 3634 }
3627 3635
3628 3636 *fp = "setreuid";
3629 3637 if (setreuid(cip->uid,
3630 3638 cip->euid != (uid_t)-1 ? cip->euid : cip->uid) != 0) {
3631 3639 ret = errno;
3632 3640 assert(ret == EINVAL || ret == EPERM);
3633 3641 goto out;
3634 3642 }
3635 3643
3636 3644 *fp = "setppriv";
3637 3645 if (cip->priv_set != NULL) {
3638 3646 if (setppriv(PRIV_SET, PRIV_PERMITTED, cip->priv_set) != 0) {
3639 3647 ret = errno;
3640 3648 assert(ret == EFAULT || ret == EPERM);
3641 3649 goto out;
3642 3650 }
3643 3651 }
3644 3652
3645 3653 /*
3646 3654 * The last thing to do is chdir to the specified working directory.
3647 3655 * This should come after the uid switching as only the user might
3648 3656 * have access to the specified directory.
3649 3657 */
3650 3658 if (cip->working_dir != NULL) {
3651 3659 do {
3652 3660 r = chdir(cip->working_dir);
3653 3661 } while (r != 0 && errno == EINTR);
3654 3662 if (r != 0) {
3655 3663 *fp = "chdir";
3656 3664 ret = errno;
3657 3665 goto out;
3658 3666 }
3659 3667 }
3660 3668
3661 3669 ret = 0;
3662 3670 out:
3663 3671 free(cip->pwbuf);
3664 3672 cip->pwbuf = NULL;
3665 3673 return (ret);
3666 3674 }
3667 3675
3668 3676 void
3669 3677 restarter_free_method_context(struct method_context *mcp)
3670 3678 {
3671 3679 size_t i;
3672 3680
3673 3681 if (mcp->lpriv_set != NULL)
3674 3682 priv_freeset(mcp->lpriv_set);
3675 3683 if (mcp->priv_set != NULL)
3676 3684 priv_freeset(mcp->priv_set);
3677 3685
3678 3686 if (mcp->env != NULL) {
3679 3687 for (i = 0; i < mcp->env_sz; i++)
3680 3688 free(mcp->env[i]);
3681 3689 free(mcp->env);
3682 3690 }
3683 3691
3684 3692 free(mcp->working_dir);
3685 3693 free(mcp->corefile_pattern);
3686 3694 free(mcp->project);
3687 3695 free(mcp->resource_pool);
3688 3696 free(mcp);
3689 3697 }
3690 3698
3691 3699 /*
3692 3700 * Method keyword functions
3693 3701 */
3694 3702
3695 3703 int
3696 3704 restarter_is_null_method(const char *meth)
3697 3705 {
3698 3706 return (strcmp(meth, MKW_TRUE) == 0);
3699 3707 }
3700 3708
3701 3709 static int
3702 3710 is_kill_method(const char *method, const char *kill_str,
3703 3711 size_t kill_str_len)
3704 3712 {
3705 3713 const char *cp;
3706 3714 int sig;
3707 3715
3708 3716 if (strncmp(method, kill_str, kill_str_len) != 0 ||
3709 3717 (method[kill_str_len] != '\0' &&
3710 3718 !isspace(method[kill_str_len])))
3711 3719 return (-1);
3712 3720
3713 3721 cp = method + kill_str_len;
3714 3722 while (*cp != '\0' && isspace(*cp))
3715 3723 ++cp;
3716 3724
3717 3725 if (*cp == '\0')
3718 3726 return (SIGTERM);
3719 3727
3720 3728 if (*cp != '-')
3721 3729 return (-1);
3722 3730
3723 3731 return (str2sig(cp + 1, &sig) == 0 ? sig : -1);
3724 3732 }
3725 3733
3726 3734 int
3727 3735 restarter_is_kill_proc_method(const char *method)
3728 3736 {
3729 3737 return (is_kill_method(method, MKW_KILL_PROC,
3730 3738 sizeof (MKW_KILL_PROC) - 1));
3731 3739 }
3732 3740
3733 3741 int
3734 3742 restarter_is_kill_method(const char *method)
3735 3743 {
3736 3744 return (is_kill_method(method, MKW_KILL, sizeof (MKW_KILL) - 1));
3737 3745 }
3738 3746
3739 3747 /*
3740 3748 * Stubs for now.
3741 3749 */
3742 3750
3743 3751 /* ARGSUSED */
3744 3752 int
3745 3753 restarter_event_get_enabled(restarter_event_t *e)
3746 3754 {
3747 3755 return (-1);
3748 3756 }
3749 3757
3750 3758 /* ARGSUSED */
3751 3759 uint64_t
3752 3760 restarter_event_get_seq(restarter_event_t *e)
3753 3761 {
3754 3762 return (-1);
3755 3763 }
3756 3764
3757 3765 /* ARGSUSED */
3758 3766 void
3759 3767 restarter_event_get_time(restarter_event_t *e, hrtime_t *time)
3760 3768 {
3761 3769 }
3762 3770
3763 3771 /*
3764 3772 * Check for and validate fmri specified in restarter_actions/auxiliary_fmri
3765 3773 * 0 - Success
3766 3774 * 1 - Failure
3767 3775 */
3768 3776 int
3769 3777 restarter_inst_validate_ractions_aux_fmri(scf_instance_t *inst)
3770 3778 {
3771 3779 scf_handle_t *h;
3772 3780 scf_propertygroup_t *pg;
3773 3781 scf_property_t *prop;
3774 3782 scf_value_t *val;
3775 3783 char *aux_fmri;
3776 3784 size_t size = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
3777 3785 int ret = 1;
3778 3786
3779 3787 if ((aux_fmri = malloc(size)) == NULL)
3780 3788 return (1);
3781 3789
3782 3790 h = scf_instance_handle(inst);
3783 3791
3784 3792 pg = scf_pg_create(h);
3785 3793 prop = scf_property_create(h);
3786 3794 val = scf_value_create(h);
3787 3795 if (pg == NULL || prop == NULL || val == NULL)
3788 3796 goto out;
3789 3797
3790 3798 if (instance_get_or_add_pg(inst, SCF_PG_RESTARTER_ACTIONS,
3791 3799 SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS,
3792 3800 pg) != SCF_SUCCESS)
3793 3801 goto out;
3794 3802
3795 3803 if (get_astring_val(pg, SCF_PROPERTY_AUX_FMRI, aux_fmri, size,
3796 3804 prop, val) != SCF_SUCCESS)
3797 3805 goto out;
3798 3806
3799 3807 if (scf_parse_fmri(aux_fmri, NULL, NULL, NULL, NULL, NULL,
3800 3808 NULL) != SCF_SUCCESS)
3801 3809 goto out;
3802 3810
3803 3811 ret = 0;
3804 3812
3805 3813 out:
3806 3814 free(aux_fmri);
3807 3815 scf_value_destroy(val);
3808 3816 scf_property_destroy(prop);
3809 3817 scf_pg_destroy(pg);
3810 3818 return (ret);
3811 3819 }
3812 3820
3813 3821 /*
3814 3822 * Get instance's boolean value in restarter_actions/auxiliary_tty
3815 3823 * Return -1 on failure
3816 3824 */
3817 3825 int
3818 3826 restarter_inst_ractions_from_tty(scf_instance_t *inst)
3819 3827 {
3820 3828 scf_handle_t *h;
3821 3829 scf_propertygroup_t *pg;
3822 3830 scf_property_t *prop;
3823 3831 scf_value_t *val;
3824 3832 uint8_t has_tty;
3825 3833 int ret = -1;
3826 3834
3827 3835 h = scf_instance_handle(inst);
3828 3836 pg = scf_pg_create(h);
3829 3837 prop = scf_property_create(h);
3830 3838 val = scf_value_create(h);
3831 3839 if (pg == NULL || prop == NULL || val == NULL)
3832 3840 goto out;
3833 3841
3834 3842 if (instance_get_or_add_pg(inst, SCF_PG_RESTARTER_ACTIONS,
3835 3843 SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS,
3836 3844 pg) != SCF_SUCCESS)
3837 3845 goto out;
3838 3846
3839 3847 if (get_boolean_val(pg, SCF_PROPERTY_AUX_TTY, &has_tty, prop,
3840 3848 val) != SCF_SUCCESS)
3841 3849 goto out;
3842 3850
3843 3851 ret = has_tty;
3844 3852
3845 3853 out:
3846 3854 scf_value_destroy(val);
3847 3855 scf_property_destroy(prop);
3848 3856 scf_pg_destroy(pg);
3849 3857 return (ret);
3850 3858 }
3851 3859
3852 3860 static int
3853 3861 restarter_inst_set_astring_prop(scf_instance_t *inst, const char *pgname,
3854 3862 const char *pgtype, uint32_t pgflags, const char *pname, const char *str)
3855 3863 {
3856 3864 scf_handle_t *h;
3857 3865 scf_propertygroup_t *pg;
3858 3866 scf_transaction_t *t;
3859 3867 scf_transaction_entry_t *e;
3860 3868 scf_value_t *v;
3861 3869 int ret = 1, r;
3862 3870
3863 3871 h = scf_instance_handle(inst);
3864 3872
3865 3873 pg = scf_pg_create(h);
3866 3874 t = scf_transaction_create(h);
3867 3875 e = scf_entry_create(h);
3868 3876 v = scf_value_create(h);
3869 3877 if (pg == NULL || t == NULL || e == NULL || v == NULL)
3870 3878 goto out;
3871 3879
3872 3880 if (instance_get_or_add_pg(inst, pgname, pgtype, pgflags, pg))
3873 3881 goto out;
3874 3882
3875 3883 if (scf_value_set_astring(v, str) != SCF_SUCCESS)
3876 3884 goto out;
3877 3885
3878 3886 for (;;) {
3879 3887 if (scf_transaction_start(t, pg) != 0)
3880 3888 goto out;
3881 3889
3882 3890 if (tx_set_value(t, e, pname, SCF_TYPE_ASTRING, v) != 0)
3883 3891 goto out;
3884 3892
3885 3893 if ((r = scf_transaction_commit(t)) == 1)
3886 3894 break;
3887 3895
3888 3896 if (r == -1)
3889 3897 goto out;
3890 3898
3891 3899 scf_transaction_reset(t);
3892 3900 if (scf_pg_update(pg) == -1)
3893 3901 goto out;
3894 3902 }
3895 3903 ret = 0;
3896 3904
3897 3905 out:
3898 3906 scf_transaction_destroy(t);
3899 3907 scf_entry_destroy(e);
3900 3908 scf_value_destroy(v);
3901 3909 scf_pg_destroy(pg);
3902 3910
3903 3911 return (ret);
3904 3912 }
3905 3913
3906 3914 int
3907 3915 restarter_inst_set_aux_fmri(scf_instance_t *inst)
3908 3916 {
3909 3917 scf_handle_t *h;
3910 3918 scf_propertygroup_t *pg;
3911 3919 scf_property_t *prop;
3912 3920 scf_value_t *val;
3913 3921 char *aux_fmri;
3914 3922 size_t size = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
3915 3923 int ret = 1;
3916 3924
3917 3925 if ((aux_fmri = malloc(size)) == NULL)
3918 3926 return (1);
3919 3927
3920 3928 h = scf_instance_handle(inst);
3921 3929
3922 3930 pg = scf_pg_create(h);
3923 3931 prop = scf_property_create(h);
3924 3932 val = scf_value_create(h);
3925 3933 if (pg == NULL || prop == NULL || val == NULL)
3926 3934 goto out;
3927 3935
3928 3936 /*
3929 3937 * Get auxiliary_fmri value from restarter_actions pg
3930 3938 */
3931 3939 if (instance_get_or_add_pg(inst, SCF_PG_RESTARTER_ACTIONS,
3932 3940 SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS,
3933 3941 pg) != SCF_SUCCESS)
3934 3942 goto out;
3935 3943
3936 3944 if (get_astring_val(pg, SCF_PROPERTY_AUX_FMRI, aux_fmri, size,
3937 3945 prop, val) != SCF_SUCCESS)
3938 3946 goto out;
3939 3947
3940 3948 /*
3941 3949 * Populate restarter/auxiliary_fmri with the obtained fmri.
3942 3950 */
3943 3951 ret = restarter_inst_set_astring_prop(inst, SCF_PG_RESTARTER,
3944 3952 SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS,
3945 3953 SCF_PROPERTY_AUX_FMRI, aux_fmri);
3946 3954
3947 3955 out:
3948 3956 free(aux_fmri);
3949 3957 scf_value_destroy(val);
3950 3958 scf_property_destroy(prop);
3951 3959 scf_pg_destroy(pg);
3952 3960 return (ret);
3953 3961 }
3954 3962
3955 3963 int
3956 3964 restarter_inst_reset_aux_fmri(scf_instance_t *inst)
3957 3965 {
3958 3966 return (scf_instance_delete_prop(inst,
3959 3967 SCF_PG_RESTARTER, SCF_PROPERTY_AUX_FMRI));
3960 3968 }
3961 3969
3962 3970 int
3963 3971 restarter_inst_reset_ractions_aux_fmri(scf_instance_t *inst)
3964 3972 {
3965 3973 return (scf_instance_delete_prop(inst,
3966 3974 SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_AUX_FMRI));
3967 3975 }
↓ open down ↓ |
605 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX