xref: /illumos-gate/usr/src/lib/libpkg/common/pkgserv.c (revision e753f464d28e02e23aa93bd7d51d39fc56f79897)
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 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <pkglib.h>
28 
29 #include <alloca.h>
30 #include <assert.h>
31 #include <door.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <pthread.h>
35 #include <spawn.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <strings.h>
39 #include <sys/mman.h>
40 #include <sys/param.h>
41 #include <sys/stat.h>
42 #include <sys/wait.h>
43 #include <unistd.h>
44 #include <libintl.h>
45 
46 #define	PKGADD_MAX	(512 * 1024)
47 
48 #define	SADM_DIR	"/var/sadm/install"
49 
50 #define	PKGSERV_PATH	"/usr/sadm/install/bin/pkgserv"
51 
52 #define	ERR_PATH_TOO_BIG	"alternate root path is too long"
53 #define	ERR_OPEN_DOOR		"cannot open pkgserv door"
54 #define	ERR_START_SERVER	"cannot start pkgserv daemon: %s"
55 #define	ERR_START_FILTER	"cannot enumerate database entries"
56 
57 struct pkg_server {
58 	FILE		*fp;
59 	char		*curbuf;
60 	int		buflen;
61 	int		door;
62 	boolean_t	onetime;
63 };
64 
65 static PKGserver current_server;
66 
67 static start_mode_t defmode = INVALID;
68 static boolean_t registered = B_FALSE;
69 static pid_t master_pid = -1;
70 
71 static void
72 pkgfilename(char path[PATH_MAX], const char *root, const char *sadmdir,
73     const char *file)
74 {
75 	if (snprintf(path, PATH_MAX, "%s%s/%s", root == NULL ? "" : root,
76 	    sadmdir == NULL ? SADM_DIR : sadmdir, file) >= PATH_MAX) {
77 		progerr(gettext(ERR_PATH_TOO_BIG));
78 		exit(99);
79 	}
80 }
81 
82 static void
83 pkgexit_close(void)
84 {
85 	if (current_server != NULL)
86 		pkgcloseserver(current_server);
87 }
88 
89 static PKGserver
90 pkgopenserver_i(const char *root, const char *sadmdir, boolean_t readonly,
91 	start_mode_t mode)
92 {
93 	PKGserver server;
94 	struct door_info di;
95 	pid_t pid;
96 	int stat;
97 	int first = B_TRUE;
98 	char *cmd[16];
99 	int args;
100 	char pkgdoor[PATH_MAX];
101 	extern char **environ;
102 	char *prog;
103 	char pidbuf[12];
104 
105 	if (current_server != NULL)
106 		return (current_server);
107 
108 	if (!registered) {
109 		registered = B_TRUE;
110 		(void) atexit(pkgexit_close);
111 	}
112 	if (readonly) {
113 		int fd;
114 
115 		(void) strcpy(pkgdoor, "/tmp/pkgdoor.XXXXXX");
116 		if ((fd = mkstemp(pkgdoor)) < 0) {
117 			progerr(gettext(ERR_OPEN_DOOR));
118 			return (NULL);
119 		}
120 		(void) close(fd);
121 	} else {
122 		pkgfilename(pkgdoor, root, sadmdir, PKGDOOR);
123 	}
124 
125 	server = malloc(sizeof (*server));
126 
127 	if (server == NULL)
128 		goto return_null;
129 
130 	server->fp = NULL;
131 	server->onetime = readonly;
132 
133 openserver:
134 	server->door = open(pkgdoor, O_RDWR);
135 
136 	if (server->door >= 0) {
137 		if (door_info(server->door, &di) == 0 && di.di_target >= 0) {
138 			pkgcmd_t n;
139 			n.cmd = PKG_NOP;
140 			server->buflen = 1024;
141 			server->curbuf = malloc(1024);
142 			if (server->curbuf == NULL ||
143 			    pkgcmd(server, &n, sizeof (n), NULL, NULL, NULL)) {
144 				pkgcloseserver(server);
145 				return (NULL);
146 			}
147 			return (current_server = server);
148 		}
149 
150 		(void) close(server->door);
151 	}
152 
153 	if (!first || mode == NEVER)
154 		goto return_null;
155 
156 	first = B_FALSE;
157 
158 	args = 0;
159 	cmd[args++] = strrchr(PKGSERV_PATH, '/') + 1;
160 	if (root != NULL && strcmp(root, "/") != 0) {
161 		cmd[args++] = "-R";
162 		cmd[args++] = (char *)root;
163 	}
164 	if (sadmdir != NULL && strcmp(sadmdir, SADM_DIR) != 0) {
165 		cmd[args++] = "-d";
166 		cmd[args++] = (char *)sadmdir;
167 	}
168 	if (readonly) {
169 		cmd[args++] = "-r";
170 		cmd[args++] = pkgdoor;
171 	}
172 	prog = get_prog_name();
173 	if (prog != NULL) {
174 		cmd[args++] = "-N";
175 		cmd[args++] = prog;
176 	}
177 
178 	switch (mode) {
179 	case FLUSH_LOG:
180 		cmd[args++] = "-e";
181 		break;
182 	case RUN_ONCE:
183 		cmd[args++] = "-o";
184 		break;
185 	case PERMANENT:
186 		cmd[args++] = "-p";
187 		break;
188 	default:
189 		break;
190 	}
191 
192 	if (master_pid != -1) {
193 		cmd[args++] = "-P";
194 		(void) snprintf(pidbuf, sizeof (pidbuf), "%d", master_pid);
195 		cmd[args++] = pidbuf;
196 	}
197 	cmd[args++] = NULL;
198 	assert(args <= sizeof (cmd)/sizeof (char *));
199 
200 	if (posix_spawn(&pid, PKGSERV_PATH, NULL, NULL, cmd, environ) == 0) {
201 		server->onetime |= mode == RUN_ONCE;
202 		while (wait4(pid, &stat, 0, NULL) != -1) {
203 			if (WIFEXITED(stat)) {
204 				int s = WEXITSTATUS(stat);
205 				if (s == 0 || s == 1)
206 					if (mode == FLUSH_LOG)
207 						goto return_null;
208 					else
209 						goto openserver;
210 				if (s == 2)
211 					goto return_null;
212 				break;
213 			} else if (WIFSIGNALED(stat)) {
214 				break;
215 			}
216 		}
217 	}
218 
219 	progerr(gettext(ERR_START_SERVER), strerror(errno));
220 
221 return_null:
222 	if (readonly)
223 		(void) unlink(pkgdoor);
224 	free(server);
225 	return (NULL);
226 }
227 
228 PKGserver
229 pkgopenserver(const char *root, const char *sadmdir, boolean_t ro)
230 {
231 	return (pkgopenserver_i(root, sadmdir, ro, pkgservergetmode()));
232 }
233 
234 start_mode_t
235 pkgparsemode(const char *mode)
236 {
237 	if (strcasecmp(mode, MODE_PERMANENT) == 0) {
238 		return (PERMANENT);
239 	} else if (strncasecmp(mode, MODE_TIMEOUT,
240 	    sizeof (MODE_TIMEOUT) - 1) == 0) {
241 		const char *pidstr = mode + sizeof (MODE_TIMEOUT) - 1;
242 		if (pidstr[0] != '\0') {
243 			master_pid = atoi(pidstr);
244 			if (master_pid <= 1 || kill(master_pid, 0) != 0)
245 				master_pid = -1;
246 		}
247 
248 		return (TIMEOUT);
249 	} else if (strcasecmp(mode, MODE_RUN_ONCE) == 0) {
250 		return (RUN_ONCE);
251 	} else {
252 		progerr(gettext("invalid pkgserver mode: %s"), mode);
253 		exit(99);
254 		/*NOTREACHED*/
255 	}
256 }
257 
258 char *
259 pkgmodeargument(start_mode_t mode)
260 {
261 	static char timebuf[sizeof (PKGSERV_MODE) + sizeof (MODE_TIMEOUT) + 10];
262 
263 	switch (mode) {
264 	case PERMANENT:
265 		return (PKGSERV_MODE MODE_PERMANENT);
266 	case TIMEOUT:
267 		(void) snprintf(timebuf, sizeof (timebuf),
268 		    PKGSERV_MODE MODE_TIMEOUT "%d",
269 		    (master_pid > 1 && kill(master_pid, 0) == 0) ? master_pid :
270 		    getpid());
271 		return (timebuf);
272 	case RUN_ONCE:
273 		return (PKGSERV_MODE MODE_RUN_ONCE);
274 	}
275 	progerr(gettext("Bad pkgserv mode: %d"), (int)mode);
276 	exit(99);
277 }
278 
279 void
280 pkgserversetmode(start_mode_t mode)
281 {
282 	if (mode == DEFAULTMODE || mode == INVALID) {
283 		char *var = getenv(SUNW_PKG_SERVERMODE);
284 
285 		if (var != NULL)
286 			defmode = pkgparsemode(var);
287 		else
288 			defmode = DEFAULTMODE;
289 	} else {
290 		defmode = mode;
291 	}
292 }
293 
294 start_mode_t
295 pkgservergetmode(void)
296 {
297 	if (defmode == INVALID)
298 		pkgserversetmode(DEFAULTMODE);
299 	return (defmode);
300 }
301 
302 void
303 pkgcloseserver(PKGserver server)
304 {
305 
306 	if (server->fp != NULL)
307 		(void) fclose(server->fp);
308 	free(server->curbuf);
309 	if (server->onetime) {
310 		pkgcmd_t cmd;
311 		cmd.cmd = PKG_EXIT;
312 		(void) pkgcmd(server, &cmd, sizeof (cmd), NULL, NULL, NULL);
313 	}
314 	(void) close(server->door);
315 	if (server == current_server)
316 		current_server = NULL;
317 	free(server);
318 }
319 
320 int
321 pkgcmd(PKGserver srv, void *cmd, size_t len, char **result, size_t *rlen,
322     int *fd)
323 {
324 	door_arg_t da;
325 
326 	da.data_ptr = cmd;
327 	da.data_size = len;
328 	da.desc_ptr = NULL;
329 	da.desc_num = 0;
330 	da.rbuf = result == NULL ? NULL : *result;
331 	da.rsize = rlen == NULL ? 0 : *rlen;
332 
333 	if (door_call(srv->door, &da) != 0) {
334 		if (((pkgcmd_t *)cmd)->cmd == PKG_EXIT && errno == EINTR)
335 			return (0);
336 		return (-1);
337 	}
338 
339 	if (da.desc_ptr != NULL) {
340 		int i = 0;
341 		if (fd != NULL)
342 			*fd = da.desc_ptr[i++].d_data.d_desc.d_descriptor;
343 		for (; i < da.desc_num; i++)
344 			(void) close(da.desc_ptr[i].d_data.d_desc.d_descriptor);
345 	}
346 	/* Error return */
347 	if (da.data_size == sizeof (int)) {
348 		int x = *(int *)da.data_ptr;
349 		if (x != 0) {
350 			if (result == NULL || da.rbuf != *result)
351 				(void) munmap(da.rbuf, da.rsize);
352 			return (x);
353 		}
354 	}
355 
356 	/* Other result */
357 	if (result != NULL) {
358 		/* Make sure that the result is at the start of the buffer. */
359 		if (da.data_ptr != NULL && da.rbuf != da.data_ptr)
360 			(void) memmove(da.rbuf, da.data_ptr, da.data_size);
361 		*result = da.rbuf;
362 		*rlen = da.data_size;
363 	} else if (da.rbuf != NULL) {
364 		(void) munmap(da.rbuf, da.rsize);
365 	}
366 	return (0);
367 }
368 
369 /*
370  * Pkgsync:
371  *	If the server is running, make sure that the contents
372  *	file is written.
373  *	If the server is not running, check for the log file;
374  *	if there's a non-empty log file, we need to start the server
375  *	as it will incorporate the log file into the contents file.
376  *	And then check if the door is present.  If it doesn't, we don't
377  *	need to call it.
378  */
379 
380 boolean_t
381 pkgsync_needed(const char *root, const char *sadmdir, boolean_t want_quit)
382 {
383 	struct stat pbuf;
384 	char pkgfile[PATH_MAX];
385 	boolean_t sync_needed, running;
386 	int fd;
387 	struct door_info di;
388 
389 	pkgfilename(pkgfile, root, sadmdir, PKGLOG);
390 
391 	sync_needed = stat(pkgfile, &pbuf) == 0 && pbuf.st_size > 0;
392 
393 	if (!sync_needed && !want_quit)
394 		return (B_FALSE);
395 
396 	pkgfilename(pkgfile, root, sadmdir, PKGDOOR);
397 
398 	/* sync_needed == B_TRUE || want_quit == B_TRUE */
399 	running = B_FALSE;
400 
401 	fd = open(pkgfile, O_RDWR);
402 
403 	if (fd >= 0) {
404 		if (door_info(fd, &di) == 0) {
405 			/* It's mounted, so the server is likely there */
406 			running = B_TRUE;
407 		}
408 		(void) close(fd);
409 	}
410 	return (running || sync_needed);
411 }
412 
413 int
414 pkgsync(const char *root, const char *sadmdir, boolean_t force_quit)
415 {
416 	void *server;
417 	pkgcmd_t cmd;
418 
419 	/* No need to write contents file; don't start if not running */
420 	if (!pkgsync_needed(root, sadmdir, force_quit))
421 		return (0);
422 
423 	server = pkgopenserver_i(root, sadmdir, B_FALSE, FLUSH_LOG);
424 	/*
425 	 * We're assuming that it started the server and exited immediately.
426 	 * If that didn't work, there's nothing we can do.
427 	 */
428 	if (server == NULL)
429 		return (0);
430 
431 	cmd.cmd = force_quit ? PKG_EXIT : PKG_DUMP;
432 
433 	(void) pkgcmd(server, &cmd, sizeof (cmd), NULL, NULL, NULL);
434 	(void) pkgcloseserver(server);
435 	return (0);
436 }
437 
438 int
439 pkgservercommitfile(VFP_T *a_vfp, PKGserver server)
440 {
441 	size_t len = vfpGetModifiedLen(a_vfp);
442 	ssize_t rem = len;
443 	size_t off;
444 	pkgfilter_t *pcmd;
445 	char *map = a_vfp->_vfpStart;
446 
447 	if (len < PKGADD_MAX)
448 		pcmd = alloca(sizeof (*pcmd) + len);
449 	else
450 		pcmd = alloca(sizeof (*pcmd) + PKGADD_MAX);
451 
452 
453 	off = 0;
454 	pcmd->cmd = PKG_ADDLINES;
455 	while (rem > 0) {
456 		char *p = map + off;
457 		len = rem;
458 
459 		if (len >= PKGADD_MAX) {
460 			len = PKGADD_MAX - 1;
461 			while (p[len] != '\n' && len > 0)
462 				len--;
463 			if (p[len] != '\n')
464 				return (-1);
465 			len++;
466 		}
467 		(void) memcpy(&pcmd->buf[0], p, len);
468 		pcmd->len = len;
469 
470 		if (pkgcmd(server, pcmd, sizeof (*pcmd) + len - 1,
471 		    NULL, NULL, NULL) != 0) {
472 			return (-1);
473 		}
474 		rem -= len;
475 		off += len;
476 	}
477 	pcmd->len = 0;
478 	pcmd->cmd = PKG_PKGSYNC;
479 	if (pkgcmd(server, pcmd, sizeof (*pcmd), NULL, NULL, NULL) != 0)
480 		return (-1);
481 
482 	/* Mark it unmodified. */
483 	vfpTruncate(a_vfp);
484 	(void) vfpClearModified(a_vfp);
485 
486 	return (0);
487 }
488 
489 int
490 pkgopenfilter(PKGserver server, const char *filt)
491 {
492 	int fd;
493 	pkgfilter_t *pfcmd;
494 	int clen = filt == NULL ? 0 : strlen(filt);
495 	int len = sizeof (*pfcmd) + clen;
496 
497 	pfcmd = alloca(len);
498 
499 	if (server->fp != NULL) {
500 		(void) fclose(server->fp);
501 		server->fp = NULL;
502 	}
503 
504 	pfcmd->cmd = PKG_FILTER;
505 	pfcmd->len = clen;
506 	if (filt != NULL)
507 		(void) strcpy(pfcmd->buf, filt);
508 
509 	fd = -1;
510 
511 	if (pkgcmd(server, pfcmd, len, NULL, NULL, &fd) != 0 || fd == -1) {
512 		progerr(gettext(ERR_START_FILTER));
513 		return (-1);
514 	}
515 	(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
516 
517 	server->fp = fdopen(fd, "r");
518 	if (server->fp == NULL) {
519 		(void) close(fd);
520 		progerr(gettext(ERR_START_FILTER));
521 		return (-1);
522 	}
523 	return (0);
524 }
525 
526 void
527 pkgclosefilter(PKGserver server)
528 {
529 	if (server->fp != NULL) {
530 		(void) fclose(server->fp);
531 		server->fp = NULL;
532 	}
533 }
534 
535 /*
536  * Report the next entry from the contents file.
537  */
538 char *
539 pkggetentry(PKGserver server, int *len, int *pathlen)
540 {
541 	int num[2];
542 
543 	if (server->fp == NULL)
544 		return (NULL);
545 
546 	if (feof(server->fp) || ferror(server->fp))
547 		return (NULL);
548 
549 	if (fread(num, sizeof (int), 2, server->fp) != 2)
550 		return (NULL);
551 
552 	if (num[0] > server->buflen) {
553 		free(server->curbuf);
554 		server->buflen = num[0];
555 		server->curbuf = malloc(server->buflen);
556 		if (server->curbuf == NULL)
557 			return (NULL);
558 	}
559 	if (fread(server->curbuf, 1, num[0], server->fp) != num[0])
560 		return (NULL);
561 
562 	*len = num[0];
563 	*pathlen = num[1];
564 
565 	return (server->curbuf);
566 }
567 
568 char *
569 pkggetentry_named(PKGserver server, const char *path, int *len, int *pathlen)
570 {
571 	int plen = strlen(path);
572 	pkgfilter_t *pcmd = alloca(sizeof (*pcmd) + plen);
573 	char *result;
574 	unsigned int rlen;
575 
576 	pcmd->cmd = PKG_FINDFILE;
577 	*pathlen = pcmd->len = plen;
578 	(void) memcpy(pcmd->buf, path, pcmd->len + 1);
579 
580 	result = server->curbuf;
581 	rlen = server->buflen;
582 
583 	if (pkgcmd(server, pcmd, sizeof (*pcmd) + pcmd->len,
584 	    &result, &rlen, NULL) != 0) {
585 		return (NULL);
586 	}
587 	if (rlen == 0)
588 		return (NULL);
589 
590 	/* Result too big */
591 	if (result != server->curbuf) {
592 		free(server->curbuf);
593 		server->buflen = rlen;
594 		server->curbuf = malloc(server->buflen);
595 		if (server->curbuf == NULL)
596 			return (NULL);
597 		(void) memcpy(server->curbuf, result, rlen);
598 		(void) munmap(result, rlen);
599 	}
600 	*len = rlen;
601 
602 	return (server->curbuf);
603 }
604