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, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 * Creates and maintains a short-term cache of live upgrade slices.
31 */
32
33#include <dirent.h>
34#include <stdlib.h>
35#include <stdio.h>
36#include <string.h>
37#include <synch.h>
38#include <sys/errno.h>
39#include <sys/param.h>
40#include <sys/types.h>
41#include <sys/stat.h>
42#include <unistd.h>
43#include <sys/types.h>
44#include <sys/wait.h>
45
46#include "libdiskmgt.h"
47#include "disks_private.h"
48
49#define	TMPNM_SIZE	25
50
51/*
52 * The list of live upgrade slices in use.
53 */
54
55struct lu_list {
56	struct lu_list	*next;
57	char		*slice;
58	char		*name;
59};
60
61static struct lu_list	*lu_listp = NULL;
62static time_t		timestamp = 0;
63static mutex_t		lu_lock = DEFAULTMUTEX;
64
65static int		add_use_record(char *devname, char *name);
66static void		free_lu(struct lu_list *listp);
67static int		load_lu();
68static int		lustatus(int fd);
69static int		lufslist(int fd);
70static int		run_cmd(char *path, char *cmd, char *arg, int fd);
71
72/*
73 * Search the list of devices under live upgrade for the specified device.
74 */
75int
76inuse_lu(char *slice, nvlist_t *attrs, int *errp)
77{
78	int		found = 0;
79	time_t		curr_time;
80
81	*errp = 0;
82
83	if (slice == NULL) {
84	    return (found);
85	}
86
87	/*
88	 * We don't want to have to re-read the live upgrade config for
89	 * every slice, but we can't just cache it since there is no event
90	 * when this changes.  So, we'll keep the config in memory for
91	 * a short time (1 minute) before reloading it.
92	 */
93	(void) mutex_lock(&lu_lock);
94
95	curr_time = time(NULL);
96	if (timestamp < curr_time && (curr_time - timestamp) > 60) {
97	    free_lu(lu_listp);	/* free old entries */
98	    lu_listp = NULL;
99	    *errp = load_lu();	/* load the cache */
100	    timestamp = curr_time;
101	}
102
103	if (*errp == 0) {
104	    struct lu_list	*listp;
105
106	    listp = lu_listp;
107	    while (listp != NULL) {
108		if (strcmp(slice, listp->slice) == 0) {
109		    libdiskmgt_add_str(attrs, DM_USED_BY, DM_USE_LU, errp);
110		    libdiskmgt_add_str(attrs, DM_USED_NAME, listp->name, errp);
111		    found = 1;
112		    break;
113		}
114		listp = listp->next;
115	    }
116	}
117
118	(void) mutex_unlock(&lu_lock);
119
120	return (found);
121}
122
123static int
124add_use_record(char *devname, char *name)
125{
126	struct lu_list *sp;
127
128	sp = (struct lu_list *)malloc(sizeof (struct lu_list));
129	if (sp == NULL) {
130	    return (ENOMEM);
131	}
132
133	if ((sp->slice = strdup(devname)) == NULL) {
134	    free(sp);
135	    return (ENOMEM);
136	}
137
138	if ((sp->name = strdup(name)) == NULL) {
139	    free(sp->slice);
140	    free(sp);
141	    return (ENOMEM);
142	}
143
144	sp->next = lu_listp;
145	lu_listp = sp;
146
147	return (0);
148}
149
150/*
151 * Free the list of liveupgrade entries.
152 */
153static void
154free_lu(struct lu_list *listp) {
155
156	struct lu_list	*nextp;
157
158	while (listp != NULL) {
159	    nextp = listp->next;
160	    free((void *)listp->slice);
161	    free((void *)listp->name);
162	    free((void *)listp);
163	    listp = nextp;
164	}
165}
166
167/*
168 * Create a list of live upgrade devices.
169 */
170static int
171load_lu()
172{
173	char	tmpname[TMPNM_SIZE];
174	int	fd;
175	int	status = 0;
176
177	(void) strlcpy(tmpname, "/var/run/dm_lu_XXXXXX", TMPNM_SIZE);
178	if ((fd = mkstemp(tmpname)) != -1) {
179	    (void) unlink(tmpname);
180	    if (run_cmd("/usr/sbin/lustatus", "lustatus", NULL, fd)) {
181		status = lustatus(fd);
182	    } else {
183		(void) close(fd);
184	    }
185	}
186
187	return (status);
188}
189
190/*
191 * The XML generated by the live upgrade commands is not parseable by the
192 * standard Solaris XML parser, so we have to do it ourselves.
193 */
194static int
195lufslist(int fd)
196{
197	FILE	*fp;
198	char	line[MAXPATHLEN];
199	int	status;
200
201	if ((fp = fdopen(fd, "r")) == NULL) {
202	    (void) close(fd);
203	    return (0);
204	}
205
206	(void) fseek(fp, 0L, SEEK_SET);
207	while (fgets(line, sizeof (line), fp) == line) {
208	    char *devp;
209	    char *nmp;
210	    char *ep;
211
212	    if (strncmp(line, "<beFsComponent ", 15) != 0) {
213		continue;
214	    }
215
216	    if ((devp = strstr(line, "fsDevice=\"")) == NULL) {
217		continue;
218	    }
219
220	    devp = devp + 10;
221
222	    if ((ep = strchr(devp, '"')) == NULL) {
223		continue;
224	    }
225
226	    *ep = 0;
227
228	    /* try to get the mountpoint name */
229	    if ((nmp = strstr(ep + 1, "mountPoint=\"")) != NULL) {
230		nmp = nmp + 12;
231
232		if ((ep = strchr(nmp, '"')) != NULL) {
233		    *ep = 0;
234		} else {
235		    nmp = "";
236		}
237
238	    } else {
239		nmp = "";
240	    }
241
242	    if ((status = add_use_record(devp, nmp)) != 0) {
243		break;
244	    }
245	}
246
247	(void) fclose(fp);
248
249	return (status);
250}
251
252static int
253lustatus(int fd)
254{
255	FILE	*fp;
256	char	line[MAXPATHLEN];
257	int	status = 0;
258
259	if ((fp = fdopen(fd, "r")) == NULL) {
260	    (void) close(fd);
261	    return (0);
262	}
263
264	(void) fseek(fp, 0L, SEEK_SET);
265	while (fgets(line, sizeof (line), fp) == line) {
266	    char	*sp;
267	    char	*ep;
268	    char	tmpname[TMPNM_SIZE];
269	    int		ffd;
270
271	    if (strncmp(line, "<beStatus ", 10) != 0) {
272		continue;
273	    }
274
275	    if ((sp = strstr(line, "name=\"")) == NULL) {
276		continue;
277	    }
278
279	    sp = sp + 6;
280
281	    if ((ep = strchr(sp, '"')) == NULL) {
282		continue;
283	    }
284
285	    *ep = 0;
286
287	    (void) strlcpy(tmpname, "/var/run/dm_lu_XXXXXX", TMPNM_SIZE);
288	    if ((ffd = mkstemp(tmpname)) != -1) {
289		(void) unlink(tmpname);
290
291		if (run_cmd("/usr/sbin/lufslist", "lufslist", sp, ffd) == 0) {
292		    (void) close(ffd);
293		    break;
294		}
295
296		if ((status = lufslist(ffd)) != 0) {
297		    break;
298		}
299	    }
300	}
301
302	(void) fclose(fp);
303
304	return (status);
305}
306
307static int
308run_cmd(char *path, char *cmd, char *arg, int fd)
309{
310	pid_t	pid;
311	int	loc;
312
313	/* create the server process */
314	switch ((pid = fork1())) {
315	case 0:
316	    /* child process */
317	    (void) close(1);
318	    (void) dup(fd);
319	    (void) close(2);
320	    (void) dup(fd);
321	    closefrom(3);
322	    (void) execl(path, cmd, "-X", arg, NULL);
323	    _exit(1);
324	    break;
325
326	case -1:
327	    return (0);
328
329	default:
330	    /* parent process */
331	    break;
332	}
333
334	(void) waitpid(pid, &loc, 0);
335
336	/* printf("got 0x%x %d %d\n", loc, WIFEXITED(loc), WEXITSTATUS(loc)); */
337
338	if (WIFEXITED(loc) && WEXITSTATUS(loc) == 0) {
339	    return (1);
340	}
341
342	return (0);
343}
344