xref: /illumos-gate/usr/src/cmd/svc/startd/deathrow.c (revision 3d393ee6c37fa10ac512ed6d36109ad616dc7c1a)
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 */
68 typedef struct deathrow {
69     char *fmri;
70     uu_list_node_t deathrow_link;
71 } deathrow_t;
72 
73 static uu_list_pool_t *deathrow_pool;
74 static uu_list_t *deathrow_list;
75 
76 static boolean_t deathrow_handling_status = B_FALSE;
77 
78 static deathrow_t *fmri_in_deathrow_internal(const char *);
79 static void deathrow_add(const char *);
80 
81 static void
82 deathrow_handling_start()
83 {
84 	assert(deathrow_handling_status == B_FALSE);
85 	deathrow_handling_status = B_TRUE;
86 }
87 
88 static void
89 deathrow_handling_stop()
90 {
91 	assert(deathrow_handling_status == B_TRUE);
92 	deathrow_handling_status = B_FALSE;
93 }
94 
95 void
96 deathrow_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 
192 void
193 deathrow_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 
216 static void
217 deathrow_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 
232 static deathrow_t *
233 fmri_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 
250 boolean_t
251 is_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