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