Print this page
12721 would like svcadm disable -c

*** 22,32 **** * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* ! * Copyright 2015, Joyent, Inc. All rights reserved. */ /* * svcadm - request adminstrative actions for service instances */ --- 22,32 ---- * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* ! * Copyright 2020, Joyent, Inc. All rights reserved. */ /* * svcadm - request adminstrative actions for service instances */
*** 80,89 **** --- 80,102 ---- boolean_t active; char str[1]; }; + /* + * Callback data for enable/disable. + */ + #define SET_ENABLED 0x1 + #define SET_TEMPORARY 0x2 + #define SET_RECURSIVE 0x4 + + typedef struct { + char ed_comment[SCF_COMMENT_MAX_LENGTH]; + int ed_flags; + } enable_data_t; + + scf_handle_t *h; ssize_t max_scf_fmri_sz; static const char *emsg_permission_denied; static const char *emsg_nomem; static const char *emsg_create_pg_perm_denied;
*** 144,154 **** usage() { (void) fprintf(stderr, gettext( "Usage: %1$s [-S <state>] [-v] [-Z | -z zone] [cmd [args ... ]]\n\n" "\t%1$s enable [-rst] [<service> ...]\t- enable and online service(s)\n" ! "\t%1$s disable [-st] [<service> ...]\t- disable and offline " "service(s)\n" "\t%1$s restart [-d] [<service> ...]\t- restart specified service(s)\n" "\t%1$s refresh [<service> ...]\t\t- re-read service configuration\n" "\t%1$s mark [-It] <state> [<service> ...] - set maintenance state\n" "\t%1$s clear [<service> ...]\t\t- clear maintenance state\n" --- 157,167 ---- usage() { (void) fprintf(stderr, gettext( "Usage: %1$s [-S <state>] [-v] [-Z | -z zone] [cmd [args ... ]]\n\n" "\t%1$s enable [-rst] [<service> ...]\t- enable and online service(s)\n" ! "\t%1$s disable [-c comment] [-st] [<service> ...] - disable " "service(s)\n" "\t%1$s restart [-d] [<service> ...]\t- restart specified service(s)\n" "\t%1$s refresh [<service> ...]\t\t- re-read service configuration\n" "\t%1$s mark [-It] <state> [<service> ...] - set maintenance state\n" "\t%1$s clear [<service> ...]\t\t- clear maintenance state\n"
*** 565,588 **** out: scf_pg_destroy(pg); return (ret); } /* ! * Enable or disable inst, per enable. If temp is true, set ! * general_ovr/enabled. Otherwise set general/enabled and delete ! * general_ovr/enabled if it exists (order is important here: we don't want the ! * enabled status to glitch). */ static void ! set_inst_enabled(const char *fmri, scf_instance_t *inst, boolean_t temp, ! boolean_t enable) { scf_propertygroup_t *pg; uint8_t b; const char *pgname = NULL; /* For emsg_pg_perm_denied */ - int r; pg = scf_pg_create(h); if (pg == NULL) scfdie(); --- 578,731 ---- out: scf_pg_destroy(pg); return (ret); } + static int + delete_prop(const char *fmri, scf_instance_t *inst, const char *pgname, + const char *propname) + { + int r = scf_instance_delete_prop(inst, pgname, propname); + + switch (r) { + case 0: + break; + + case ECANCELED: + uu_warn(emsg_no_service, fmri); + break; + + case EACCES: + uu_warn(gettext("Could not delete %s/%s " + "property of %s: backend access denied.\n"), + pgname, propname, fmri); + break; + + case EROFS: + uu_warn(gettext("Could not delete %s/%s " + "property of %s: backend is read-only.\n"), + pgname, propname, fmri); + break; + + default: + bad_error("scf_instance_delete_prop", r); + } + + return (r); + } + /* ! * Returns 0, EPERM, or EROFS. */ + static int + set_enabled_props(scf_propertygroup_t *pg, enable_data_t *ed) + { + scf_transaction_entry_t *ent1; + scf_transaction_entry_t *ent2; + scf_transaction_t *tx; + scf_value_t *v2; + scf_value_t *v1; + int ret = 0, r; + + if ((tx = scf_transaction_create(h)) == NULL || + (ent1 = scf_entry_create(h)) == NULL || + (ent2 = scf_entry_create(h)) == NULL || + (v1 = scf_value_create(h)) == NULL || + (v2 = scf_value_create(h)) == NULL) + scfdie(); + + for (;;) { + if (scf_transaction_start(tx, pg) == -1) { + switch (scf_error()) { + case SCF_ERROR_PERMISSION_DENIED: + ret = EPERM; + goto out; + + case SCF_ERROR_BACKEND_READONLY: + ret = EROFS; + goto out; + + default: + scfdie(); + } + } + + if (scf_transaction_property_change_type(tx, ent1, + SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN) != 0) { + if (scf_error() != SCF_ERROR_NOT_FOUND) + scfdie(); + + if (scf_transaction_property_new(tx, ent1, + SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN) != 0) + scfdie(); + } + + scf_value_set_boolean(v1, !!(ed->ed_flags & SET_ENABLED)); + + r = scf_entry_add_value(ent1, v1); + assert(r == 0); + + if (scf_transaction_property_change_type(tx, ent2, + SCF_PROPERTY_COMMENT, SCF_TYPE_ASTRING) != 0) { + if (scf_error() != SCF_ERROR_NOT_FOUND) + scfdie(); + + if (scf_transaction_property_new(tx, ent2, + SCF_PROPERTY_COMMENT, SCF_TYPE_ASTRING) != 0) + scfdie(); + } + + if (scf_value_set_astring(v2, ed->ed_comment) != SCF_SUCCESS) + scfdie(); + + if (scf_entry_add_value(ent2, v2) != SCF_SUCCESS) + scfdie(); + + r = scf_transaction_commit(tx); + if (r == 1) + break; + + scf_transaction_reset(tx); + + if (r != 0) { + switch (scf_error()) { + case SCF_ERROR_PERMISSION_DENIED: + ret = EPERM; + goto out; + + case SCF_ERROR_BACKEND_READONLY: + ret = EROFS; + goto out; + + default: + scfdie(); + } + } + + if (scf_pg_update(pg) == -1) + scfdie(); + } + + out: + scf_transaction_destroy(tx); + scf_entry_destroy(ent1); + scf_entry_destroy(ent2); + scf_value_destroy(v1); + scf_value_destroy(v2); + return (ret); + } + + /* + * Enable or disable an instance. SET_TEMPORARY modifications apply to + * general_ovr/ property group. + */ static void ! set_inst_enabled(const char *fmri, scf_instance_t *inst, enable_data_t *ed) { scf_propertygroup_t *pg; uint8_t b; const char *pgname = NULL; /* For emsg_pg_perm_denied */ pg = scf_pg_create(h); if (pg == NULL) scfdie();
*** 623,640 **** assert(0); abort(); } } ! if (temp) { ! /* Set general_ovr/enabled */ pgname = SCF_PG_GENERAL_OVR; if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_OVR_TYPE, SCF_PG_GENERAL_OVR_FLAGS, pg) != 0) goto eperm; ! switch (set_bool_prop(pg, SCF_PROPERTY_ENABLED, enable)) { case 0: break; case EPERM: goto eperm; --- 766,782 ---- assert(0); abort(); } } ! if (ed->ed_flags & SET_TEMPORARY) { pgname = SCF_PG_GENERAL_OVR; if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_OVR_TYPE, SCF_PG_GENERAL_OVR_FLAGS, pg) != 0) goto eperm; ! switch (set_enabled_props(pg, ed)) { case 0: break; case EPERM: goto eperm;
*** 654,664 **** assert(0); abort(); } if (verbose) ! (void) printf(enable ? gettext("%s temporarily enabled.\n") : gettext("%s temporarily disabled.\n"), fmri); } else { again: /* --- 796,806 ---- assert(0); abort(); } if (verbose) ! (void) printf((ed->ed_flags & SET_ENABLED) ? gettext("%s temporarily enabled.\n") : gettext("%s temporarily disabled.\n"), fmri); } else { again: /*
*** 669,679 **** */ if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_TYPE, SCF_PG_GENERAL_FLAGS, pg) != 0) goto eperm; ! switch (set_bool_prop(pg, SCF_PROPERTY_ENABLED, enable)) { case 0: break; case EPERM: goto eperm; --- 811,821 ---- */ if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_TYPE, SCF_PG_GENERAL_FLAGS, pg) != 0) goto eperm; ! switch (set_enabled_props(pg, ed)) { case 0: break; case EPERM: goto eperm;
*** 683,693 **** * If general/enabled is already set the way we want, * proceed. */ switch (get_bool_prop(pg, SCF_PROPERTY_ENABLED, &b)) { case 0: ! if ((b != 0) == (enable != B_FALSE)) break; /* FALLTHROUGH */ case ENOENT: case EINVAL: --- 825,835 ---- * If general/enabled is already set the way we want, * proceed. */ switch (get_bool_prop(pg, SCF_PROPERTY_ENABLED, &b)) { case 0: ! if (!(b) == !(ed->ed_flags & SET_ENABLED)) break; /* FALLTHROUGH */ case ENOENT: case EINVAL:
*** 714,757 **** default: assert(0); abort(); } ! pgname = SCF_PG_GENERAL_OVR; ! r = scf_instance_delete_prop(inst, pgname, ! SCF_PROPERTY_ENABLED); ! switch (r) { case 0: break; - case ECANCELED: - uu_warn(emsg_no_service, fmri); - goto out; - case EPERM: goto eperm; ! case EACCES: ! uu_warn(gettext("Could not delete %s/%s " ! "property of %s: backend access denied.\n"), ! pgname, SCF_PROPERTY_ENABLED, fmri); goto out; ! case EROFS: ! uu_warn(gettext("Could not delete %s/%s " ! "property of %s: backend is read-only.\n"), ! pgname, SCF_PROPERTY_ENABLED, fmri); ! goto out; default: ! bad_error("scf_instance_delete_prop", r); } ! if (verbose) ! (void) printf(enable ? gettext("%s enabled.\n") : gettext("%s disabled.\n"), fmri); } scf_pg_destroy(pg); return; eperm: --- 856,895 ---- default: assert(0); abort(); } ! switch (delete_prop(fmri, inst, SCF_PG_GENERAL_OVR, ! SCF_PROPERTY_ENABLED)) { case 0: break; case EPERM: goto eperm; ! default: goto out; + } ! switch (delete_prop(fmri, inst, SCF_PG_GENERAL_OVR, ! SCF_PROPERTY_COMMENT)) { ! case 0: ! break; + case EPERM: + goto eperm; + default: ! goto out; } ! if (verbose) { ! (void) printf((ed->ed_flags & SET_ENABLED) ? ! gettext("%s enabled.\n") : gettext("%s disabled.\n"), fmri); } + } scf_pg_destroy(pg); return; eperm:
*** 1012,1022 **** * fmri must point to a writable max_scf_fmri_sz buffer. Returns EINVAL if fmri * is invalid, E2BIG if fmri identifies a service with multiple instances, ELOOP * on cycle detection, or 0 on success. */ static int ! enable_fmri_rec(char *fmri, boolean_t temp) { scf_instance_t *inst; scf_snapshot_t *snap; scf_propertygroup_t *pg; scf_property_t *prop; --- 1150,1160 ---- * fmri must point to a writable max_scf_fmri_sz buffer. Returns EINVAL if fmri * is invalid, E2BIG if fmri identifies a service with multiple instances, ELOOP * on cycle detection, or 0 on success. */ static int ! enable_fmri_rec(char *fmri, enable_data_t *ed) { scf_instance_t *inst; scf_snapshot_t *snap; scf_propertygroup_t *pg; scf_property_t *prop;
*** 1066,1076 **** default: he->active = B_FALSE; return (0); } ! set_inst_enabled(fmri, inst, temp, B_TRUE); if ((snap = scf_snapshot_create(h)) == NULL || (pg = scf_pg_create(h)) == NULL || (prop = scf_property_create(h)) == NULL || (v = scf_value_create(h)) == NULL || --- 1204,1214 ---- default: he->active = B_FALSE; return (0); } ! set_inst_enabled(fmri, inst, ed); if ((snap = scf_snapshot_create(h)) == NULL || (pg = scf_pg_create(h)) == NULL || (prop = scf_property_create(h)) == NULL || (v = scf_value_create(h)) == NULL ||
*** 1116,1126 **** "\"%s/%s\" is too long).\n"), fmri, SCF_PG_GENERAL, SCF_PROPERTY_RESTARTER); ret = 0; goto out; } else if (sz >= 0) { ! switch (enable_fmri_rec(buf, temp)) { case 0: break; case EINVAL: uu_warn(gettext("Restarter FMRI for \"%s\" is " --- 1254,1264 ---- "\"%s/%s\" is too long).\n"), fmri, SCF_PG_GENERAL, SCF_PROPERTY_RESTARTER); ret = 0; goto out; } else if (sz >= 0) { ! switch (enable_fmri_rec(buf, ed)) { case 0: break; case EINVAL: uu_warn(gettext("Restarter FMRI for \"%s\" is "
*** 1267,1277 **** if (scf_value_get_astring(v, buf, max_scf_fmri_sz) == -1) scfdie(); ! switch (enable_fmri_rec(buf, temp)) { case 0: break; case EINVAL: uu_warn(gettext("\"%s\" dependency of \"%s\" " --- 1405,1415 ---- if (scf_value_get_astring(v, buf, max_scf_fmri_sz) == -1) scfdie(); ! switch (enable_fmri_rec(buf, ed)) { case 0: break; case EINVAL: uu_warn(gettext("\"%s\" dependency of \"%s\" "
*** 1666,1686 **** scf_pg_destroy(pg); scf_instance_destroy(inst); } - /* - * Flags to control enable and disable actions. - */ - #define SET_ENABLED 0x1 - #define SET_TEMPORARY 0x2 - #define SET_RECURSIVE 0x4 - static int set_fmri_enabled(void *data, scf_walkinfo_t *wip) { ! int flags = (int)data; assert(wip->inst != NULL); assert(wip->pg == NULL); if (svcsearch) { --- 1804,1817 ---- scf_pg_destroy(pg); scf_instance_destroy(inst); } static int set_fmri_enabled(void *data, scf_walkinfo_t *wip) { ! enable_data_t *ed = data; assert(wip->inst != NULL); assert(wip->pg == NULL); if (svcsearch) {
*** 1690,1700 **** return (0); if (strcmp(state, svcstate) != 0) return (0); } ! if (flags & SET_RECURSIVE) { char *fmri_buf = malloc(max_scf_fmri_sz); if (fmri_buf == NULL) uu_die(emsg_nomem); visited = calloc(HT_BUCKETS, sizeof (*visited)); --- 1821,1831 ---- return (0); if (strcmp(state, svcstate) != 0) return (0); } ! if (ed->ed_flags & SET_RECURSIVE) { char *fmri_buf = malloc(max_scf_fmri_sz); if (fmri_buf == NULL) uu_die(emsg_nomem); visited = calloc(HT_BUCKETS, sizeof (*visited));
*** 1703,1713 **** /* scf_walk_fmri() guarantees that fmri isn't too long */ assert(strlen(wip->fmri) <= max_scf_fmri_sz); (void) strlcpy(fmri_buf, wip->fmri, max_scf_fmri_sz); ! switch (enable_fmri_rec(fmri_buf, (flags & SET_TEMPORARY))) { case E2BIG: uu_warn(gettext("operation on service %s is ambiguous; " "instance specification needed.\n"), fmri_buf); break; --- 1834,1844 ---- /* scf_walk_fmri() guarantees that fmri isn't too long */ assert(strlen(wip->fmri) <= max_scf_fmri_sz); (void) strlcpy(fmri_buf, wip->fmri, max_scf_fmri_sz); ! switch (enable_fmri_rec(fmri_buf, ed)) { case E2BIG: uu_warn(gettext("operation on service %s is ambiguous; " "instance specification needed.\n"), fmri_buf); break;
*** 1718,1729 **** free(visited); free(fmri_buf); } else { ! set_inst_enabled(wip->fmri, wip->inst, ! (flags & SET_TEMPORARY) != 0, (flags & SET_ENABLED) != 0); } return (0); } --- 1849,1859 ---- free(visited); free(fmri_buf); } else { ! set_inst_enabled(wip->fmri, wip->inst, ed); } return (0); }
*** 1970,1980 **** static void set_milestone(const char *fmri, boolean_t temporary) { scf_instance_t *inst; scf_propertygroup_t *pg; - int r; if (temporary) { set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS_OVR, SCF_PG_OPTIONS_OVR_TYPE, SCF_PG_OPTIONS_OVR_FLAGS, SCF_PROPERTY_MILESTONE, fmri); --- 2100,2109 ----
*** 1996,2042 **** */ set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS, SCF_PG_OPTIONS_TYPE, SCF_PG_OPTIONS_FLAGS, SCF_PROPERTY_MILESTONE, fmri); ! r = scf_instance_delete_prop(inst, SCF_PG_OPTIONS_OVR, ! SCF_PROPERTY_MILESTONE); ! switch (r) { ! case 0: ! break; ! ! case ECANCELED: ! uu_warn(emsg_no_service, fmri); exit_status = 1; - goto out; - case EPERM: - uu_warn(gettext("Could not delete %s/%s property of " - "%s: permission denied.\n"), SCF_PG_OPTIONS_OVR, - SCF_PROPERTY_MILESTONE, SCF_SERVICE_STARTD); - exit_status = 1; - goto out; - - case EACCES: - uu_warn(gettext("Could not delete %s/%s property of " - "%s: access denied.\n"), SCF_PG_OPTIONS_OVR, - SCF_PROPERTY_MILESTONE, SCF_SERVICE_STARTD); - exit_status = 1; - goto out; - - case EROFS: - uu_warn(gettext("Could not delete %s/%s property of " - "%s: backend read-only.\n"), SCF_PG_OPTIONS_OVR, - SCF_PROPERTY_MILESTONE, SCF_SERVICE_STARTD); - exit_status = 1; - goto out; - - default: - bad_error("scf_instance_delete_prop", r); - } - - out: scf_pg_destroy(pg); scf_instance_destroy(inst); } static char const *milestones[] = { --- 2125,2138 ---- */ set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS, SCF_PG_OPTIONS_TYPE, SCF_PG_OPTIONS_FLAGS, SCF_PROPERTY_MILESTONE, fmri); ! if (delete_prop(SCF_SERVICE_STARTD, inst, SCF_PG_OPTIONS_OVR, ! SCF_PROPERTY_MILESTONE) != 0) exit_status = 1; scf_pg_destroy(pg); scf_instance_destroy(inst); } static char const *milestones[] = {
*** 2313,2333 **** if (optind >= argc) usage(); if (strcmp(argv[optind], "enable") == 0) { ! int flags = SET_ENABLED; int wait = 0; int error = 0; ++optind; while ((o = getopt(argc, argv, "rst")) != -1) { if (o == 'r') ! flags |= SET_RECURSIVE; else if (o == 't') ! flags |= SET_TEMPORARY; else if (o == 's') wait = 1; else if (o == '?') usage(); else { --- 2409,2432 ---- if (optind >= argc) usage(); if (strcmp(argv[optind], "enable") == 0) { ! enable_data_t ed = { ! .ed_flags = SET_ENABLED, ! .ed_comment = "" ! }; int wait = 0; int error = 0; ++optind; while ((o = getopt(argc, argv, "rst")) != -1) { if (o == 'r') ! ed.ed_flags |= SET_RECURSIVE; else if (o == 't') ! ed.ed_flags |= SET_TEMPORARY; else if (o == 's') wait = 1; else if (o == '?') usage(); else {
*** 2349,2367 **** * invalid options, but not if an enable failed. We * squelch output the second time we walk fmris; we saw * the errors the first time. */ if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS, ! set_fmri_enabled, (void *)flags, &error, pr_warn)) != 0) { pr_warn(gettext("failed to iterate over " "instances: %s\n"), scf_strerror(err)); exit_status = UU_EXIT_FATAL; } else if (wait && exit_status == 0 && (err = scf_walk_fmri(h, argc, argv, WALK_FLAGS, ! wait_fmri_enabled, (void *)flags, &error, quiet)) != 0) { pr_warn(gettext("failed to iterate over " "instances: %s\n"), scf_strerror(err)); exit_status = UU_EXIT_FATAL; } --- 2448,2466 ---- * invalid options, but not if an enable failed. We * squelch output the second time we walk fmris; we saw * the errors the first time. */ if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS, ! set_fmri_enabled, &ed, &error, pr_warn)) != 0) { pr_warn(gettext("failed to iterate over " "instances: %s\n"), scf_strerror(err)); exit_status = UU_EXIT_FATAL; } else if (wait && exit_status == 0 && (err = scf_walk_fmri(h, argc, argv, WALK_FLAGS, ! wait_fmri_enabled, NULL, &error, quiet)) != 0) { pr_warn(gettext("failed to iterate over " "instances: %s\n"), scf_strerror(err)); exit_status = UU_EXIT_FATAL; }
*** 2368,2386 **** if (error > 0) exit_status = error; } else if (strcmp(argv[optind], "disable") == 0) { ! int flags = 0; int wait = 0; int error = 0; ++optind; ! while ((o = getopt(argc, argv, "st")) != -1) { ! if (o == 't') ! flags |= SET_TEMPORARY; else if (o == 's') wait = 1; else if (o == '?') usage(); else { --- 2467,2495 ---- if (error > 0) exit_status = error; } else if (strcmp(argv[optind], "disable") == 0) { ! enable_data_t ed = { ! .ed_flags = 0, ! .ed_comment = "" ! }; int wait = 0; int error = 0; ++optind; ! while ((o = getopt(argc, argv, "c:st")) != -1) { ! if (o == 'c') { ! if (strlcpy(ed.ed_comment, optarg, ! sizeof (ed.ed_comment)) >= ! sizeof (ed.ed_comment)) { ! uu_die(gettext("disable -c comment " ! "too long.\n")); ! } ! } else if (o == 't') ! ed.ed_flags |= SET_TEMPORARY; else if (o == 's') wait = 1; else if (o == '?') usage(); else {
*** 2402,2421 **** * invalid options, but not if a disable failed. We * squelch output the second time we walk fmris; we saw * the errors the first time. */ if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS, ! set_fmri_enabled, (void *)flags, &exit_status, pr_warn)) != 0) { pr_warn(gettext("failed to iterate over " "instances: %s\n"), scf_strerror(err)); exit_status = UU_EXIT_FATAL; } else if (wait && exit_status == 0 && (err = scf_walk_fmri(h, argc, argv, WALK_FLAGS, ! wait_fmri_disabled, (void *)flags, &error, quiet)) != 0) { pr_warn(gettext("failed to iterate over " "instances: %s\n"), scf_strerror(err)); exit_status = UU_EXIT_FATAL; } --- 2511,2530 ---- * invalid options, but not if a disable failed. We * squelch output the second time we walk fmris; we saw * the errors the first time. */ if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS, ! set_fmri_enabled, &ed, &exit_status, pr_warn)) != 0) { pr_warn(gettext("failed to iterate over " "instances: %s\n"), scf_strerror(err)); exit_status = UU_EXIT_FATAL; } else if (wait && exit_status == 0 && (err = scf_walk_fmri(h, argc, argv, WALK_FLAGS, ! wait_fmri_disabled, NULL, &error, quiet)) != 0) { pr_warn(gettext("failed to iterate over " "instances: %s\n"), scf_strerror(err)); exit_status = UU_EXIT_FATAL; }