1113277Smike/*-
2113277Smike * Copyright (c) 2003 Mike Barcroft <mike@FreeBSD.org>
3185435Sbz * Copyright (c) 2008 Bjoern A. Zeeb <bz@FreeBSD.org>
4192896Sjamie * Copyright (c) 2009 James Gritton <jamie@FreeBSD.org>
5113277Smike * All rights reserved.
6113277Smike *
7113277Smike * Redistribution and use in source and binary forms, with or without
8113277Smike * modification, are permitted provided that the following conditions
9113277Smike * are met:
10113277Smike * 1. Redistributions of source code must retain the above copyright
11113277Smike *    notice, this list of conditions and the following disclaimer.
12113277Smike * 2. Redistributions in binary form must reproduce the above copyright
13113277Smike *    notice, this list of conditions and the following disclaimer in the
14113277Smike *    documentation and/or other materials provided with the distribution.
15113277Smike *
16113277Smike * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17113277Smike * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18113277Smike * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19113277Smike * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20113277Smike * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21113277Smike * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22113277Smike * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23113277Smike * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24113277Smike * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25113277Smike * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26113277Smike * SUCH DAMAGE.
27113277Smike */
28113277Smike
29192896Sjamie#include <sys/cdefs.h>
30192896Sjamie__FBSDID("$FreeBSD$");
31192896Sjamie
32113277Smike#include <sys/param.h>
33113277Smike#include <sys/jail.h>
34192896Sjamie#include <sys/socket.h>
35113277Smike#include <sys/sysctl.h>
36113277Smike
37192896Sjamie#include <arpa/inet.h>
38185435Sbz#include <netinet/in.h>
39192896Sjamie
40113277Smike#include <err.h>
41113277Smike#include <errno.h>
42194869Sjamie#include <jail.h>
43113277Smike#include <limits.h>
44113277Smike#include <stdio.h>
45113277Smike#include <stdlib.h>
46185435Sbz#include <string.h>
47185435Sbz#include <unistd.h>
48113277Smike
49194869Sjamie#define	JP_USER		0x01000000
50194869Sjamie#define	JP_OPT		0x02000000
51113277Smike
52192896Sjamie#define	PRINT_DEFAULT	0x01
53192896Sjamie#define	PRINT_HEADER	0x02
54192896Sjamie#define	PRINT_NAMEVAL	0x04
55192896Sjamie#define	PRINT_QUOTED	0x08
56192896Sjamie#define	PRINT_SKIP	0x10
57192896Sjamie#define	PRINT_VERBOSE	0x20
58250736Sdes#define	PRINT_JAIL_NAME	0x40
59192896Sjamie
60194869Sjamiestatic struct jailparam *params;
61195870Sjamiestatic int *param_parent;
62192896Sjamiestatic int nparams;
63222465Sbz#ifdef INET6
64222465Sbzstatic int ip6_ok;
65222465Sbz#endif
66222465Sbz#ifdef INET
67222465Sbzstatic int ip4_ok;
68222465Sbz#endif
69192896Sjamie
70194869Sjamiestatic int add_param(const char *name, void *value, size_t valuelen,
71194869Sjamie		struct jailparam *source, unsigned flags);
72192896Sjamiestatic int sort_param(const void *a, const void *b);
73192896Sjamiestatic char *noname(const char *name);
74192896Sjamiestatic char *nononame(const char *name);
75192896Sjamiestatic int print_jail(int pflags, int jflags);
76194869Sjamiestatic void quoted_print(char *str);
77192896Sjamie
78192896Sjamieint
79192896Sjamiemain(int argc, char **argv)
80185435Sbz{
81195870Sjamie	char *dot, *ep, *jname;
82192896Sjamie	int c, i, jflags, jid, lastjid, pflags, spc;
83185435Sbz
84192896Sjamie	jname = NULL;
85192896Sjamie	pflags = jflags = jid = 0;
86250736Sdes	while ((c = getopt(argc, argv, "adj:hNnqsv")) >= 0)
87192896Sjamie		switch (c) {
88192896Sjamie		case 'a':
89192896Sjamie		case 'd':
90192896Sjamie			jflags |= JAIL_DYING;
91192896Sjamie			break;
92192896Sjamie		case 'j':
93192896Sjamie			jid = strtoul(optarg, &ep, 10);
94209820Sjamie			if (!jid || *ep) {
95209820Sjamie				jid = 0;
96192896Sjamie				jname = optarg;
97209820Sjamie			}
98192896Sjamie			break;
99192896Sjamie		case 'h':
100195462Sjamie			pflags = (pflags & ~(PRINT_SKIP | PRINT_VERBOSE)) |
101195462Sjamie			    PRINT_HEADER;
102192896Sjamie			break;
103250736Sdes		case 'N':
104250736Sdes			pflags |= PRINT_JAIL_NAME;
105250736Sdes			break;
106192896Sjamie		case 'n':
107192896Sjamie			pflags = (pflags & ~PRINT_VERBOSE) | PRINT_NAMEVAL;
108192896Sjamie			break;
109192896Sjamie		case 'q':
110192896Sjamie			pflags |= PRINT_QUOTED;
111192896Sjamie			break;
112192896Sjamie		case 's':
113192896Sjamie			pflags = (pflags & ~(PRINT_HEADER | PRINT_VERBOSE)) |
114192896Sjamie			    PRINT_NAMEVAL | PRINT_QUOTED | PRINT_SKIP;
115192896Sjamie			break;
116192896Sjamie		case 'v':
117195462Sjamie			pflags = (pflags &
118195462Sjamie			    ~(PRINT_HEADER | PRINT_NAMEVAL | PRINT_SKIP)) |
119192896Sjamie			    PRINT_VERBOSE;
120192896Sjamie			break;
121192896Sjamie		default:
122250736Sdes			errx(1, "usage: jls [-dhNnqv] [-j jail] [param ...]");
123192896Sjamie		}
124185435Sbz
125222465Sbz#ifdef INET6
126222465Sbz	ip6_ok = feature_present("inet6");
127222465Sbz#endif
128222465Sbz#ifdef INET
129222465Sbz	ip4_ok = feature_present("inet");
130222465Sbz#endif
131222465Sbz
132192896Sjamie	/* Add the parameters to print. */
133192896Sjamie	if (optind == argc) {
134195462Sjamie		if (pflags & (PRINT_HEADER | PRINT_NAMEVAL))
135195462Sjamie			add_param("all", NULL, (size_t)0, NULL, JP_USER);
136195462Sjamie		else if (pflags & PRINT_VERBOSE) {
137194869Sjamie			add_param("jid", NULL, (size_t)0, NULL, JP_USER);
138194869Sjamie			add_param("host.hostname", NULL, (size_t)0, NULL,
139194869Sjamie			    JP_USER);
140194869Sjamie			add_param("path", NULL, (size_t)0, NULL, JP_USER);
141194869Sjamie			add_param("name", NULL, (size_t)0, NULL, JP_USER);
142194869Sjamie			add_param("dying", NULL, (size_t)0, NULL, JP_USER);
143194869Sjamie			add_param("cpuset.id", NULL, (size_t)0, NULL, JP_USER);
144222465Sbz#ifdef INET
145222465Sbz			if (ip4_ok)
146222465Sbz				add_param("ip4.addr", NULL, (size_t)0, NULL,
147222465Sbz				    JP_USER);
148222465Sbz#endif
149222465Sbz#ifdef INET6
150222465Sbz			if (ip6_ok)
151222465Sbz				add_param("ip6.addr", NULL, (size_t)0, NULL,
152222465Sbz				    JP_USER | JP_OPT);
153222465Sbz#endif
154192896Sjamie		} else {
155195462Sjamie			pflags |= PRINT_DEFAULT;
156250736Sdes			if (pflags & PRINT_JAIL_NAME)
157250736Sdes				add_param("name", NULL, (size_t)0, NULL, JP_USER);
158250736Sdes			else
159250736Sdes				add_param("jid", NULL, (size_t)0, NULL, JP_USER);
160222465Sbz#ifdef INET
161222465Sbz			if (ip4_ok)
162222465Sbz				add_param("ip4.addr", NULL, (size_t)0, NULL,
163222465Sbz				    JP_USER);
164222465Sbz#endif
165194869Sjamie			add_param("host.hostname", NULL, (size_t)0, NULL,
166194869Sjamie			    JP_USER);
167194869Sjamie			add_param("path", NULL, (size_t)0, NULL, JP_USER);
168192896Sjamie		}
169192896Sjamie	} else
170192896Sjamie		while (optind < argc)
171194869Sjamie			add_param(argv[optind++], NULL, (size_t)0, NULL,
172194869Sjamie			    JP_USER);
173192896Sjamie
174192896Sjamie	if (pflags & PRINT_SKIP) {
175195870Sjamie		/* Check for parameters with jailsys parents. */
176192896Sjamie		for (i = 0; i < nparams; i++) {
177194869Sjamie			if ((params[i].jp_flags & JP_USER) &&
178194869Sjamie			    (dot = strchr(params[i].jp_name, '.'))) {
179192896Sjamie				*dot = 0;
180195870Sjamie				param_parent[i] = add_param(params[i].jp_name,
181195870Sjamie				    NULL, (size_t)0, NULL, JP_OPT);
182192896Sjamie				*dot = '.';
183192896Sjamie			}
184192896Sjamie		}
185192896Sjamie	}
186192896Sjamie
187194869Sjamie	/* Add the index key parameters. */
188192896Sjamie	if (jid != 0)
189194869Sjamie		add_param("jid", &jid, sizeof(jid), NULL, 0);
190192896Sjamie	else if (jname != NULL)
191194869Sjamie		add_param("name", jname, strlen(jname), NULL, 0);
192192896Sjamie	else
193194869Sjamie		add_param("lastjid", &lastjid, sizeof(lastjid), NULL, 0);
194192896Sjamie
195192896Sjamie	/* Print a header line if requested. */
196192896Sjamie	if (pflags & PRINT_VERBOSE)
197192896Sjamie		printf("   JID  Hostname                      Path\n"
198192896Sjamie		       "        Name                          State\n"
199192896Sjamie		       "        CPUSetID\n"
200192896Sjamie		       "        IP Address(es)\n");
201192896Sjamie	else if (pflags & PRINT_DEFAULT)
202250736Sdes		if (pflags & PRINT_JAIL_NAME)
203250736Sdes			printf(" JID             IP Address      "
204250736Sdes			    "Hostname                      Path\n");
205250736Sdes		else
206250736Sdes			printf("   JID  IP Address      "
207250736Sdes			    "Hostname                      Path\n");
208192896Sjamie	else if (pflags & PRINT_HEADER) {
209192896Sjamie		for (i = spc = 0; i < nparams; i++)
210194869Sjamie			if (params[i].jp_flags & JP_USER) {
211192896Sjamie				if (spc)
212192896Sjamie					putchar(' ');
213192896Sjamie				else
214192896Sjamie					spc = 1;
215194869Sjamie				fputs(params[i].jp_name, stdout);
216192896Sjamie			}
217192896Sjamie		putchar('\n');
218192896Sjamie	}
219192896Sjamie
220192896Sjamie	/* Fetch the jail(s) and print the paramters. */
221192896Sjamie	if (jid != 0 || jname != NULL) {
222194869Sjamie		if (print_jail(pflags, jflags) < 0)
223194869Sjamie			errx(1, "%s", jail_errmsg);
224186085Sbz	} else {
225192896Sjamie		for (lastjid = 0;
226192896Sjamie		     (lastjid = print_jail(pflags, jflags)) >= 0; )
227192896Sjamie			;
228194869Sjamie		if (errno != 0 && errno != ENOENT)
229194869Sjamie			errx(1, "%s", jail_errmsg);
230186085Sbz	}
231185435Sbz
232192896Sjamie	return (0);
233185435Sbz}
234185435Sbz
235192896Sjamiestatic int
236194869Sjamieadd_param(const char *name, void *value, size_t valuelen,
237194869Sjamie    struct jailparam *source, unsigned flags)
238185435Sbz{
239194869Sjamie	struct jailparam *param, *tparams;
240192896Sjamie	int i, tnparams;
241185435Sbz
242192896Sjamie	static int paramlistsize;
243185435Sbz
244192896Sjamie	/* The pseudo-parameter "all" scans the list of available parameters. */
245192896Sjamie	if (!strcmp(name, "all")) {
246194869Sjamie		tnparams = jailparam_all(&tparams);
247194869Sjamie		if (tnparams < 0)
248194869Sjamie			errx(1, "%s", jail_errmsg);
249194869Sjamie		qsort(tparams, (size_t)tnparams, sizeof(struct jailparam),
250194869Sjamie		    sort_param);
251194869Sjamie		for (i = 0; i < tnparams; i++)
252194869Sjamie			add_param(tparams[i].jp_name, NULL, (size_t)0,
253194869Sjamie			    tparams + i, flags);
254194869Sjamie		free(tparams);
255192896Sjamie		return -1;
256185435Sbz	}
257185435Sbz
258192896Sjamie	/* Check for repeat parameters. */
259192896Sjamie	for (i = 0; i < nparams; i++)
260194869Sjamie		if (!strcmp(name, params[i].jp_name)) {
261194869Sjamie			if (value != NULL && jailparam_import_raw(params + i,
262194869Sjamie			    value, valuelen) < 0)
263194869Sjamie				errx(1, "%s", jail_errmsg);
264194869Sjamie			params[i].jp_flags |= flags;
265194869Sjamie			if (source != NULL)
266194869Sjamie				jailparam_free(source, 1);
267192896Sjamie			return i;
268192896Sjamie		}
269185435Sbz
270192896Sjamie	/* Make sure there is room for the new param record. */
271192896Sjamie	if (!nparams) {
272192896Sjamie		paramlistsize = 32;
273192896Sjamie		params = malloc(paramlistsize * sizeof(*params));
274195870Sjamie		param_parent = malloc(paramlistsize * sizeof(*param_parent));
275195870Sjamie		if (params == NULL || param_parent == NULL)
276192896Sjamie			err(1, "malloc");
277192896Sjamie	} else if (nparams >= paramlistsize) {
278192896Sjamie		paramlistsize *= 2;
279192896Sjamie		params = realloc(params, paramlistsize * sizeof(*params));
280195870Sjamie		param_parent = realloc(param_parent,
281195870Sjamie		    paramlistsize * sizeof(*param_parent));
282195870Sjamie		if (params == NULL || param_parent == NULL)
283192896Sjamie			err(1, "realloc");
284192896Sjamie	}
285185435Sbz
286192896Sjamie	/* Look up the parameter. */
287195870Sjamie	param_parent[nparams] = -1;
288192896Sjamie	param = params + nparams++;
289194869Sjamie	if (source != NULL) {
290194869Sjamie		*param = *source;
291194869Sjamie		param->jp_flags |= flags;
292194869Sjamie		return param - params;
293192896Sjamie	}
294194869Sjamie	if (jailparam_init(param, name) < 0)
295194869Sjamie		errx(1, "%s", jail_errmsg);
296194869Sjamie	param->jp_flags = flags;
297194869Sjamie	if ((value != NULL ? jailparam_import_raw(param, value, valuelen)
298194869Sjamie	     : jailparam_import(param, value)) < 0) {
299194869Sjamie		if (flags & JP_OPT) {
300192896Sjamie			nparams--;
301194869Sjamie			return (-1);
302192896Sjamie		}
303194869Sjamie		errx(1, "%s", jail_errmsg);
304192896Sjamie	}
305192896Sjamie	return param - params;
306192896Sjamie}
307192896Sjamie
308192896Sjamiestatic int
309192896Sjamiesort_param(const void *a, const void *b)
310192896Sjamie{
311194869Sjamie	const struct jailparam *parama, *paramb;
312192896Sjamie	char *ap, *bp;
313186085Sbz
314192896Sjamie	/* Put top-level parameters first. */
315192896Sjamie	parama = a;
316192896Sjamie	paramb = b;
317194869Sjamie	ap = strchr(parama->jp_name, '.');
318194869Sjamie	bp = strchr(paramb->jp_name, '.');
319192896Sjamie	if (ap && !bp)
320192896Sjamie		return (1);
321192896Sjamie	if (bp && !ap)
322192896Sjamie		return (-1);
323194869Sjamie	return (strcmp(parama->jp_name, paramb->jp_name));
324185435Sbz}
325185435Sbz
326192896Sjamiestatic char *
327192896Sjamienoname(const char *name)
328185435Sbz{
329192896Sjamie	char *nname, *p;
330185435Sbz
331192896Sjamie	nname = malloc(strlen(name) + 3);
332192896Sjamie	if (nname == NULL)
333192896Sjamie		err(1, "malloc");
334192896Sjamie	p = strrchr(name, '.');
335192896Sjamie	if (p != NULL)
336192896Sjamie		sprintf(nname, "%.*s.no%s", (int)(p - name), name, p + 1);
337192896Sjamie	else
338192896Sjamie		sprintf(nname, "no%s", name);
339192896Sjamie	return nname;
340185435Sbz}
341185435Sbz
342192896Sjamiestatic char *
343192896Sjamienononame(const char *name)
344192896Sjamie{
345192896Sjamie	char *nname, *p;
346113277Smike
347192896Sjamie	p = strrchr(name, '.');
348192896Sjamie	if (strncmp(p ? p + 1 : name, "no", 2))
349192896Sjamie		return NULL;
350192896Sjamie	nname = malloc(strlen(name) - 1);
351192896Sjamie	if (nname == NULL)
352192896Sjamie		err(1, "malloc");
353192896Sjamie	if (p != NULL)
354192896Sjamie		sprintf(nname, "%.*s.%s", (int)(p - name), name, p + 3);
355192896Sjamie	else
356192896Sjamie		strcpy(nname, name + 2);
357192896Sjamie	return nname;
358192896Sjamie}
359185435Sbz
360192896Sjamiestatic int
361192896Sjamieprint_jail(int pflags, int jflags)
362192896Sjamie{
363192896Sjamie	char *nname;
364194869Sjamie	char **param_values;
365222465Sbz	int i, ai, jid, count, n, spc;
366192896Sjamie	char ipbuf[INET6_ADDRSTRLEN];
367113277Smike
368194869Sjamie	jid = jailparam_get(params, nparams, jflags);
369194869Sjamie	if (jid < 0)
370194869Sjamie		return jid;
371192896Sjamie	if (pflags & PRINT_VERBOSE) {
372192896Sjamie		printf("%6d  %-29.29s %.74s\n"
373192896Sjamie		       "%6s  %-29.29s %.74s\n"
374192896Sjamie		       "%6s  %-6d\n",
375194869Sjamie		    *(int *)params[0].jp_value,
376194869Sjamie		    (char *)params[1].jp_value,
377194869Sjamie		    (char *)params[2].jp_value,
378192896Sjamie		    "",
379194869Sjamie		    (char *)params[3].jp_value,
380194869Sjamie		    *(int *)params[4].jp_value ? "DYING" : "ACTIVE",
381192896Sjamie		    "",
382194869Sjamie		    *(int *)params[5].jp_value);
383222465Sbz		n = 6;
384222465Sbz#ifdef INET
385224841Sbz		if (ip4_ok && !strcmp(params[n].jp_name, "ip4.addr")) {
386222465Sbz			count = params[n].jp_valuelen / sizeof(struct in_addr);
387192896Sjamie			for (ai = 0; ai < count; ai++)
388222465Sbz				if (inet_ntop(AF_INET,
389222465Sbz				    &((struct in_addr *)params[n].jp_value)[ai],
390222465Sbz				    ipbuf, sizeof(ipbuf)) == NULL)
391222465Sbz					err(1, "inet_ntop");
392222465Sbz				else
393222465Sbz					printf("%6s  %-15.15s\n", "", ipbuf);
394222465Sbz			n++;
395222465Sbz		}
396222465Sbz#endif
397222465Sbz#ifdef INET6
398222465Sbz		if (ip6_ok && !strcmp(params[n].jp_name, "ip6.addr")) {
399222465Sbz			count = params[n].jp_valuelen / sizeof(struct in6_addr);
400222465Sbz			for (ai = 0; ai < count; ai++)
401194869Sjamie				if (inet_ntop(AF_INET6,
402222465Sbz				    &((struct in6_addr *)
403222465Sbz					params[n].jp_value)[ai],
404192896Sjamie				    ipbuf, sizeof(ipbuf)) == NULL)
405192896Sjamie					err(1, "inet_ntop");
406192896Sjamie				else
407196137Sbz					printf("%6s  %s\n", "", ipbuf);
408222465Sbz			n++;
409192896Sjamie		}
410222465Sbz#endif
411250736Sdes	} else if (pflags & PRINT_DEFAULT) {
412250736Sdes		if (pflags & PRINT_JAIL_NAME)
413250736Sdes			printf(" %-15s ", (char *)params[0].jp_value);
414250736Sdes		else
415250736Sdes			printf("%6d  ", *(int *)params[0].jp_value);
416250736Sdes		printf("%-15.15s %-29.29s %.74s\n",
417222465Sbz#ifdef INET
418222465Sbz		    (!ip4_ok || params[1].jp_valuelen == 0) ? "-"
419194869Sjamie		    : inet_ntoa(*(struct in_addr *)params[1].jp_value),
420232613Sbz		    (char *)params[2-!ip4_ok].jp_value,
421232613Sbz		    (char *)params[3-!ip4_ok].jp_value);
422222465Sbz#else
423223224Sbz		    "-",
424232613Sbz		    (char *)params[1].jp_value,
425232613Sbz		    (char *)params[2].jp_value);
426222465Sbz#endif
427250736Sdes	} else {
428194869Sjamie		param_values = alloca(nparams * sizeof(*param_values));
429194869Sjamie		for (i = 0; i < nparams; i++) {
430194869Sjamie			if (!(params[i].jp_flags & JP_USER))
431194869Sjamie				continue;
432194869Sjamie			param_values[i] = jailparam_export(params + i);
433194869Sjamie			if (param_values[i] == NULL)
434194869Sjamie				errx(1, "%s", jail_errmsg);
435194869Sjamie		}
436192896Sjamie		for (i = spc = 0; i < nparams; i++) {
437194869Sjamie			if (!(params[i].jp_flags & JP_USER))
438149081Spjd				continue;
439192896Sjamie			if ((pflags & PRINT_SKIP) &&
440194869Sjamie			    ((!(params[i].jp_ctltype &
441194869Sjamie				(CTLFLAG_WR | CTLFLAG_TUN))) ||
442195870Sjamie			     (param_parent[i] >= 0 &&
443195870Sjamie			      *(int *)params[param_parent[i]].jp_value !=
444195870Sjamie			      JAIL_SYS_NEW)))
445192896Sjamie				continue;
446192896Sjamie			if (spc)
447192896Sjamie				putchar(' ');
448192896Sjamie			else
449192896Sjamie				spc = 1;
450192896Sjamie			if (pflags & PRINT_NAMEVAL) {
451192896Sjamie				/*
452192896Sjamie				 * Generally "name=value", but for booleans
453192896Sjamie				 * either "name" or "noname".
454192896Sjamie				 */
455194869Sjamie				if (params[i].jp_flags &
456194869Sjamie				    (JP_BOOL | JP_NOBOOL)) {
457194869Sjamie					if (*(int *)params[i].jp_value)
458194869Sjamie						printf("%s", params[i].jp_name);
459192896Sjamie					else {
460194869Sjamie						nname = (params[i].jp_flags &
461194869Sjamie						    JP_NOBOOL) ?
462194869Sjamie						    nononame(params[i].jp_name)
463194869Sjamie						    : noname(params[i].jp_name);
464192896Sjamie						printf("%s", nname);
465192896Sjamie						free(nname);
466192896Sjamie					}
467194869Sjamie					continue;
468192896Sjamie				}
469194869Sjamie				printf("%s=", params[i].jp_name);
470149081Spjd			}
471194869Sjamie			if (params[i].jp_valuelen == 0) {
472192896Sjamie				if (pflags & PRINT_QUOTED)
473192896Sjamie					printf("\"\"");
474192896Sjamie				else if (!(pflags & PRINT_NAMEVAL))
475192896Sjamie					putchar('-');
476194869Sjamie			} else
477194869Sjamie				quoted_print(param_values[i]);
478113277Smike		}
479192896Sjamie		putchar('\n');
480194869Sjamie		for (i = 0; i < nparams; i++)
481194869Sjamie			if (params[i].jp_flags & JP_USER)
482194869Sjamie				free(param_values[i]);
483149081Spjd	}
484192896Sjamie	return (jid);
485192896Sjamie}
486113277Smike
487192896Sjamiestatic void
488194869Sjamiequoted_print(char *str)
489192896Sjamie{
490192896Sjamie	int c, qc;
491192896Sjamie	char *p = str;
492192896Sjamie
493192896Sjamie	/* An empty string needs quoting. */
494192896Sjamie	if (!*p) {
495192896Sjamie		fputs("\"\"", stdout);
496192896Sjamie		return;
497113277Smike	}
498192896Sjamie
499192896Sjamie	/*
500192896Sjamie	 * The value will be surrounded by quotes if it contains spaces
501192896Sjamie	 * or quotes.
502192896Sjamie	 */
503192896Sjamie	qc = strchr(p, '\'') ? '"'
504192896Sjamie	    : strchr(p, '"') ? '\''
505192896Sjamie	    : strchr(p, ' ') || strchr(p, '\t') ? '"'
506192896Sjamie	    : 0;
507192896Sjamie	if (qc)
508192896Sjamie		putchar(qc);
509194869Sjamie	while ((c = *p++)) {
510192896Sjamie		if (c == '\\' || c == qc)
511192896Sjamie			putchar('\\');
512192896Sjamie		putchar(c);
513185435Sbz	}
514192896Sjamie	if (qc)
515192896Sjamie		putchar(qc);
516113277Smike}
517