root/usr/src/cmd/fs.d/nfs/nfslog/buffer_list.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
 */
/*
 * Copyright (c) 1999 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <syslog.h>
#include <libintl.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include "nfslogd.h"
#include "../lib/nfslogtab.h"
#include "buffer_list.h"

static int buildbuffer_list(struct buffer_ent **, timestruc_t *);
static void free_buffer_ent(struct buffer_ent *);
static struct buffer_ent *findbuffer(struct buffer_ent *, char *);
static void free_sharepnt_list(struct sharepnt_ent *);
static void free_sharepnt_ent(struct sharepnt_ent *);
#ifdef  DEBUG
static void print_sharepnt_list(struct sharepnt_ent *);
#endif
static struct sharepnt_ent *findsharepnt(struct sharepnt_ent *, char *,
        struct sharepnt_ent **);

/*
 * Builds the buffer list from NFSLOGTAB and returns it in *listpp.
 * Returns 0 on success, non-zero error code otherwise.
 */
int
getbuffer_list(struct buffer_ent **listpp, timestruc_t *lu)
{
        *listpp = NULL;
        return (buildbuffer_list(listpp, lu));
}

/*
 * If NFSLOGTAB has not been modified since the last time we read it,
 * it simply returns the same buffer list, otherwise it re-reads NFSLOGTAB
 * and rebuilds the list.
 * No NFSLOGTAB is not treated as an error.
 * Returns 0 on success, non-zero error code otherwise
 */
int
checkbuffer_list(struct buffer_ent **listpp, timestruc_t *lu)
{
        struct stat st;
        int error = 0;

        if (stat(NFSLOGTAB, &st) == -1) {
                error = errno;
                if (error != ENOENT) {
                        syslog(LOG_ERR, gettext("Can't stat %s - %s"),
                                NFSLOGTAB, strerror(error));
                        error = 0;
                }
                return (error);
        }

        if (lu->tv_sec == st.st_mtim.tv_sec &&
            lu->tv_nsec == st.st_mtim.tv_nsec)
                return (0);

        free_buffer_list(listpp);       /* free existing list first */
        return (buildbuffer_list(listpp, lu));
}

/*
 * Does the actual work of reading NFSLOGTAB, and building the
 * buffer list. If *be_head already contains entries, it will
 * update the list with new information.
 * Returns 0 on success, non-zero error code otherwise.
 */
static int
buildbuffer_list(struct buffer_ent **be_head, timestruc_t *lu)
{
        FILE *fd;
        struct buffer_ent *be_tail = NULL, *bep;
        struct sharepnt_ent *se_tail = NULL, *sep;
        struct logtab_ent *lep;
        struct stat st;
        int error = 0, res;

        if ((fd = fopen(NFSLOGTAB, "r+")) == NULL) {
                error = errno;
                if (error != ENOENT) {
                        syslog(LOG_ERR, gettext("%s - %s\n"), NFSLOGTAB,
                                strerror(error));
                        error = 0;
                }
                return (error);
        }

        if (lockf(fileno(fd), F_LOCK, 0L) < 0) {
                error = errno;
                syslog(LOG_ERR, gettext("cannot lock %s - %s\n"), NFSLOGTAB,
                        strerror(error));
                (void) fclose(fd);
                return (error);
        }

        assert(*be_head == NULL);
        while ((res = logtab_getent(fd, &lep)) > 0) {
                if (bep = findbuffer(*be_head, lep->le_buffer)) {
                        /*
                         * Add sharepnt to buffer list
                         */
                        if (sep = findsharepnt(bep->be_sharepnt,
                            lep->le_path, &se_tail)) {
                                /*
                                 * Sharepoint already in list,
                                 * update its state.
                                 */
                                sep->se_state = lep->le_state;
                        } else {
                                /*
                                 * Need to add to sharepoint list
                                 */
                                sep = (struct sharepnt_ent *)
                                        malloc(sizeof (*sep));
                                if (sep == NULL) {
                                        error = ENOMEM;
                                        goto errout;
                                }
                                (void) memset(sep, 0, sizeof (*sep));

                                sep->se_name = strdup(lep->le_path);
                                if (sep->se_name == NULL) {
                                        error = ENOMEM;
                                        goto errout;
                                }
                                sep->se_state = lep->le_state;

                                assert(se_tail != NULL);
                                assert(se_tail->se_next == NULL);
                                se_tail->se_next = sep;
                        }
                } else {
                        /*
                         * Add new buffer to list
                         */
                        bep = (struct buffer_ent *)malloc(sizeof (*bep));
                        if (bep == NULL) {
                                error = ENOMEM;
                                goto errout;
                        }
                        (void) memset(bep, 0, sizeof (*bep));

                        bep->be_name = strdup(lep->le_buffer);
                        if (bep->be_name == NULL) {
                                error = ENOMEM;
                                goto errout;
                        }

                        if (*be_head == NULL)
                                *be_head = bep;
                        else
                                be_tail->be_next = bep;
                        be_tail = bep;

                        bep->be_sharepnt = (struct sharepnt_ent *)
                                malloc(sizeof (*(bep->be_sharepnt)));
                        (void) memset(bep->be_sharepnt, 0,
                                sizeof (*(bep->be_sharepnt)));

                        if (bep->be_sharepnt == NULL) {
                                error = ENOMEM;
                                goto errout;
                        }
                        bep->be_sharepnt->se_name = strdup(lep->le_path);
                        if (bep->be_sharepnt->se_name == NULL) {
                                error = ENOMEM;
                                goto errout;
                        }
                        bep->be_sharepnt->se_state = lep->le_state;
                }
        }

        if (res < 0) {
                error = EIO;
                goto errout;
        }

        /*
         * Get modification time while we have the file locked.
         */
        if (lu) {
                if ((error = fstat(fileno(fd), &st)) == -1) {
                        syslog(LOG_ERR, gettext("Can't stat %s"), NFSLOGTAB);
                        goto errout;
                }
                *lu = st.st_mtim;
        }

        (void) fclose(fd);
        return (error);

errout:
        (void) fclose(fd);
        if (lep)
                logtab_ent_free(lep);
        free_buffer_list(be_head);
        assert(*be_head == NULL);
        syslog(LOG_ERR, gettext("cannot read %s: %s\n"), NFSLOGTAB,
                strerror(error));

        return (error);
}

/*
 * Removes the entry from the buffer list and frees it.
 */
void
remove_buffer_ent(struct buffer_ent **be_listpp, struct buffer_ent *bep)
{
        struct buffer_ent *p, *prev;

        for (p = prev = *be_listpp; p != NULL; p = p->be_next) {
                if (p == bep) {
                        if (p == *be_listpp)
                                *be_listpp = (*be_listpp)->be_next;
                        else
                                prev->be_next = bep->be_next;
                        free_buffer_ent(bep);
                        break;
                }
                prev = p;
        }
}

/*
 * Frees the buffer list.
 */
void
free_buffer_list(struct buffer_ent **be_listpp)
{
        struct buffer_ent *bep, *nextp;

        for (bep = *be_listpp; bep != NULL; bep = nextp) {
                nextp = bep->be_next;
                free_buffer_ent(bep);
        }
        *be_listpp = NULL;
}

static void
free_buffer_ent(struct buffer_ent *bep)
{
        assert(bep != NULL);
        if (debug)
                (void) printf("freeing %s\n", bep->be_name);
        if (bep->be_name != NULL)
                free(bep->be_name);
        if (bep->be_sharepnt != NULL)
                free_sharepnt_list(bep->be_sharepnt);
        free(bep);
}

static void
free_sharepnt_list(struct sharepnt_ent *sep_listp)
{
        struct sharepnt_ent *nextp;

        for (; sep_listp != NULL; sep_listp = nextp) {
                nextp = sep_listp->se_next;
                free_sharepnt_ent(sep_listp);
        }
        free(sep_listp);
}

/*
 * Removes the entry from the sharepnt list and frees it.
 */
void
remove_sharepnt_ent(struct sharepnt_ent **se_listpp, struct sharepnt_ent *sep)
{
        struct sharepnt_ent *p, *prev;

        for (p = prev = *se_listpp; p != NULL; p = p->se_next) {
                if (p == sep) {
                        if (p == *se_listpp)
                                *se_listpp = (*se_listpp)->se_next;
                        else
                                prev->se_next = sep->se_next;
                        free_sharepnt_ent(sep);
                        break;
                }
                prev = p;
        }
}

static void
free_sharepnt_ent(struct sharepnt_ent *sep)
{
        assert(sep != NULL);
        if (debug)
                (void) printf("freeing %s\n", sep->se_name);
        if (sep->se_name != NULL)
                free(sep->se_name);
        free(sep);
}

#ifdef DEBUG
void
printbuffer_list(struct buffer_ent *bep)
{
        for (; bep != NULL; bep = bep->be_next) {
                (void) printf("%s\n", bep->be_name);
                if (bep->be_sharepnt != NULL)
                        print_sharepnt_list(bep->be_sharepnt);
        }
}

static void
print_sharepnt_list(struct sharepnt_ent *sep)
{
        for (; sep != NULL; sep = sep->se_next)
                (void) printf("\t(%d) %s\n", sep->se_state, sep->se_name);
}
#endif

/*
 * Returns a pointer to the buffer matching 'name', NULL otherwise.
 */
static struct buffer_ent *
findbuffer(struct buffer_ent *bep, char *name)
{
        for (; bep != NULL; bep = bep->be_next) {
                if (strcmp(bep->be_name, name) == 0)
                        return (bep);
        }
        return (NULL);
}

/*
 * Returns a pointer the sharepoint entry matching 'name'.
 * Otherwise, it sets '*se_tail' to the last element of the list
 * to make insertion of new element easier, and returns NULL.
 */
static struct sharepnt_ent *
findsharepnt(
        struct sharepnt_ent *sep,
        char *name,
        struct sharepnt_ent **se_tail)
{
        struct sharepnt_ent *tail;

        for (; sep != NULL; sep = sep->se_next) {
                if (strcmp(sep->se_name, name) == 0)
                        return (sep);
                tail = sep;
        }
        *se_tail = tail;
        return (NULL);
}