1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27#include <assert.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31#include <errno.h>
32#include <sys/param.h>
33#include "startd.h"
34
35/*
36 * The service deathrow mechanism addresses the problem of removing services
37 * from a non accessible SMF repository. In this case, we can't simply use the
38 * "SVCCFG_REPOSITORY=$ROOT/etc/svc/repository.db svccfg delete service_fmri"
39 * command as the alternate repository format is not committed and could be
40 * incompatible with the local SMF commands version.
41 *
42 * The idea is to manage a file (/etc/svc/deathrow) on the alternate root
43 * directory that lists the FMRIs that need to disappear from the repository
44 * when the system that uses this root directory boots up.
45 * r.manifest and i.manifest update the file /etc/svc/deathrow in the alternate
46 * root case.
47 *
48 * When svc.startd daemon launches, it first reads the /etc/svc/deathrow file
49 * and for all FMRIs listed in this file, the service is not configured and
50 * dependencies on it are forced satisfied (during svc.startd init time only).
51 *
52 * Than manifest-import service will actually, as first task, delete the
53 * unconfigured services found in the /etc/svc/deathrow file and the
54 * manifest hash entry from the repository.
55 *
56 */
57
58#define	SVC_DEATHROW_FILE	"/etc/svc/deathrow"
59
60/*
61 * These data structures are unprotected because they
62 * are modified by a single thread, at startup time.
63 * After initialization, these data structures are
64 * used only in read mode, thus requiring no protection.
65 */
66
67/* list of deathrow fmris, created from the file SVC_DEATHROW_FILE */
68typedef struct deathrow {
69    char *fmri;
70    uu_list_node_t deathrow_link;
71} deathrow_t;
72
73static uu_list_pool_t *deathrow_pool;
74static uu_list_t *deathrow_list;
75
76static boolean_t deathrow_handling_status = B_FALSE;
77
78static deathrow_t *fmri_in_deathrow_internal(const char *);
79static void deathrow_add(const char *);
80
81static void
82deathrow_handling_start()
83{
84	assert(deathrow_handling_status == B_FALSE);
85	deathrow_handling_status = B_TRUE;
86}
87
88static void
89deathrow_handling_stop()
90{
91	assert(deathrow_handling_status == B_TRUE);
92	deathrow_handling_status = B_FALSE;
93}
94
95void
96deathrow_init()
97{
98	FILE *file;
99	char *line;
100	char *fmri;
101	char *manifest;
102	char *pkgname;
103	size_t line_size, sz;
104	unsigned int line_parsed = 0;
105
106	log_framework(LOG_DEBUG, "Deathrow init\n");
107
108	while ((file = fopen(SVC_DEATHROW_FILE, "r")) == NULL) {
109		if (errno == EINTR) {
110			continue;
111		}
112		if (errno != ENOENT) {
113			log_framework(LOG_ERR,
114			    "Deathrow not processed. "
115			    "Error opening file (%s): %s\n",
116			    SVC_DEATHROW_FILE, strerror(errno));
117		}
118		return;
119	}
120
121	deathrow_pool = uu_list_pool_create("deathrow",
122	    sizeof (deathrow_t), offsetof(deathrow_t, deathrow_link),
123	    NULL, UU_LIST_POOL_DEBUG);
124	if (deathrow_pool == NULL) {
125		uu_die("deathrow_init couldn't create deathrow_pool");
126	}
127
128	deathrow_list = uu_list_create(deathrow_pool,  deathrow_list, 0);
129	if (deathrow_list == NULL) {
130		uu_die("deathrow_init couldn't create deathrow_list");
131	}
132
133	/*
134	 * A deathrow file line looks like:
135	 * <fmri>< ><manifest path>< ><package name><\n>
136	 * (field separator is a space character)
137	 */
138	line_size = max_scf_fmri_size + 3 + MAXPATHLEN + MAXNAMELEN;
139	line = (char *)startd_alloc(line_size);
140	*line = '\0';
141
142	while (fgets(line, line_size, file) != NULL) {
143		line_parsed++;
144		fmri = NULL;
145		manifest = NULL;
146		pkgname = NULL;
147		sz = strlen(line);
148		if (sz > 0) {
149			/* remove linefeed */
150			if (line[sz - 1] == '\n') {
151				line[sz - 1] = '\0';
152			}
153			manifest = strchr(line, ' ');
154			if (manifest != NULL) {
155				fmri = line;
156				*manifest = '\0';
157				manifest++;
158				pkgname = strchr(manifest, ' ');
159				if (pkgname != NULL) {
160					*pkgname = '\0';
161					pkgname++;
162				}
163			}
164		}
165		if (fmri != NULL && strlen(fmri) > 0 &&
166		    strlen(fmri) < max_scf_fmri_size &&
167		    manifest != NULL && strlen(manifest) > 0 &&
168		    pkgname != NULL && strlen(pkgname) > 0) {
169			log_framework(LOG_DEBUG,
170			    "Deathrow parser <%s><%s><%s>\n",
171			    fmri, manifest, pkgname);
172			if (fmri_in_deathrow_internal(fmri) == NULL) {
173				/* fmri is not in list, add fmri */
174				deathrow_add(fmri);
175			}
176		} else {
177			log_framework(LOG_ERR,
178			    "Deathrow error processing file (%s). "
179			    "Skipping line %u.\n",
180			    SVC_DEATHROW_FILE, line_parsed);
181		}
182		*line = '\0';
183	}
184	startd_free(line, line_size);
185	(void) fclose(file);
186
187	if (uu_list_first(deathrow_list) != NULL) {
188		deathrow_handling_start();
189	}
190}
191
192void
193deathrow_fini()
194{
195	deathrow_t *d;
196	void *cookie = NULL;
197
198	if (deathrow_handling_status == B_FALSE) {
199		log_framework(LOG_DEBUG, "Deathrow fini\n");
200		return;
201	}
202	deathrow_handling_stop();
203
204	while ((d = uu_list_teardown(deathrow_list, &cookie)) != NULL) {
205		startd_free(d->fmri, strlen(d->fmri) + 1);
206		startd_free(d, sizeof (deathrow_t));
207	}
208
209	uu_list_destroy(deathrow_list);
210	uu_list_pool_destroy(deathrow_pool);
211	deathrow_pool = NULL;
212	deathrow_list = NULL;
213	log_framework(LOG_DEBUG, "Deathrow fini\n");
214}
215
216static void
217deathrow_add(const char *fmri)
218{
219	deathrow_t *d;
220
221	assert(fmri != NULL);
222
223	d = startd_alloc(sizeof (deathrow_t));
224	d->fmri = startd_alloc(strlen(fmri) + 1);
225	(void) strcpy(d->fmri, fmri);
226	uu_list_node_init(d, &d->deathrow_link, deathrow_pool);
227	(void) uu_list_insert_after(deathrow_list, NULL, d);
228
229	log_framework(LOG_DEBUG, "Deathrow added <%s>\n", d->fmri);
230}
231
232static deathrow_t *
233fmri_in_deathrow_internal(const char *fmri)
234{
235	deathrow_t *d;
236
237	assert(fmri != NULL);
238	assert(deathrow_pool != NULL);
239	assert(deathrow_list != NULL);
240
241	for ((d = uu_list_first(deathrow_list)); d != NULL;
242	    d = uu_list_next(deathrow_list, d)) {
243		if (strcmp(fmri, d->fmri) == 0) {
244			return (d);
245		}
246	}
247	return (NULL);
248}
249
250boolean_t
251is_fmri_in_deathrow(const char *fmri)
252{
253	if (deathrow_handling_status == B_FALSE) {
254		return (B_FALSE);
255	}
256	return ((fmri_in_deathrow_internal(fmri) != NULL) ? B_TRUE : B_FALSE);
257}
258