root/usr/src/cmd/svc/startd/deathrow.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/param.h>
#include "startd.h"

/*
 * The service deathrow mechanism addresses the problem of removing services
 * from a non accessible SMF repository. In this case, we can't simply use the
 * "SVCCFG_REPOSITORY=$ROOT/etc/svc/repository.db svccfg delete service_fmri"
 * command as the alternate repository format is not committed and could be
 * incompatible with the local SMF commands version.
 *
 * The idea is to manage a file (/etc/svc/deathrow) on the alternate root
 * directory that lists the FMRIs that need to disappear from the repository
 * when the system that uses this root directory boots up.
 * r.manifest and i.manifest update the file /etc/svc/deathrow in the alternate
 * root case.
 *
 * When svc.startd daemon launches, it first reads the /etc/svc/deathrow file
 * and for all FMRIs listed in this file, the service is not configured and
 * dependencies on it are forced satisfied (during svc.startd init time only).
 *
 * Than manifest-import service will actually, as first task, delete the
 * unconfigured services found in the /etc/svc/deathrow file and the
 * manifest hash entry from the repository.
 *
 */

#define SVC_DEATHROW_FILE       "/etc/svc/deathrow"

/*
 * These data structures are unprotected because they
 * are modified by a single thread, at startup time.
 * After initialization, these data structures are
 * used only in read mode, thus requiring no protection.
 */

/* list of deathrow fmris, created from the file SVC_DEATHROW_FILE */
typedef struct deathrow {
    char *fmri;
    uu_list_node_t deathrow_link;
} deathrow_t;

static uu_list_pool_t *deathrow_pool;
static uu_list_t *deathrow_list;

static boolean_t deathrow_handling_status = B_FALSE;

static deathrow_t *fmri_in_deathrow_internal(const char *);
static void deathrow_add(const char *);

static void
deathrow_handling_start()
{
        assert(deathrow_handling_status == B_FALSE);
        deathrow_handling_status = B_TRUE;
}

static void
deathrow_handling_stop()
{
        assert(deathrow_handling_status == B_TRUE);
        deathrow_handling_status = B_FALSE;
}

void
deathrow_init()
{
        FILE *file;
        char *line;
        char *fmri;
        char *manifest;
        char *pkgname;
        size_t line_size, sz;
        unsigned int line_parsed = 0;

        log_framework(LOG_DEBUG, "Deathrow init\n");

        while ((file = fopen(SVC_DEATHROW_FILE, "r")) == NULL) {
                if (errno == EINTR) {
                        continue;
                }
                if (errno != ENOENT) {
                        log_framework(LOG_ERR,
                            "Deathrow not processed. "
                            "Error opening file (%s): %s\n",
                            SVC_DEATHROW_FILE, strerror(errno));
                }
                return;
        }

        deathrow_pool = uu_list_pool_create("deathrow",
            sizeof (deathrow_t), offsetof(deathrow_t, deathrow_link),
            NULL, UU_LIST_POOL_DEBUG);
        if (deathrow_pool == NULL) {
                uu_die("deathrow_init couldn't create deathrow_pool");
        }

        deathrow_list = uu_list_create(deathrow_pool,  deathrow_list, 0);
        if (deathrow_list == NULL) {
                uu_die("deathrow_init couldn't create deathrow_list");
        }

        /*
         * A deathrow file line looks like:
         * <fmri>< ><manifest path>< ><package name><\n>
         * (field separator is a space character)
         */
        line_size = max_scf_fmri_size + 3 + MAXPATHLEN + MAXNAMELEN;
        line = (char *)startd_alloc(line_size);
        *line = '\0';

        while (fgets(line, line_size, file) != NULL) {
                line_parsed++;
                fmri = NULL;
                manifest = NULL;
                pkgname = NULL;
                sz = strlen(line);
                if (sz > 0) {
                        /* remove linefeed */
                        if (line[sz - 1] == '\n') {
                                line[sz - 1] = '\0';
                        }
                        manifest = strchr(line, ' ');
                        if (manifest != NULL) {
                                fmri = line;
                                *manifest = '\0';
                                manifest++;
                                pkgname = strchr(manifest, ' ');
                                if (pkgname != NULL) {
                                        *pkgname = '\0';
                                        pkgname++;
                                }
                        }
                }
                if (fmri != NULL && strlen(fmri) > 0 &&
                    strlen(fmri) < max_scf_fmri_size &&
                    manifest != NULL && strlen(manifest) > 0 &&
                    pkgname != NULL && strlen(pkgname) > 0) {
                        log_framework(LOG_DEBUG,
                            "Deathrow parser <%s><%s><%s>\n",
                            fmri, manifest, pkgname);
                        if (fmri_in_deathrow_internal(fmri) == NULL) {
                                /* fmri is not in list, add fmri */
                                deathrow_add(fmri);
                        }
                } else {
                        log_framework(LOG_ERR,
                            "Deathrow error processing file (%s). "
                            "Skipping line %u.\n",
                            SVC_DEATHROW_FILE, line_parsed);
                }
                *line = '\0';
        }
        startd_free(line, line_size);
        (void) fclose(file);

        if (uu_list_first(deathrow_list) != NULL) {
                deathrow_handling_start();
        }
}

void
deathrow_fini()
{
        deathrow_t *d;
        void *cookie = NULL;

        if (deathrow_handling_status == B_FALSE) {
                log_framework(LOG_DEBUG, "Deathrow fini\n");
                return;
        }
        deathrow_handling_stop();

        while ((d = uu_list_teardown(deathrow_list, &cookie)) != NULL) {
                startd_free(d->fmri, strlen(d->fmri) + 1);
                startd_free(d, sizeof (deathrow_t));
        }

        uu_list_destroy(deathrow_list);
        uu_list_pool_destroy(deathrow_pool);
        deathrow_pool = NULL;
        deathrow_list = NULL;
        log_framework(LOG_DEBUG, "Deathrow fini\n");
}

static void
deathrow_add(const char *fmri)
{
        deathrow_t *d;

        assert(fmri != NULL);

        d = startd_alloc(sizeof (deathrow_t));
        d->fmri = startd_alloc(strlen(fmri) + 1);
        (void) strcpy(d->fmri, fmri);
        uu_list_node_init(d, &d->deathrow_link, deathrow_pool);
        (void) uu_list_insert_after(deathrow_list, NULL, d);

        log_framework(LOG_DEBUG, "Deathrow added <%s>\n", d->fmri);
}

static deathrow_t *
fmri_in_deathrow_internal(const char *fmri)
{
        deathrow_t *d;

        assert(fmri != NULL);
        assert(deathrow_pool != NULL);
        assert(deathrow_list != NULL);

        for ((d = uu_list_first(deathrow_list)); d != NULL;
            d = uu_list_next(deathrow_list, d)) {
                if (strcmp(fmri, d->fmri) == 0) {
                        return (d);
                }
        }
        return (NULL);
}

boolean_t
is_fmri_in_deathrow(const char *fmri)
{
        if (deathrow_handling_status == B_FALSE) {
                return (B_FALSE);
        }
        return ((fmri_in_deathrow_internal(fmri) != NULL) ? B_TRUE : B_FALSE);
}