diff options
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/cmd/zonecfg/zonecfg.c | 600 | ||||
-rw-r--r-- | usr/src/cmd/zonecfg/zonecfg.h | 54 | ||||
-rw-r--r-- | usr/src/cmd/zonecfg/zonecfg_grammar.y | 18 | ||||
-rw-r--r-- | usr/src/cmd/zonecfg/zonecfg_lex.l | 5 | ||||
-rw-r--r-- | usr/src/head/libzonecfg.h | 15 | ||||
-rw-r--r-- | usr/src/lib/libzonecfg/common/getzoneent.c | 29 | ||||
-rw-r--r-- | usr/src/lib/libzonecfg/common/libzonecfg.c | 500 | ||||
-rw-r--r-- | usr/src/lib/libzonecfg/spec/libzonecfg.spec | 26 |
8 files changed, 830 insertions, 417 deletions
diff --git a/usr/src/cmd/zonecfg/zonecfg.c b/usr/src/cmd/zonecfg/zonecfg.c index 46f389a768..69e931e1a9 100644 --- a/usr/src/cmd/zonecfg/zonecfg.c +++ b/usr/src/cmd/zonecfg/zonecfg.c @@ -143,6 +143,7 @@ static struct help helptab[] = { /* These *must* match the order of the RT_ define's from zonecfg.h */ static char *res_types[] = { "unknown", + "zonename", "zonepath", "autoboot", "pool", @@ -158,6 +159,7 @@ static char *res_types[] = { /* These *must* match the order of the PT_ define's from zonecfg.h */ static char *prop_types[] = { "unknown", + "zonename", "zonepath", "autoboot", "pool", @@ -221,19 +223,20 @@ static const char *add_cmds[] = { }; static const char *select_cmds[] = { - "select fs", - "select inherit-pkg-dir", - "select net", - "select device", - "select rctl", - "select attr", + "select fs ", + "select inherit-pkg-dir ", + "select net ", + "select device ", + "select rctl ", + "select attr ", NULL }; static const char *set_cmds[] = { - "set zonepath", - "set autoboot", - "set pool", + "set zonename=", + "set zonepath=", + "set autoboot=", + "set pool=", NULL }; @@ -314,7 +317,8 @@ static char *execname; static zone_dochandle_t handle; /* used all over the place */ -static char *zone; +static char zone[ZONENAME_MAX]; +static char revert_zone[ZONENAME_MAX]; /* set in modifying functions, checked in read_input() */ static bool need_to_commit = FALSE; @@ -344,9 +348,6 @@ static bool interactive_mode; /* set in main(), checked in multiple places */ static bool read_only_mode; -/* set in check_if_zone_already_exists(), checked in save_it() */ -static bool new_zone = FALSE; - static bool global_scope = TRUE; /* scope is outer/global or inner/resource */ static int resource_scope; /* should be in the RT_ list from zonecfg.h */ static int end_op = -1; /* operation on end is either add or modify */ @@ -849,6 +850,8 @@ usage(bool verbose, uint_t flags) (void) fprintf(fp, gettext("For resource type ... there are " "property types ...:\n")); (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), + pt_to_str(PT_ZONENAME)); + (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), pt_to_str(PT_ZONEPATH)); (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), pt_to_str(PT_AUTOBOOT)); @@ -942,6 +945,23 @@ initialize(bool handle_expected) return (Z_OK); } +static bool +state_atleast(zone_state_t state) +{ + zone_state_t state_num; + int err; + + if ((err = zone_get_state(zone, &state_num)) != Z_OK) { + /* all states are greater than "non-existent" */ + if (err == Z_NO_ZONE) + return (B_FALSE); + zerr(gettext("Unexpectedly failed to determine state " + "of zone %s: %s"), zone, zonecfg_strerror(err)); + exit(Z_ERR); + } + return (state_num >= state); +} + /* * short_usage() is for bad syntax: getopt() issues, too many arguments, etc. */ @@ -1023,10 +1043,13 @@ ask_yesno(bool default_answer, const char *question) return (-1); } for (;;) { - (void) printf("%s (%s)? ", question, - default_answer ? "[y]/n" : "y/[n]"); - if (fgets(line, sizeof (line), stdin) == NULL || - line[0] == '\n') + if (printf("%s (%s)? ", question, + default_answer ? "[y]/n" : "y/[n]") < 0) + return (-1); + if (fgets(line, sizeof (line), stdin) == NULL) + return (-1); + + if (line[0] == '\n') return (default_answer ? 1 : 0); if (tolower(line[0]) == 'y') return (1); @@ -1048,7 +1071,6 @@ static int check_if_zone_already_exists(bool force) { char line[ZONENAME_MAX + 128]; /* enough to ask a question */ - zone_state_t state_num; zone_dochandle_t tmphandle; int res, answer; @@ -1058,12 +1080,10 @@ check_if_zone_already_exists(bool force) } res = zonecfg_get_handle(zone, tmphandle); zonecfg_fini_handle(tmphandle); - if (res != Z_OK) { - new_zone = TRUE; + if (res != Z_OK) return (Z_OK); - } - if (zone_get_state(zone, &state_num) == Z_OK && - state_num >= ZONE_STATE_INSTALLED) { + + if (state_atleast(ZONE_STATE_INSTALLED)) { zerr(gettext("Zone %s already installed; %s not allowed."), zone, cmd_to_str(CMD_CREATE)); return (Z_ERR); @@ -1167,17 +1187,17 @@ create_func(cmd_t *cmd) zone_perror(execname, Z_NOMEM, TRUE); exit(Z_ERR); } - if ((err = zonecfg_get_handle(zone_template, tmphandle)) != Z_OK) { + if ((err = zonecfg_get_template_handle(zone_template, zone, + tmphandle)) != Z_OK) { zonecfg_fini_handle(tmphandle); zone_perror(zone_template, err, TRUE); return; } + + need_to_commit = TRUE; zonecfg_fini_handle(handle); handle = tmphandle; - if ((err = zonecfg_set_name(handle, zone)) == Z_OK) - need_to_commit = TRUE; - else - zone_perror(zone, err, TRUE); + got_handle = TRUE; } /* @@ -1456,7 +1476,6 @@ static void add_resource(cmd_t *cmd) { int type; - zone_state_t state_num; if ((type = cmd->cmd_res_type) == RT_UNKNOWN) { long_usage(CMD_ADD, TRUE); @@ -1468,8 +1487,7 @@ add_resource(cmd_t *cmd) bzero(&in_progress_fstab, sizeof (in_progress_fstab)); return; case RT_IPD: - if (zone_get_state(zone, &state_num) == Z_OK && - state_num >= ZONE_STATE_INSTALLED) { + if (state_atleast(ZONE_STATE_INSTALLED)) { zerr(gettext("Zone %s already installed; %s %s not " "allowed."), zone, cmd_to_str(CMD_ADD), rt_to_str(RT_IPD)); @@ -1722,13 +1740,20 @@ add_func(cmd_t *cmd) add_property(cmd); } +/* + * This routine has an unusual implementation, because it tries very + * hard to succeed in the face of a variety of failure modes. + * The most common and most vexing occurs when the index file and + * the /etc/zones/<zonename.xml> file are not both present. In + * this case, delete must eradicate as much of the zone state as is left + * so that the user can later create a new zone with the same name. + */ void delete_func(cmd_t *cmd) { int err, arg, answer; char line[ZONENAME_MAX + 128]; /* enough to ask a question */ bool force = FALSE; - zone_state_t state_num; optind = 0; while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?F")) != EOF) { @@ -1752,54 +1777,70 @@ delete_func(cmd_t *cmd) if (zone_is_read_only(CMD_DELETE)) return; - if (zone_get_state(zone, &state_num) == Z_OK && - state_num >= ZONE_STATE_INCOMPLETE) { - zerr(gettext("Zone %s not in %s state; %s not allowed."), - zone, zone_state_str(ZONE_STATE_CONFIGURED), - cmd_to_str(CMD_DELETE)); - saw_error = TRUE; - return; - } - - if (initialize(TRUE) != Z_OK) - return; - if (!force) { + /* + * Initialize sets up the global called "handle" and warns the + * user if the zone is not configured. In force mode, we don't + * trust that evaluation, and hence skip it. (We don't need the + * handle to be loaded anyway, since zonecfg_destroy is done by + * zonename). However, we also have to take care to emulate the + * messages spit out by initialize; see below. + */ + if (initialize(TRUE) != Z_OK) + return; + (void) snprintf(line, sizeof (line), gettext("Are you sure you want to delete zone %s"), zone); if ((answer = ask_yesno(FALSE, line)) == -1) { - zerr(gettext("Input not from " - "terminal and -F not specified:\n%s command " - "ignored, exiting."), cmd_to_str(CMD_DELETE)); + zerr(gettext("Input not from terminal and -F not " + "specified:\n%s command ignored, exiting."), + cmd_to_str(CMD_DELETE)); exit(Z_ERR); } if (answer != 1) return; } - if ((err = zonecfg_delete_index(zone)) != Z_OK) { - zone_perror(zone, err, TRUE); - return; + if ((err = zonecfg_destroy(zone, force)) != Z_OK) { + if ((err == Z_BAD_ZONE_STATE) && !force) { + zerr(gettext("Zone %s not in %s state; %s not " + "allowed. Use -F to force %s."), + zone, zone_state_str(ZONE_STATE_CONFIGURED), + cmd_to_str(CMD_DELETE), cmd_to_str(CMD_DELETE)); + } else { + zone_perror(zone, err, TRUE); + } } - need_to_commit = FALSE; - if ((err = zonecfg_destroy(zone)) != Z_OK) - zone_perror(zone, err, TRUE); + + /* + * Emulate initialize's messaging; if there wasn't a valid handle to + * begin with, then user had typed delete (or delete -F) multiple + * times. So we emit a message. + * + * We only do this in the 'force' case because normally, initialize() + * takes care of this for us. + */ + if (force && zonecfg_check_handle(handle) != Z_OK && interactive_mode) + (void) printf(gettext("Use '%s' to begin " + "configuring a new zone.\n"), cmd_to_str(CMD_CREATE)); /* * Time for a new handle: finish the old one off first * then get a new one properly to avoid leaks. */ - zonecfg_fini_handle(handle); - if ((handle = zonecfg_init_handle()) == NULL) { - zone_perror(execname, Z_NOMEM, TRUE); - exit(Z_ERR); - } - if ((err = zonecfg_get_handle(zone, handle)) != Z_OK) { - /* If there was no zone before, that's OK */ - if (err != Z_NO_ZONE) - zone_perror(zone, err, TRUE); - got_handle = FALSE; + if (got_handle) { + zonecfg_fini_handle(handle); + if ((handle = zonecfg_init_handle()) == NULL) { + zone_perror(execname, Z_NOMEM, TRUE); + exit(Z_ERR); + } + if ((err = zonecfg_get_handle(zone, handle)) != Z_OK) { + /* If there was no zone before, that's OK */ + if (err != Z_NO_ZONE) + zone_perror(zone, err, TRUE); + got_handle = FALSE; + } } } @@ -2045,7 +2086,6 @@ remove_resource(cmd_t *cmd) struct zone_devtab devtab; struct zone_attrtab attrtab; struct zone_rctltab rctltab; - zone_state_t state_num; if ((type = cmd->cmd_res_type) == RT_UNKNOWN) { long_usage(CMD_REMOVE, TRUE); @@ -2068,8 +2108,7 @@ remove_resource(cmd_t *cmd) zonecfg_free_fs_option_list(fstab.zone_fs_options); return; case RT_IPD: - if (zone_get_state(zone, &state_num) == Z_OK && - state_num >= ZONE_STATE_INSTALLED) { + if (state_atleast(ZONE_STATE_INSTALLED)) { zerr(gettext("Zone %s already installed; %s %s not " "allowed."), zone, cmd_to_str(CMD_REMOVE), rt_to_str(RT_IPD)); @@ -2278,7 +2317,6 @@ void select_func(cmd_t *cmd) { int type, err; - zone_state_t state_num; if (zone_is_read_only(CMD_SELECT)) return; @@ -2312,8 +2350,7 @@ select_func(cmd_t *cmd) sizeof (struct zone_fstab)); return; case RT_IPD: - if (zone_get_state(zone, &state_num) == Z_OK && - state_num >= ZONE_STATE_INCOMPLETE) { + if (state_atleast(ZONE_STATE_INCOMPLETE)) { zerr(gettext("Zone %s not in %s state; %s %s not " "allowed."), zone, zone_state_str(ZONE_STATE_CONFIGURED), @@ -2473,7 +2510,7 @@ valid_fs_type(const char *type) */ if (strchr(type, '/') != NULL || type[0] == '\0' || strcmp(type, ".") == 0 || strcmp(type, "..") == 0) - return (B_FALSE); + return (B_FALSE); /* * More detailed verification happens later by zoneadm(1m). */ @@ -2486,7 +2523,6 @@ set_func(cmd_t *cmd) char *prop_id; int err, res_type, prop_type; property_value_ptr_t pp; - zone_state_t state_num; boolean_t autoboot; if (zone_is_read_only(CMD_SET)) @@ -2496,7 +2532,9 @@ set_func(cmd_t *cmd) prop_type = cmd->cmd_prop_name[0]; if (global_scope) { - if (prop_type == PT_ZONEPATH) { + if (prop_type == PT_ZONENAME) { + res_type = RT_ZONENAME; + } else if (prop_type == PT_ZONEPATH) { res_type = RT_ZONEPATH; } else if (prop_type == PT_AUTOBOOT) { res_type = RT_AUTOBOOT; @@ -2533,13 +2571,35 @@ set_func(cmd_t *cmd) return; } + /* + * Special case: the user can change the zone name prior to 'create'; + * if the zone already exists, we fall through letting initialize() + * and the rest of the logic run. + */ + if (res_type == RT_ZONENAME && got_handle == FALSE && + !state_atleast(ZONE_STATE_CONFIGURED)) { + (void) strlcpy(zone, prop_id, sizeof (zone)); + return; + } + if (initialize(TRUE) != Z_OK) return; switch (res_type) { + case RT_ZONENAME: + if ((err = zonecfg_set_name(handle, prop_id)) != Z_OK) { + /* + * Use prop_id instead of 'zone' here, since we're + * reporting a problem about the *new* zonename. + */ + zone_perror(prop_id, err, TRUE); + } else { + need_to_commit = TRUE; + (void) strlcpy(zone, prop_id, sizeof (zone)); + } + return; case RT_ZONEPATH: - if (zone_get_state(zone, &state_num) == Z_OK && - state_num >= ZONE_STATE_INSTALLED) { + if (state_atleast(ZONE_STATE_INSTALLED)) { zerr(gettext("Zone %s already installed; %s %s not " "allowed."), zone, cmd_to_str(CMD_SET), rt_to_str(RT_ZONEPATH)); @@ -2759,8 +2819,21 @@ output_prop(FILE *fp, int pnum, char *pval, bool print_notspec) (void) fprintf(fp, "\t%s: %s\n", pt_to_str(pnum), qstr); free(qstr); } else if (print_notspec) - (void) fprintf(fp, "\t%s %s\n", pt_to_str(pnum), - gettext("not specified")); + (void) fprintf(fp, gettext("\t%s not specified\n"), + pt_to_str(pnum)); +} + +static void +info_zonename(zone_dochandle_t handle, FILE *fp) +{ + char zonename[ZONENAME_MAX]; + + if (zonecfg_get_name(handle, zonename, sizeof (zonename)) == Z_OK) + (void) fprintf(fp, "%s: %s\n", pt_to_str(PT_ZONENAME), + zonename); + else + (void) fprintf(fp, gettext("%s not specified\n"), + pt_to_str(PT_ZONENAME)); } static void @@ -2771,9 +2844,10 @@ info_zonepath(zone_dochandle_t handle, FILE *fp) if (zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath)) == Z_OK) (void) fprintf(fp, "%s: %s\n", pt_to_str(PT_ZONEPATH), zonepath); - else - (void) fprintf(fp, "%s %s\n", pt_to_str(PT_ZONEPATH), - gettext("not specified")); + else { + (void) fprintf(fp, gettext("%s not specified\n"), + pt_to_str(PT_ZONEPATH)); + } } static void @@ -3124,6 +3198,7 @@ info_func(cmd_t *cmd) switch (cmd->cmd_res_type) { case RT_UNKNOWN: + info_zonename(handle, fp); info_zonepath(handle, fp); info_autoboot(handle, fp); info_pool(handle, fp); @@ -3134,6 +3209,9 @@ info_func(cmd_t *cmd) info_rctl(handle, fp, cmd); info_attr(handle, fp, cmd); break; + case RT_ZONENAME: + info_zonename(handle, fp); + break; case RT_ZONEPATH: info_zonepath(handle, fp); break; @@ -3171,22 +3249,20 @@ cleanup: (void) pclose(fp); } -static int -save_it(char *zonepath) +/* + * Helper function for verify-- checks that a required string property + * exists. + */ +static void +check_reqd_prop(char *attr, int rt, int pt, int *ret_val) { - int err; - - if (new_zone) { - err = zonecfg_add_index(zone, zonepath); - if (err != Z_OK) { - zone_perror(zone, err, TRUE); - return (err); - } - new_zone = FALSE; + if (strlen(attr) == 0) { + zerr(gettext("%s: %s not specified"), rt_to_str(rt), + pt_to_str(pt)); + saw_error = TRUE; + if (*ret_val == Z_OK) + *ret_val = Z_REQD_PROPERTY_MISSING; } - if ((err = zonecfg_save(handle)) == Z_OK) - need_to_commit = FALSE; - return (err); } /* @@ -3236,13 +3312,12 @@ verify_func(cmd_t *cmd) return; if (zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath)) != Z_OK) { - zerr("%s %s", pt_to_str(PT_ZONEPATH), gettext("not specified")); + zerr(gettext("%s not specified"), pt_to_str(PT_ZONEPATH)); ret_val = Z_REQD_RESOURCE_MISSING; saw_error = TRUE; } if (strlen(zonepath) == 0) { - zerr("%s %s", pt_to_str(PT_ZONEPATH), - gettext("cannot be empty.")); + zerr(gettext("%s cannot be empty."), pt_to_str(PT_ZONEPATH)); ret_val = Z_REQD_RESOURCE_MISSING; saw_error = TRUE; } @@ -3252,13 +3327,7 @@ verify_func(cmd_t *cmd) return; } while (zonecfg_getipdent(handle, &fstab) == Z_OK) { - if (strlen(fstab.zone_fs_dir) == 0) { - zerr("%s: %s %s", rt_to_str(RT_IPD), pt_to_str(PT_DIR), - gettext("not specified")); - saw_error = TRUE; - if (ret_val == Z_OK) - ret_val = Z_REQD_PROPERTY_MISSING; - } + check_reqd_prop(fstab.zone_fs_dir, RT_IPD, PT_DIR, &ret_val); } (void) zonecfg_endipdent(handle); @@ -3267,27 +3336,11 @@ verify_func(cmd_t *cmd) return; } while (zonecfg_getfsent(handle, &fstab) == Z_OK) { - if (strlen(fstab.zone_fs_dir) == 0) { - zerr("%s: %s %s", rt_to_str(RT_FS), pt_to_str(PT_DIR), - gettext("not specified")); - saw_error = TRUE; - if (ret_val == Z_OK) - ret_val = Z_REQD_PROPERTY_MISSING; - } - if (strlen(fstab.zone_fs_special) == 0) { - zerr("%s: %s %s", rt_to_str(RT_FS), - pt_to_str(PT_SPECIAL), gettext("not specified")); - saw_error = TRUE; - if (ret_val == Z_OK) - ret_val = Z_REQD_PROPERTY_MISSING; - } - if (strlen(fstab.zone_fs_type) == 0) { - zerr("%s: %s %s", rt_to_str(RT_FS), pt_to_str(PT_TYPE), - gettext("not specified")); - saw_error = TRUE; - if (ret_val == Z_OK) - ret_val = Z_REQD_PROPERTY_MISSING; - } + check_reqd_prop(fstab.zone_fs_dir, RT_FS, PT_DIR, &ret_val); + check_reqd_prop(fstab.zone_fs_special, RT_FS, PT_SPECIAL, + &ret_val); + check_reqd_prop(fstab.zone_fs_type, RT_FS, PT_TYPE, &ret_val); + zonecfg_free_fs_option_list(fstab.zone_fs_options); } (void) zonecfg_endfsent(handle); @@ -3297,20 +3350,10 @@ verify_func(cmd_t *cmd) return; } while (zonecfg_getnwifent(handle, &nwiftab) == Z_OK) { - if (strlen(nwiftab.zone_nwif_address) == 0) { - zerr("%s: %s %s", rt_to_str(RT_NET), - pt_to_str(PT_ADDRESS), gettext("not specified")); - saw_error = TRUE; - if (ret_val == Z_OK) - ret_val = Z_REQD_PROPERTY_MISSING; - } - if (strlen(nwiftab.zone_nwif_physical) == 0) { - zerr("%s: %s %s", rt_to_str(RT_NET), - pt_to_str(PT_PHYSICAL), gettext("not specified")); - saw_error = TRUE; - if (ret_val == Z_OK) - ret_val = Z_REQD_PROPERTY_MISSING; - } + check_reqd_prop(nwiftab.zone_nwif_address, RT_NET, + PT_ADDRESS, &ret_val); + check_reqd_prop(nwiftab.zone_nwif_physical, RT_NET, + PT_PHYSICAL, &ret_val); } (void) zonecfg_endnwifent(handle); @@ -3319,13 +3362,9 @@ verify_func(cmd_t *cmd) return; } while (zonecfg_getrctlent(handle, &rctltab) == Z_OK) { - if (strlen(rctltab.zone_rctl_name) == 0) { - zerr("%s: %s %s", rt_to_str(RT_RCTL), - pt_to_str(PT_NAME), gettext("not specified")); - saw_error = TRUE; - if (ret_val == Z_OK) - ret_val = Z_REQD_PROPERTY_MISSING; - } + check_reqd_prop(rctltab.zone_rctl_name, RT_RCTL, PT_NAME, + &ret_val); + if (rctltab.zone_rctl_valptr == NULL) { zerr(gettext("%s: no %s specified"), rt_to_str(RT_RCTL), pt_to_str(PT_VALUE)); @@ -3343,27 +3382,12 @@ verify_func(cmd_t *cmd) return; } while (zonecfg_getattrent(handle, &attrtab) == Z_OK) { - if (strlen(attrtab.zone_attr_name) == 0) { - zerr("%s: %s %s", rt_to_str(RT_ATTR), - pt_to_str(PT_NAME), gettext("not specified")); - saw_error = TRUE; - if (ret_val == Z_OK) - ret_val = Z_REQD_PROPERTY_MISSING; - } - if (strlen(attrtab.zone_attr_type) == 0) { - zerr("%s: %s %s", rt_to_str(RT_ATTR), - pt_to_str(PT_TYPE), gettext("not specified")); - saw_error = TRUE; - if (ret_val == Z_OK) - ret_val = Z_REQD_PROPERTY_MISSING; - } - if (strlen(attrtab.zone_attr_value) == 0) { - zerr("%s: %s %s", rt_to_str(RT_ATTR), - pt_to_str(PT_VALUE), gettext("not specified")); - saw_error = TRUE; - if (ret_val == Z_OK) - ret_val = Z_REQD_PROPERTY_MISSING; - } + check_reqd_prop(attrtab.zone_attr_name, RT_ATTR, PT_NAME, + &ret_val); + check_reqd_prop(attrtab.zone_attr_type, RT_ATTR, PT_TYPE, + &ret_val); + check_reqd_prop(attrtab.zone_attr_value, RT_ATTR, PT_VALUE, + &ret_val); } (void) zonecfg_endattrent(handle); @@ -3375,10 +3399,15 @@ verify_func(cmd_t *cmd) } if (save) { - if (ret_val == Z_OK) - ret_val = save_it(zonepath); - else - zerr("zone %s %s", zone, gettext("failed to verify")); + if (ret_val == Z_OK) { + if ((ret_val = zonecfg_save(handle)) == Z_OK) { + need_to_commit = FALSE; + (void) strlcpy(revert_zone, zone, + sizeof (revert_zone)); + } + } else { + zerr(gettext("Zone %s failed to verify"), zone); + } } if (ret_val != Z_OK) zone_perror(zone, ret_val, TRUE); @@ -3486,6 +3515,21 @@ validate_attr_type_val(struct zone_attrtab *attrtab) return (Z_ERR); } +/* + * Helper function for end_func-- checks the existence of a given property + * and emits a message if not specified. + */ +static int +end_check_reqd(char *attr, int pt, bool *validation_failed) +{ + if (strlen(attr) == 0) { + *validation_failed = TRUE; + zerr(gettext("%s not specified"), pt_to_str(pt)); + return (Z_ERR); + } + return (Z_OK); +} + void end_func(cmd_t *cmd) { @@ -3525,36 +3569,35 @@ end_func(cmd_t *cmd) switch (resource_scope) { case RT_FS: /* First make sure everything was filled in. */ - if (strlen(in_progress_fstab.zone_fs_dir) == 0) { - zerr("dir %s", gettext("not specified")); - saw_error = TRUE; - validation_failed = TRUE; - } else if (in_progress_fstab.zone_fs_dir[0] != '/') { - zerr("dir %s %s", in_progress_fstab.zone_fs_dir, - gettext("is not an absolute path.")); - saw_error = TRUE; - validation_failed = TRUE; - } - if (strlen(in_progress_fstab.zone_fs_special) == 0) { - zerr("special %s", gettext("not specified")); - saw_error = TRUE; - validation_failed = TRUE; + if (end_check_reqd(in_progress_fstab.zone_fs_dir, + PT_DIR, &validation_failed) == Z_OK) { + if (in_progress_fstab.zone_fs_dir[0] != '/') { + zerr(gettext("%s %s is not an absolute path."), + pt_to_str(PT_DIR), + in_progress_fstab.zone_fs_dir); + validation_failed = TRUE; + } } + + (void) end_check_reqd(in_progress_fstab.zone_fs_special, + PT_SPECIAL, &validation_failed); + if (in_progress_fstab.zone_fs_raw[0] != '\0' && in_progress_fstab.zone_fs_raw[0] != '/') { - zerr("raw device %s %s", - in_progress_fstab.zone_fs_raw, - gettext("is not an absolute path.")); - saw_error = TRUE; + zerr(gettext("%s %s is not an absolute path."), + pt_to_str(PT_RAW), + in_progress_fstab.zone_fs_raw); validation_failed = TRUE; } - if (strlen(in_progress_fstab.zone_fs_type) == 0) { - zerr("type %s", gettext("not specified")); + + (void) end_check_reqd(in_progress_fstab.zone_fs_type, PT_TYPE, + &validation_failed); + + if (validation_failed) { saw_error = TRUE; - validation_failed = TRUE; - } - if (validation_failed) return; + } + if (end_op == CMD_ADD) { /* Make sure there isn't already one like this. */ bzero(&tmp_fstab, sizeof (tmp_fstab)); @@ -3580,20 +3623,23 @@ end_func(cmd_t *cmd) zonecfg_free_fs_option_list(in_progress_fstab.zone_fs_options); in_progress_fstab.zone_fs_options = NULL; break; + case RT_IPD: /* First make sure everything was filled in. */ - if (strlen(in_progress_ipdtab.zone_fs_dir) == 0) { - zerr("dir %s", gettext("not specified")); - saw_error = TRUE; - validation_failed = TRUE; - } else if (in_progress_ipdtab.zone_fs_dir[0] != '/') { - zerr("dir %s %s", in_progress_ipdtab.zone_fs_dir, - gettext("is not an absolute path.")); - saw_error = TRUE; - validation_failed = TRUE; + if (end_check_reqd(in_progress_ipdtab.zone_fs_dir, PT_DIR, + &validation_failed) == Z_OK) { + if (in_progress_ipdtab.zone_fs_dir[0] != '/') { + zerr(gettext("%s %s is not an absolute path."), + pt_to_str(PT_DIR), + in_progress_ipdtab.zone_fs_dir); + validation_failed = TRUE; + } } - if (validation_failed) + if (validation_failed) { + saw_error = TRUE; return; + } + if (end_op == CMD_ADD) { /* Make sure there isn't already one like this. */ bzero(&tmp_fstab, sizeof (tmp_fstab)); @@ -3617,18 +3663,16 @@ end_func(cmd_t *cmd) break; case RT_NET: /* First make sure everything was filled in. */ - if (strlen(in_progress_nwiftab.zone_nwif_physical) == 0) { - zerr("physical %s", gettext("not specified")); - saw_error = TRUE; - validation_failed = TRUE; - } - if (strlen(in_progress_nwiftab.zone_nwif_address) == 0) { - zerr("address %s", gettext("not specified")); + (void) end_check_reqd(in_progress_nwiftab.zone_nwif_physical, + PT_PHYSICAL, &validation_failed); + (void) end_check_reqd(in_progress_nwiftab.zone_nwif_address, + PT_ADDRESS, &validation_failed); + + if (validation_failed) { saw_error = TRUE; - validation_failed = TRUE; - } - if (validation_failed) return; + } + if (end_op == CMD_ADD) { /* Make sure there isn't already one like this. */ bzero(&tmp_nwiftab, sizeof (tmp_nwiftab)); @@ -3649,15 +3693,17 @@ end_func(cmd_t *cmd) &in_progress_nwiftab); } break; + case RT_DEVICE: /* First make sure everything was filled in. */ - if (strlen(in_progress_devtab.zone_dev_match) == 0) { - zerr("match %s", gettext("not specified")); + (void) end_check_reqd(in_progress_devtab.zone_dev_match, + PT_MATCH, &validation_failed); + + if (validation_failed) { saw_error = TRUE; - validation_failed = TRUE; - } - if (validation_failed) return; + } + if (end_op == CMD_ADD) { /* Make sure there isn't already one like this. */ (void) strlcpy(tmp_devtab.zone_dev_match, @@ -3677,20 +3723,22 @@ end_func(cmd_t *cmd) &in_progress_devtab); } break; + case RT_RCTL: /* First make sure everything was filled in. */ - if (strlen(in_progress_rctltab.zone_rctl_name) == 0) { - zerr("name %s", gettext("not specified")); - saw_error = TRUE; - validation_failed = TRUE; - } + (void) end_check_reqd(in_progress_rctltab.zone_rctl_name, + PT_NAME, &validation_failed); + if (in_progress_rctltab.zone_rctl_valptr == NULL) { zerr(gettext("no %s specified"), pt_to_str(PT_VALUE)); - saw_error = TRUE; validation_failed = TRUE; } - if (validation_failed) + + if (validation_failed) { + saw_error = TRUE; return; + } + if (end_op == CMD_ADD) { /* Make sure there isn't already one like this. */ (void) strlcpy(tmp_rctltab.zone_rctl_name, @@ -3719,34 +3767,27 @@ end_func(cmd_t *cmd) in_progress_rctltab.zone_rctl_valptr = NULL; } break; + case RT_ATTR: /* First make sure everything was filled in. */ - if (strlen(in_progress_attrtab.zone_attr_name) == 0) { - zerr("name %s", gettext("not specified")); - saw_error = TRUE; - validation_failed = TRUE; - } - if (strlen(in_progress_attrtab.zone_attr_type) == 0) { - zerr("type %s", gettext("not specified")); - saw_error = TRUE; - validation_failed = TRUE; - } - if (strlen(in_progress_attrtab.zone_attr_value) == 0) { - zerr("value %s", gettext("not specified")); - saw_error = TRUE; - validation_failed = TRUE; - } + (void) end_check_reqd(in_progress_attrtab.zone_attr_name, + PT_NAME, &validation_failed); + (void) end_check_reqd(in_progress_attrtab.zone_attr_type, + PT_TYPE, &validation_failed); + (void) end_check_reqd(in_progress_attrtab.zone_attr_value, + PT_VALUE, &validation_failed); + if (validate_attr_name(in_progress_attrtab.zone_attr_name) != - Z_OK) { - saw_error = TRUE; + Z_OK) validation_failed = TRUE; - } - if (validate_attr_type_val(&in_progress_attrtab) != Z_OK) { - saw_error = TRUE; + + if (validate_attr_type_val(&in_progress_attrtab) != Z_OK) validation_failed = TRUE; - } - if (validation_failed) + + if (validation_failed) { + saw_error = TRUE; return; + } if (end_op == CMD_ADD) { /* Make sure there isn't already one like this. */ bzero(&tmp_attrtab, sizeof (tmp_attrtab)); @@ -3882,15 +3923,16 @@ revert_func(cmd_t *cmd) zone_perror(execname, Z_NOMEM, TRUE); exit(Z_ERR); } - if ((err = zonecfg_get_handle(zone, handle)) != Z_OK) { + if ((err = zonecfg_get_handle(revert_zone, handle)) != Z_OK) { saw_error = TRUE; got_handle = FALSE; if (err == Z_NO_ZONE) zerr(gettext("%s: no such saved zone to revert to."), - zone); + revert_zone); else zone_perror(zone, err, TRUE); } + (void) strlcpy(zone, revert_zone, sizeof (zone)); } void @@ -4109,9 +4151,9 @@ do_interactive(void) */ (void) initialize(FALSE); } - do + do { err = read_input(); - while (err == Z_REPEAT); + } while (err == Z_REPEAT); return (err); } @@ -4231,42 +4273,6 @@ get_execbasename(char *execfullname) return (execbasename); } -static void -validate_zone_name() -{ - regex_t reg; - char *locale = NULL, locale_buf[MAXPATHLEN]; - - if (strcmp(zone, GLOBAL_ZONENAME) == 0) - goto err; - - /* - * The regex(5) functions below are locale-sensitive, so save the - * user's locale, then set it to "C" for the regex's, and restore - * it afterwards. - */ - if ((locale = setlocale(LC_ALL, NULL)) != NULL) { - (void) strlcpy(locale_buf, locale, sizeof (locale_buf)); - locale = locale_buf; - } - (void) setlocale(LC_ALL, "C"); - if (regcomp(®, "^" ZONENAME_REGEXP "$", REG_EXTENDED|REG_NOSUB) != 0) - goto err; - - if (regexec(®, zone, (size_t)0, NULL, 0) != 0) - goto err; - - regfree(®); - (void) setlocale(LC_ALL, locale); - return; - -err: - (void) setlocale(LC_ALL, locale); - zone_perror(zone, Z_BOGUS_ZONE_NAME, TRUE); - usage(FALSE, HELP_SYNTAX); - exit(Z_USAGE); -} - int main(int argc, char *argv[]) { @@ -4297,7 +4303,6 @@ main(int argc, char *argv[]) exit(Z_OK); } - zone = NULL; while ((arg = getopt(argc, argv, "?f:z:")) != EOF) { switch (arg) { case '?': @@ -4312,7 +4317,13 @@ main(int argc, char *argv[]) cmd_file_mode = TRUE; break; case 'z': - zone = optarg; + if (zonecfg_validate_zonename(optarg) != Z_OK) { + zone_perror(optarg, Z_BOGUS_ZONE_NAME, TRUE); + usage(FALSE, HELP_SYNTAX); + exit(Z_USAGE); + } + (void) strlcpy(zone, optarg, sizeof (zone)); + (void) strlcpy(revert_zone, optarg, sizeof (zone)); break; default: usage(FALSE, HELP_USAGE); @@ -4320,21 +4331,24 @@ main(int argc, char *argv[]) } } - if (optind > argc || zone == NULL) { + if (optind > argc || strcmp(zone, "") == 0) { usage(FALSE, HELP_USAGE); exit(Z_USAGE); } - validate_zone_name(); - if (zonecfg_access(zone, W_OK) == Z_OK) { + if ((err = zonecfg_access(zone, W_OK)) == Z_OK) { read_only_mode = FALSE; - } else { + } else if (err == Z_ACCES) { read_only_mode = TRUE; /* skip this message in one-off from command line mode */ if (optind == argc) (void) fprintf(stderr, gettext("WARNING: you do not " "have write access to this zone's configuration " "file;\ngoing into read-only mode.\n")); + } else { + fprintf(stderr, "%s: Could not access zone configuration " + "store: %s\n", execname, zonecfg_strerror(err)); + exit(Z_ERR); } if ((handle = zonecfg_init_handle()) == NULL) { diff --git a/usr/src/cmd/zonecfg/zonecfg.h b/usr/src/cmd/zonecfg/zonecfg.h index 8337811ac1..e0fca7a02d 100644 --- a/usr/src/cmd/zonecfg/zonecfg.h +++ b/usr/src/cmd/zonecfg/zonecfg.h @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -69,37 +69,39 @@ typedef int bool; /* resource types: increment RT_MAX when expanding this list */ #define RT_UNKNOWN 0 -#define RT_ZONEPATH 1 /* really a property, but for info ... */ -#define RT_AUTOBOOT 2 /* really a property, but for info ... */ -#define RT_POOL 3 /* really a property, but for info ... */ -#define RT_FS 4 -#define RT_IPD 5 -#define RT_NET 6 -#define RT_DEVICE 7 -#define RT_RCTL 8 -#define RT_ATTR 9 +#define RT_ZONENAME 1 /* really a property, but for info ... */ +#define RT_ZONEPATH 2 /* really a property, but for info ... */ +#define RT_AUTOBOOT 3 /* really a property, but for info ... */ +#define RT_POOL 4 /* really a property, but for info ... */ +#define RT_FS 5 +#define RT_IPD 6 +#define RT_NET 7 +#define RT_DEVICE 8 +#define RT_RCTL 9 +#define RT_ATTR 10 #define RT_MIN RT_UNKNOWN #define RT_MAX RT_ATTR /* property types: increment PT_MAX when expanding this list */ #define PT_UNKNOWN 0 -#define PT_ZONEPATH 1 -#define PT_AUTOBOOT 2 -#define PT_POOL 3 -#define PT_DIR 4 -#define PT_SPECIAL 5 -#define PT_TYPE 6 -#define PT_OPTIONS 7 -#define PT_ADDRESS 8 -#define PT_PHYSICAL 9 -#define PT_NAME 10 -#define PT_VALUE 11 -#define PT_MATCH 12 -#define PT_PRIV 13 -#define PT_LIMIT 14 -#define PT_ACTION 15 -#define PT_RAW 16 +#define PT_ZONENAME 1 +#define PT_ZONEPATH 2 +#define PT_AUTOBOOT 3 +#define PT_POOL 4 +#define PT_DIR 5 +#define PT_SPECIAL 6 +#define PT_TYPE 7 +#define PT_OPTIONS 8 +#define PT_ADDRESS 9 +#define PT_PHYSICAL 10 +#define PT_NAME 11 +#define PT_VALUE 12 +#define PT_MATCH 13 +#define PT_PRIV 14 +#define PT_LIMIT 15 +#define PT_ACTION 16 +#define PT_RAW 17 #define PT_MIN PT_UNKNOWN #define PT_MAX PT_RAW diff --git a/usr/src/cmd/zonecfg/zonecfg_grammar.y b/usr/src/cmd/zonecfg/zonecfg_grammar.y index 2927233b95..abca323bed 100644 --- a/usr/src/cmd/zonecfg/zonecfg_grammar.y +++ b/usr/src/cmd/zonecfg/zonecfg_grammar.y @@ -58,9 +58,9 @@ extern void yyerror(char *s); %start commands %token HELP CREATE EXPORT ADD DELETE REMOVE SELECT SET INFO CANCEL END VERIFY -%token COMMIT REVERT EXIT SEMICOLON TOKEN ZONEPATH AUTOBOOT POOL NET FS IPD ATTR -%token DEVICE RCTL SPECIAL RAW DIR OPTIONS TYPE ADDRESS PHYSICAL NAME -%token MATCH PRIV LIMIT ACTION VALUE EQUAL OPEN_SQ_BRACKET CLOSE_SQ_BRACKET +%token COMMIT REVERT EXIT SEMICOLON TOKEN ZONENAME ZONEPATH AUTOBOOT POOL NET +%token FS IPD ATTR DEVICE RCTL SPECIAL RAW DIR OPTIONS TYPE ADDRESS PHYSICAL +%token NAME MATCH PRIV LIMIT ACTION VALUE EQUAL OPEN_SQ_BRACKET CLOSE_SQ_BRACKET %token OPEN_PAREN CLOSE_PAREN COMMA %type <strval> TOKEN EQUAL OPEN_SQ_BRACKET CLOSE_SQ_BRACKET @@ -68,7 +68,7 @@ extern void yyerror(char *s); %type <complex> complex_piece complex_prop_val %type <ival> resource_type NET FS IPD DEVICE RCTL ATTR %type <ival> property_name SPECIAL RAW DIR OPTIONS TYPE ADDRESS PHYSICAL NAME - MATCH ZONEPATH AUTOBOOT POOL VALUE PRIV LIMIT ACTION + MATCH ZONENAME ZONEPATH AUTOBOOT POOL VALUE PRIV LIMIT ACTION %type <cmd> command %type <cmd> add_command ADD %type <cmd> cancel_command CANCEL @@ -402,6 +402,15 @@ info_command: INFO $$->cmd_res_type = $2; $$->cmd_prop_nv_pairs = 0; } + | INFO ZONENAME + { + if (($$ = alloc_cmd()) == NULL) + YYERROR; + cmd = $$; + $$->cmd_handler = &info_func; + $$->cmd_res_type = RT_ZONENAME; + $$->cmd_prop_nv_pairs = 0; + } | INFO ZONEPATH { if (($$ = alloc_cmd()) == NULL) @@ -665,6 +674,7 @@ property_name: SPECIAL { $$ = PT_SPECIAL; } | DIR { $$ = PT_DIR; } | TYPE { $$ = PT_TYPE; } | OPTIONS { $$ = PT_OPTIONS; } + | ZONENAME { $$ = PT_ZONENAME; } | ZONEPATH { $$ = PT_ZONEPATH; } | AUTOBOOT { $$ = PT_AUTOBOOT; } | POOL { $$ = PT_POOL; } diff --git a/usr/src/cmd/zonecfg/zonecfg_lex.l b/usr/src/cmd/zonecfg/zonecfg_lex.l index 1beafcd7f0..1a5de3659e 100644 --- a/usr/src/cmd/zonecfg/zonecfg_lex.l +++ b/usr/src/cmd/zonecfg/zonecfg_lex.l @@ -20,7 +20,7 @@ * * CDDL HEADER END * - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -156,6 +156,9 @@ char *safe_strdup(char *s); <TSTATE>attr { return ATTR; } +<TSTATE>zonename { return ZONENAME; } +<CSTATE>zonename { return ZONENAME; } + <TSTATE>zonepath { return ZONEPATH; } <CSTATE>zonepath { return ZONEPATH; } diff --git a/usr/src/head/libzonecfg.h b/usr/src/head/libzonecfg.h index e5c3f9a451..dd3c1d41ef 100644 --- a/usr/src/head/libzonecfg.h +++ b/usr/src/head/libzonecfg.h @@ -72,9 +72,9 @@ extern "C" { #define Z_NO_RESOURCE_ID 18 /* no/wrong resource id */ #define Z_NO_PROPERTY_TYPE 19 /* no/wrong property type */ #define Z_NO_PROPERTY_ID 20 /* no/wrong property id */ -#define Z_RESOURCE_EXISTS 21 /* resource already exists */ +#define Z_BAD_ZONE_STATE 21 /* zone state invalid for given task */ #define Z_INVALID_DOCUMENT 22 /* libxml can't validate against DTD */ -#define Z_ID_IN_USE 23 /* add_index conflict */ +#define Z_NAME_IN_USE 23 /* zone name already in use (rename) */ #define Z_NO_SUCH_ID 24 /* delete_index: no old ID */ #define Z_UPDATING_INDEX 25 /* add/modify/delete_index problem */ #define Z_LOCKING_FILE 26 /* problem locking index file */ @@ -108,6 +108,7 @@ struct zoneent { char zone_name[ZONENAME_MAX]; /* name of the zone */ int zone_state; /* configured | incomplete | installed */ char zone_path[MAXPATHLEN]; + char zone_newname[ZONENAME_MAX]; /* for doing renames */ }; typedef struct zone_dochandle *zone_dochandle_t; /* opaque handle */ @@ -160,9 +161,10 @@ struct zone_attrtab { extern zone_dochandle_t zonecfg_init_handle(void); extern int zonecfg_get_handle(char *, zone_dochandle_t); extern int zonecfg_get_snapshot_handle(char *, zone_dochandle_t); +extern int zonecfg_get_template_handle(char *, char *, zone_dochandle_t); extern int zonecfg_check_handle(zone_dochandle_t); extern void zonecfg_fini_handle(zone_dochandle_t); -extern int zonecfg_destroy(const char *); +extern int zonecfg_destroy(const char *, boolean_t); extern int zonecfg_destroy_snapshot(char *); extern int zonecfg_save(zone_dochandle_t); extern int zonecfg_create_snapshot(char *); @@ -172,6 +174,7 @@ extern int zonecfg_access(const char *, int); /* * Zone name, path to zone directory, autoboot setting and pool. */ +extern int zonecfg_validate_zonename(char *); extern int zonecfg_get_name(zone_dochandle_t, char *, size_t); extern int zonecfg_set_name(zone_dochandle_t, char *); extern int zonecfg_get_zonepath(zone_dochandle_t, char *, size_t); @@ -279,12 +282,6 @@ extern int zonecfg_endrctlent(zone_dochandle_t); extern int zonecfg_get_privset(priv_set_t *); /* - * Index update routines. - */ -extern int zonecfg_add_index(char *, char *); -extern int zonecfg_delete_index(char *); - -/* * Higher-level routines. */ extern int zone_get_rootpath(char *, char *, size_t); diff --git a/usr/src/lib/libzonecfg/common/getzoneent.c b/usr/src/lib/libzonecfg/common/getzoneent.c index 1ac5730c9d..4a62bddaa9 100644 --- a/usr/src/lib/libzonecfg/common/getzoneent.c +++ b/usr/src/lib/libzonecfg/common/getzoneent.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -228,17 +228,20 @@ unlock_index_file(int lock_fd) * * If ze->zone_state is < 0, it means leave the * existing value unchanged; this is only meaningful when operation == - * PZE_MODIFY (i.e., it's bad on PZE_ADD and a no-op on PZE_DELETE). + * PZE_MODIFY (i.e., it's bad on PZE_ADD and a no-op on PZE_REMOVE). * - * Likewise, a zero-length ze->zone_path means leave the existing value + * A zero-length ze->zone_path means leave the existing value * unchanged; this is only meaningful when operation == PZE_MODIFY - * (i.e., it's bad on PZE_ADD and a no-op on PZE_DELETE). + * (i.e., it's bad on PZE_ADD and a no-op on PZE_REMOVE). + * + * A zero-length ze->zone_newname means leave the existing name + * unchanged; otherwise the zone is renamed to zone_newname. This is + * only meaningful when operation == PZE_MODIFY. * * Locking and unlocking is done via the functions above. * The file itself is not modified in place; rather, a copy is made which * is modified, then the copy is atomically renamed back to the main file. */ - int putzoneent(struct zoneent *ze, zoneent_op_t operation) { @@ -254,6 +257,10 @@ putzoneent(struct zoneent *ze, zoneent_op_t operation) if (operation == PZE_ADD && (ze->zone_state < 0 || strlen(ze->zone_path) == 0)) return (Z_INVAL); + + if (operation != PZE_MODIFY && strlen(ze->zone_newname) != 0) + return (Z_INVAL); + if ((err = lock_index_file(&lock_fd)) != Z_OK) return (err); tmp_file_name = strdup(_PATH_TMPFILE); @@ -331,6 +338,7 @@ putzoneent(struct zoneent *ze, zoneent_op_t operation) goto error; } else if (operation == PZE_MODIFY) { char tmp_state[ZONE_STATE_MAXSTRLEN + 1]; + char *tmp_name; if (ze->zone_state >= 0 && strlen(ze->zone_path) > 0) { @@ -351,12 +359,21 @@ putzoneent(struct zoneent *ze, zoneent_op_t operation) p = gettok(&cp); + /* + * If a new name is supplied, use it. + */ + if (strlen(ze->zone_newname) != 0) + tmp_name = ze->zone_newname; + else + tmp_name = ze->zone_name; + (void) fprintf(tmp_file, "%s:%s:%s%s%s\n", - ze->zone_name, tmp_state, + tmp_name, tmp_state, need_quotes ? "\"" : "", (strlen(ze->zone_path) == 0) ? p : ze->zone_path, need_quotes ? "\"" : ""); } + /* else if (operation == PZE_REMOVE) { no-op } */ } else { (void) fputs(orig_buf, tmp_file); } diff --git a/usr/src/lib/libzonecfg/common/libzonecfg.c b/usr/src/lib/libzonecfg/common/libzonecfg.c index 87d2e45c52..b9d80dbefa 100644 --- a/usr/src/lib/libzonecfg/common/libzonecfg.c +++ b/usr/src/lib/libzonecfg/common/libzonecfg.c @@ -38,6 +38,7 @@ #include <ctype.h> #include <sys/mntio.h> #include <sys/mnttab.h> +#include <sys/types.h> #include <arpa/inet.h> #include <netdb.h> @@ -99,6 +100,9 @@ struct zone_dochandle { xmlDocPtr zone_dh_doc; xmlNodePtr zone_dh_cur; xmlNodePtr zone_dh_top; + boolean_t zone_dh_newzone; + boolean_t zone_dh_snapshot; + char zone_dh_delete_name[ZONENAME_MAX]; }; /* @@ -140,14 +144,11 @@ zonecfg_error_func(void *ctx, const char *msg, ...) zone_dochandle_t zonecfg_init_handle(void) { - zone_dochandle_t handle = malloc(sizeof (struct zone_dochandle)); + zone_dochandle_t handle = calloc(1, sizeof (struct zone_dochandle)); if (handle == NULL) { errno = Z_NOMEM; return (NULL); } - handle->zone_dh_doc = NULL; - handle->zone_dh_cur = NULL; - handle->zone_dh_top = NULL; /* generic libxml initialization */ xmlLineNumbersDefault(1); @@ -191,12 +192,62 @@ zonecfg_destroy_impl(char *filename) } int -zonecfg_destroy(const char *zonename) +zonecfg_destroy(const char *zonename, boolean_t force) { char path[MAXPATHLEN]; + struct zoneent ze; + int err, state_err; + zone_state_t state; config_file_path(zonename, path); - return (zonecfg_destroy_impl(path)); + + state_err = zone_get_state((char *)zonename, &state); + err = access(path, W_OK); + + /* + * If there is no file, and no index entry, reliably indicate that no + * such zone exists. + */ + if ((state_err == Z_NO_ZONE) && (err == -1) && (errno == ENOENT)) + return (Z_NO_ZONE); + + /* + * Handle any other filesystem related errors (except if the XML + * file is missing, which we treat silently), unless we're forcing, + * in which case we plow on. + */ + if (err == -1 && errno != ENOENT) { + if (errno == EACCES) + return (Z_ACCES); + else if (!force) + return (Z_MISC_FS); + } + + if (state > ZONE_STATE_INSTALLED) + return (Z_BAD_ZONE_STATE); + + if (!force && state > ZONE_STATE_CONFIGURED) + return (Z_BAD_ZONE_STATE); + + /* + * Index deletion succeeds even if the entry doesn't exist. So this + * will fail only if we've had some more severe problem. + */ + bzero(&ze, sizeof (ze)); + (void) strlcpy(ze.zone_name, zonename, sizeof (ze.zone_name)); + if ((err = putzoneent(&ze, PZE_REMOVE)) != Z_OK) + if (!force) + return (err); + + err = zonecfg_destroy_impl(path); + + /* + * Treat failure to find the XML file silently, since, well, it's + * gone, and with the index file cleaned up, we're done. + */ + if (err == Z_OK || err == Z_NO_ZONE) + return (Z_OK); + return (err); } int @@ -277,12 +328,39 @@ setrootattr(zone_dochandle_t handle, const xmlChar *propname, char *propval) return (Z_OK); } +static void +addcomment(zone_dochandle_t handle, const char *comment) +{ + xmlNodePtr node; + node = xmlNewComment((xmlChar *) comment); + + if (node != NULL) + (void) xmlAddPrevSibling(handle->zone_dh_top, node); +} + +static void +stripcomments(zone_dochandle_t handle) +{ + xmlDocPtr top; + xmlNodePtr child, next; + + top = handle->zone_dh_doc; + for (child = top->xmlChildrenNode; child != NULL; child = next) { + next = child->next; + if (child->name == NULL) + continue; + if (xmlStrcmp(child->name, DTD_ELEM_COMMENT) == 0) { + next = child->next; + xmlUnlinkNode(child); + xmlFreeNode(child); + } + } +} + static int zonecfg_get_handle_impl(char *zonename, char *filename, zone_dochandle_t handle) { xmlValidCtxtPtr cvp; - xmlDocPtr top; - xmlNodePtr child, next; struct stat statbuf; int valid; @@ -302,18 +380,9 @@ zonecfg_get_handle_impl(char *zonename, char *filename, zone_dochandle_t handle) xmlFreeValidCtxt(cvp); if (valid == 0) return (Z_INVALID_DOCUMENT); + /* delete any comments such as inherited Sun copyright / ident str */ - top = handle->zone_dh_doc; - for (child = top->xmlChildrenNode; child != NULL; child = next) { - next = child->next; - if (child->name == NULL) - continue; - if (xmlStrcmp(child->name, DTD_ELEM_COMMENT) == 0) { - next = child->next; - xmlUnlinkNode(child); - xmlFreeNode(child); - } - } + stripcomments(handle); return (Z_OK); } @@ -323,6 +392,8 @@ zonecfg_get_handle(char *zonename, zone_dochandle_t handle) char path[MAXPATHLEN]; config_file_path(zonename, path); + handle->zone_dh_newzone = B_FALSE; + return (zonecfg_get_handle_impl(zonename, path, handle)); } @@ -332,10 +403,84 @@ zonecfg_get_snapshot_handle(char *zonename, zone_dochandle_t handle) char path[MAXPATHLEN]; snap_file_path(zonename, path); + handle->zone_dh_newzone = B_FALSE; return (zonecfg_get_handle_impl(zonename, path, handle)); } int +zonecfg_get_template_handle(char *template, char *zonename, + zone_dochandle_t handle) +{ + char path[MAXPATHLEN]; + int err; + + config_file_path(template, path); + + if ((err = zonecfg_get_handle_impl(template, path, handle)) != Z_OK) + return (err); + handle->zone_dh_newzone = B_TRUE; + return (setrootattr(handle, DTD_ATTR_NAME, zonename)); +} + +static boolean_t +is_renaming(zone_dochandle_t handle) +{ + if (handle->zone_dh_newzone) + return (B_FALSE); + if (strlen(handle->zone_dh_delete_name) > 0) + return (B_TRUE); + return (B_FALSE); +} + +static boolean_t +is_new(zone_dochandle_t handle) +{ + return (handle->zone_dh_newzone || handle->zone_dh_snapshot); +} + +static boolean_t +is_snapshot(zone_dochandle_t handle) +{ + return (handle->zone_dh_snapshot); +} + +/* + * It would be great to be able to use libc's ctype(3c) macros, but we + * can't, as they are locale sensitive, and it would break our limited thread + * safety if this routine had to change the app locale on the fly. + */ +int +zonecfg_validate_zonename(char *zone) +{ + int i; + + if (strcmp(zone, GLOBAL_ZONENAME) == 0) + return (Z_BOGUS_ZONE_NAME); + + if (strlen(zone) >= ZONENAME_MAX) + return (Z_BOGUS_ZONE_NAME); + + if (!((zone[0] >= 'a' && zone[0] <= 'z') || + (zone[0] >= 'A' && zone[0] <= 'Z') || + (zone[0] >= '0' && zone[0] <= '9'))) + return (Z_BOGUS_ZONE_NAME); + + for (i = 1; zone[i] != '\0'; i++) { + if (!((zone[i] >= 'a' && zone[i] <= 'z') || + (zone[i] >= 'A' && zone[i] <= 'Z') || + (zone[i] >= '0' && zone[i] <= '9') || + (zone[i] == '-') || (zone[i] == '_') || (zone[i] == '.'))) + return (Z_BOGUS_ZONE_NAME); + } + + return (Z_OK); +} + +/* + * Changing the zone name requires us to track both the old and new + * name of the zone until commit time. + */ +int zonecfg_get_name(zone_dochandle_t handle, char *name, size_t namesize) { return (getrootattr(handle, DTD_ATTR_NAME, name, namesize)); @@ -344,7 +489,85 @@ zonecfg_get_name(zone_dochandle_t handle, char *name, size_t namesize) int zonecfg_set_name(zone_dochandle_t handle, char *name) { - return (setrootattr(handle, DTD_ATTR_NAME, name)); + zone_state_t state; + char curname[ZONENAME_MAX], old_delname[ZONENAME_MAX]; + int err; + + if ((err = getrootattr(handle, DTD_ATTR_NAME, curname, + sizeof (curname))) != Z_OK) + return (err); + + if (strcmp(name, curname) == 0) + return (Z_OK); + + /* + * Switching zone names to one beginning with SUNW is not permitted. + */ + if (strncmp(name, "SUNW", 4) == 0) + return (Z_BOGUS_ZONE_NAME); + + if ((err = zonecfg_validate_zonename(name)) != Z_OK) + return (err); + + /* + * Setting the name back to the original name (effectively a revert of + * the name) is fine. But if we carry on, we'll falsely identify the + * name as "in use," so special case here. + */ + if (strcmp(name, handle->zone_dh_delete_name) == 0) { + err = setrootattr(handle, DTD_ATTR_NAME, name); + handle->zone_dh_delete_name[0] = '\0'; + return (err); + } + + /* Check to see if new name chosen is already in use */ + if (zone_get_state(name, &state) != Z_NO_ZONE) + return (Z_NAME_IN_USE); + + /* + * If this isn't already "new" or in a renaming transition, then + * we're initiating a rename here; so stash the "delete name" + * (i.e. the name of the zone we'll be removing) for the rename. + */ + (void) strlcpy(old_delname, handle->zone_dh_delete_name, + sizeof (old_delname)); + if (!is_new(handle) && !is_renaming(handle)) { + /* + * Name change is allowed only when the zone we're altering + * is not ready or running. + */ + err = zone_get_state(curname, &state); + if (err == Z_OK) { + if (state > ZONE_STATE_INSTALLED) + return (Z_BAD_ZONE_STATE); + } else if (err != Z_NO_ZONE) { + return (err); + } + + (void) strlcpy(handle->zone_dh_delete_name, curname, + sizeof (handle->zone_dh_delete_name)); + assert(is_renaming(handle)); + } else if (is_renaming(handle)) { + err = zone_get_state(handle->zone_dh_delete_name, &state); + if (err == Z_OK) { + if (state > ZONE_STATE_INSTALLED) + return (Z_BAD_ZONE_STATE); + } else if (err != Z_NO_ZONE) { + return (err); + } + } + + if ((err = setrootattr(handle, DTD_ATTR_NAME, name)) != Z_OK) { + /* + * Restore the deletename to whatever it was at the + * top of the routine, since we've had a failure. + */ + (void) strlcpy(handle->zone_dh_delete_name, old_delname, + sizeof (handle->zone_dh_delete_name)); + return (err); + } + + return (Z_OK); } int @@ -402,6 +625,11 @@ zonecfg_set_pool(zone_dochandle_t handle, char *pool) * in the <zonename>.xml file: the path to the zone. This is for performance, * since we need to walk all zonepath's in order to be able to detect conflicts * (see crosscheck_zonepaths() in the zoneadm command). + * + * An additional complexity is that when doing a rename, we'd like the entire + * index update operation (rename, and potential state changes) to be atomic. + * In general, the operation of this function should succeed or fail as + * a unit. */ int zonecfg_refresh_index_file(zone_dochandle_t handle) @@ -409,25 +637,94 @@ zonecfg_refresh_index_file(zone_dochandle_t handle) char name[ZONENAME_MAX], zonepath[MAXPATHLEN]; struct zoneent ze; int err; + int opcode; + char *zn; + + bzero(&ze, sizeof (ze)); + ze.zone_state = -1; /* Preserve existing state in index */ if ((err = zonecfg_get_name(handle, name, sizeof (name))) != Z_OK) return (err); + (void) strlcpy(ze.zone_name, name, sizeof (ze.zone_name)); + if ((err = zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath))) != Z_OK) return (err); - (void) strlcpy(ze.zone_name, name, sizeof (ze.zone_name)); - ze.zone_state = -1; (void) strlcpy(ze.zone_path, zonepath, sizeof (ze.zone_path)); - return (putzoneent(&ze, PZE_MODIFY)); + + if (is_renaming(handle)) { + opcode = PZE_MODIFY; + (void) strlcpy(ze.zone_name, handle->zone_dh_delete_name, + sizeof (ze.zone_name)); + (void) strlcpy(ze.zone_newname, name, sizeof (ze.zone_newname)); + } else if (is_new(handle)) { + FILE *cookie; + /* + * Be tolerant of the zone already existing in the index file, + * since we might be forcibly overwriting an existing + * configuration with a new one (for example 'create -F' + * in zonecfg). + */ + opcode = PZE_ADD; + cookie = setzoneent(); + while ((zn = getzoneent(cookie)) != NULL) { + if (strcmp(zn, name) == 0) { + opcode = PZE_MODIFY; + free(zn); + break; + } + free(zn); + } + endzoneent(cookie); + ze.zone_state = ZONE_STATE_CONFIGURED; + } else { + opcode = PZE_MODIFY; + } + + if ((err = putzoneent(&ze, opcode)) != Z_OK) + return (err); + + return (Z_OK); } +/* + * The goal of this routine is to cause the index file update and the + * document save to happen as an atomic operation. We do the document + * first, saving a backup copy using a hard link; if that succeeds, we go + * on to the index. If that fails, we roll the document back into place. + * + * Strategy: + * + * New zone 'foo' configuration: + * Create tmpfile (zonecfg.xxxxxx) + * Write XML to tmpfile + * Rename tmpfile to xmlfile (zonecfg.xxxxxx -> foo.xml) + * Add entry to index file + * If it fails, delete foo.xml, leaving nothing behind. + * + * Save existing zone 'foo': + * Make backup of foo.xml -> .backup + * Create tmpfile (zonecfg.xxxxxx) + * Write XML to tmpfile + * Rename tmpfile to xmlfile (zonecfg.xxxxxx -> foo.xml) + * Modify index file as needed + * If it fails, recover from .backup -> foo.xml + * + * Rename 'foo' to 'bar': + * Create tmpfile (zonecfg.xxxxxx) + * Write XML to tmpfile + * Rename tmpfile to xmlfile (zonecfg.xxxxxx -> bar.xml) + * Add entry for 'bar' to index file, Remove entry for 'foo' (refresh) + * If it fails, delete bar.xml; foo.xml is left behind. + */ static int zonecfg_save_impl(zone_dochandle_t handle, char *filename) { char tmpfile[MAXPATHLEN]; - int tmpfd; + char bakdir[MAXPATHLEN], bakbase[MAXPATHLEN], bakfile[MAXPATHLEN]; + int tmpfd, err; xmlValidCtxt cvp = { NULL }; - xmlNodePtr comment; + boolean_t backup; (void) strlcpy(tmpfile, filename, sizeof (tmpfile)); (void) dirname(tmpfile); @@ -443,28 +740,82 @@ zonecfg_save_impl(zone_dochandle_t handle, char *filename) cvp.error = zonecfg_error_func; cvp.warning = zonecfg_error_func; - if ((comment = xmlNewComment((xmlChar *) "\n DO NOT EDIT THIS " - "FILE. Use zonecfg(1M) instead.\n")) == NULL) - goto err; - if (xmlAddPrevSibling(handle->zone_dh_top, comment) == 0) - goto err; - - if (xmlValidateDocument(&cvp, handle->zone_dh_doc) == 0) - goto err; + /* + * We do a final validation of the document-- but the library has + * malfunctioned if it fails to validate, so it's an assert. + */ + assert(xmlValidateDocument(&cvp, handle->zone_dh_doc) != 0); if (xmlSaveFormatFile(tmpfile, handle->zone_dh_doc, 1) <= 0) goto err; + (void) chmod(tmpfile, 0644); + /* + * In the event we are doing a standard save, hard link a copy of the + * original file in .backup.<pid>.filename so we can restore it if + * something goes wrong. + */ + if (!is_new(handle) && !is_renaming(handle)) { + backup = B_TRUE; + + (void) strlcpy(bakdir, filename, sizeof (bakdir)); + (void) strlcpy(bakbase, filename, sizeof (bakbase)); + (void) snprintf(bakfile, sizeof (bakfile), "%s/.backup.%d.%s", + dirname(bakdir), getpid(), basename(bakbase)); + + if (link(filename, bakfile) == -1) { + err = errno; + (void) unlink(tmpfile); + if (errno == EACCES) + return (Z_ACCES); + return (Z_MISC_FS); + } + } + + /* + * Move the new document over top of the old. + * i.e.: zonecfg.XXXXXX -> myzone.xml + */ if (rename(tmpfile, filename) == -1) { + err = errno; (void) unlink(tmpfile); - if (errno == EACCES) + if (backup) + (void) unlink(bakfile); + if (err == EACCES) return (Z_ACCES); return (Z_MISC_FS); } - /* now update the cached copy of the zone path in the index file */ - return (zonecfg_refresh_index_file(handle)); + /* + * If this is a snapshot, we're done-- don't add an index entry. + */ + if (is_snapshot(handle)) + return (Z_OK); + + /* now update the index file to reflect whatever we just did */ + if ((err = zonecfg_refresh_index_file(handle)) != Z_OK) { + if (backup) { + /* + * Try to restore from our backup. + */ + (void) unlink(filename); + (void) rename(bakfile, filename); + } else { + /* + * Either the zone is new, in which case we can delete + * new.xml, or we're doing a rename, so ditto. + */ + assert(is_new(handle) || is_renaming(handle)); + (void) unlink(filename); + } + return (Z_UPDATING_INDEX); + } + + if (backup) + (void) unlink(bakfile); + + return (Z_OK); err: (void) unlink(tmpfile); @@ -474,14 +825,43 @@ err: int zonecfg_save(zone_dochandle_t handle) { - char zname[MAXPATHLEN], path[MAXPATHLEN]; - int err; + char zname[ZONENAME_MAX], path[MAXPATHLEN]; + char delpath[MAXPATHLEN]; + int err = Z_SAVING_FILE; + + if (zonecfg_check_handle(handle) != Z_OK) + return (Z_BAD_HANDLE); - if ((err = zonecfg_get_name(handle, zname, sizeof (zname))) != Z_OK) { + /* + * We don't support saving snapshots at this time. + */ + if (handle->zone_dh_snapshot) + return (Z_INVAL); + + if ((err = zonecfg_get_name(handle, zname, sizeof (zname))) != Z_OK) return (err); - } + config_file_path(zname, path); - return (zonecfg_save_impl(handle, path)); + + addcomment(handle, "\n DO NOT EDIT THIS " + "FILE. Use zonecfg(1M) instead.\n"); + + err = zonecfg_save_impl(handle, path); + + stripcomments(handle); + + if (err != Z_OK) + return (err); + + handle->zone_dh_newzone = B_FALSE; + + if (is_renaming(handle)) { + config_file_path(handle->zone_dh_delete_name, delpath); + (void) unlink(delpath); + handle->zone_dh_delete_name[0] = '\0'; + } + + return (Z_OK); } /* @@ -519,6 +899,9 @@ zonecfg_create_snapshot(char *zonename) return (Z_NOMEM); } + handle->zone_dh_newzone = B_TRUE; + handle->zone_dh_snapshot = B_TRUE; + if ((error = zonecfg_get_handle(zonename, handle)) != Z_OK) goto out; if ((error = operation_prep(handle)) != Z_OK) @@ -548,8 +931,14 @@ zonecfg_create_snapshot(char *zonename) } snap_file_path(zonename, path); + + addcomment(handle, "\n DO NOT EDIT THIS FILE. " + "It is a snapshot of running zone state.\n"); + error = zonecfg_save_impl(handle, path); + stripcomments(handle); + out: zonecfg_fini_handle(handle); return (error); @@ -2142,13 +2531,13 @@ zonecfg_strerror(int errnum) return (dgettext(TEXT_DOMAIN, "No such property type")); case Z_NO_PROPERTY_ID: return (dgettext(TEXT_DOMAIN, "No such property with that id")); - case Z_RESOURCE_EXISTS: + case Z_BAD_ZONE_STATE: return (dgettext(TEXT_DOMAIN, - "Resource already exists with that id")); + "Zone state is invalid for the requested operation")); case Z_INVALID_DOCUMENT: return (dgettext(TEXT_DOMAIN, "Invalid document")); - case Z_ID_IN_USE: - return (dgettext(TEXT_DOMAIN, "Zone ID in use")); + case Z_NAME_IN_USE: + return (dgettext(TEXT_DOMAIN, "Zone name already in use")); case Z_NO_SUCH_ID: return (dgettext(TEXT_DOMAIN, "No such zone ID")); case Z_UPDATING_INDEX: @@ -2578,26 +2967,6 @@ zonecfg_get_privset(priv_set_t *privs) } int -zonecfg_add_index(char *zone, char *path) -{ - struct zoneent ze; - - (void) strlcpy(ze.zone_name, zone, sizeof (ze.zone_name)); - ze.zone_state = ZONE_STATE_CONFIGURED; - (void) strlcpy(ze.zone_path, path, sizeof (ze.zone_path)); - return (putzoneent(&ze, PZE_ADD)); -} - -int -zonecfg_delete_index(char *zone) -{ - struct zoneent ze; - - (void) strlcpy(ze.zone_name, zone, sizeof (ze.zone_name)); - return (putzoneent(&ze, PZE_REMOVE)); -} - -int zone_get_zonepath(char *zone_name, char *zonepath, size_t rp_sz) { zone_dochandle_t handle; @@ -2733,6 +3102,7 @@ zone_set_state(char *zone, zone_state_t state) state != ZONE_STATE_INCOMPLETE) return (Z_INVAL); + bzero(&ze, sizeof (ze)); (void) strlcpy(ze.zone_name, zone, sizeof (ze.zone_name)); ze.zone_state = state; (void) strlcpy(ze.zone_path, "", sizeof (ze.zone_path)); diff --git a/usr/src/lib/libzonecfg/spec/libzonecfg.spec b/usr/src/lib/libzonecfg/spec/libzonecfg.spec index 7c7c1a456a..3d3a84a12f 100644 --- a/usr/src/lib/libzonecfg/spec/libzonecfg.spec +++ b/usr/src/lib/libzonecfg/spec/libzonecfg.spec @@ -44,6 +44,12 @@ declaration int zonecfg_get_snapshot_handle(char *, zone_dochandle_t) version SUNWprivate_1.1 end +function zonecfg_get_template_handle +include <libzonecfg.h> +declaration int zonecfg_get_template_handle(char *, char *, zone_dochandle_t) +version SUNWprivate_1.1 +end + function zonecfg_check_handle include <libzonecfg.h> declaration int zonecfg_check_handle(zone_dochandle_t) @@ -68,6 +74,12 @@ declaration int zonecfg_set_name(zone_dochandle_t, char *) version SUNWprivate_1.1 end +function zonecfg_validate_zonename +include <libzonecfg.h> +declaration int zonecfg_validate_zonename(char *) +version SUNWprivate_1.1 +end + function zonecfg_get_zonepath include <libzonecfg.h> declaration int zonecfg_get_zonepath(zone_dochandle_t, char *, size_t) @@ -430,7 +442,7 @@ end function zonecfg_destroy include <libzonecfg.h> -declaration int zonecfg_destroy(const char *) +declaration int zonecfg_destroy(const char *, boolean_t) version SUNWprivate_1.1 end @@ -495,18 +507,6 @@ declaration void putzoneent(struct zoneent *, int) version SUNWprivate_1.1 end -function zonecfg_add_index -include <libzonecfg.h> -declaration int zonecfg_add_index(char *, char *) -version SUNWprivate_1.1 -end - -function zonecfg_delete_index -include <libzonecfg.h> -declaration int zonecfg_delete_index(char *) -version SUNWprivate_1.1 -end - function zone_get_id include <libzonecfg.h> declaration int zone_get_id(const char *, zoneid_t *); |