/*
* 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);
}