root/usr/src/cmd/fs.d/nfs/lib/replica.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * replica.c
 *
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Parse replicated server lists of the form:
 *
 *      host1:/path1,host2,host3,host4:/path2,host5:/path3
 *
 * into an array containing its constituent parts:
 *
 *      host1   /path1
 *      host2   /path2
 *      host3   /path2
 *      host4   /path2
 *      host5   /path3
 * where a server could also be represented in form of literal address
 * and in case it is an IPv6 literal address it will be enclosed in
 * square brackets [IPv6 Literal address]
 * Problems indicated by null return; they will be memory allocation
 * errors worthy of an error message unless count == -1, which means
 * a parse error.
 */

#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <errno.h>
#include "replica.h"

void
free_replica(struct replica *list, int count)
{
        int i;

        for (i = 0; i < count; i++) {
                if (list[i].host)
                        free(list[i].host);
                if (list[i].path)
                        free(list[i].path);
        }
        free(list);
}

struct replica *
parse_replica(char *special, int *count)
{
        struct replica *list = NULL;
        char *root, *special2;
        char *proot, *x, *y;
        int scount, v6addr, i;
        int found_colon = 0;

        *count = 0;
        scount = 0;
        v6addr = 0;
        root = special2 = strdup(special);
        proot = root;

        while (root) {
                switch (*root) {
                case '[':
                        if ((root != special2) && (*(root -1) != ',')) {
                                root++;
                                break;
                        }
                        y = strchr(root, ']');
                        if (!y) {
                                root++;
                                break;
                        }
                        if ((*(y + 1) != ',') && (*(y + 1) != ':')) {
                                root = y + 1;
                                break;
                        }
                        /*
                         * Found a v6 Literal Address, so set "v6addr"
                         * and grab the address and store it in the list
                         * under "host part".
                         */
                        proot = root + 1;
                        root = y + 1;
                        v6addr = 1;
                        if ((list = realloc(list, (*count + 1) *
                            sizeof (struct replica))) == NULL)
                                goto bad;
                        bzero(&list[(*count)++], sizeof (struct replica));
                        *y = '\0';
                        list[*count-1].host = strdup(proot);
                        if (!list[*count-1].host)
                                goto bad;
                        break;
                case ':':
                        *root = '\0';
                        x = root + 1;
                        /*
                         * Find comma (if present), which bounds the path.
                         * The comma implies that the user is trying to
                         * specify failover syntax if another colon follows.
                         */
                        if (((y = strchr(x, ',')) != NULL) &&
                            (strchr((y + 1), ':'))) {
                                root = y + 1;
                                *y = '\0';
                        } else {
                                found_colon = 1;
                                root = NULL;
                        }
                        /*
                         * If "v6addr" is set, unset it, and since the "host
                         * part" is already taken care of, skip to the "path
                         * path" part.
                         */
                        if (v6addr == 1)
                                v6addr = 0;
                        else {
                                if ((list = realloc(list, (*count + 1) *
                                    sizeof (struct replica))) == NULL)
                                        goto bad;
                                bzero(&list[(*count)++],
                                    sizeof (struct replica));
                                list[*count-1].host = strdup(proot);
                                if (!list[*count-1].host)
                                        goto bad;
                                proot = root;

                        }
                        for (i = scount; i < *count; i++) {
                                list[i].path = strdup(x);
                                if (!list[i].path)
                                        goto bad;
                        }
                        scount = i;
                        proot = root;
                        if (y)
                                *y = ',';
                        break;
                case ',':
                        /*
                         * If "v6addr" is set, unset it and continue
                         * else grab the address and store it in the list
                         * under "host part".
                         */
                        if (v6addr == 1) {
                                v6addr = 0;
                                proot = ++root;
                        } else {
                                *root = '\0';
                                root++;
                                if ((list = realloc(list, (*count + 1) *
                                    sizeof (struct replica))) == NULL)
                                        goto bad;
                                bzero(&list[(*count)++],
                                    sizeof (struct replica));
                                list[*count-1].host = strdup(proot);
                                if (!list[*count-1].host)
                                        goto bad;
                                proot = root;
                                *(root - 1) = ',';
                        }
                        break;
                default:
                        if (*root == '\0')
                                root = NULL;
                        else
                                root++;
                }
        }
        if (found_colon) {
                free(special2);
                return (list);
        }
bad:
        if (list)
                free_replica(list, *count);
        if (!found_colon)
                *count = -1;
        free(special2);
        return (NULL);
}