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
deathrow_handling_start()82 deathrow_handling_start()
83 {
84 assert(deathrow_handling_status == B_FALSE);
85 deathrow_handling_status = B_TRUE;
86 }
87
88 static void
deathrow_handling_stop()89 deathrow_handling_stop()
90 {
91 assert(deathrow_handling_status == B_TRUE);
92 deathrow_handling_status = B_FALSE;
93 }
94
95 void
deathrow_init()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
deathrow_fini()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
deathrow_add(const char * fmri)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 *
fmri_in_deathrow_internal(const char * fmri)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
is_fmri_in_deathrow(const char * fmri)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