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{
81279348Sjamie	char *dot, *ep, *jname, *pname;
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		}
169279347Sjamie	} else {
170279347Sjamie		pflags &= ~PRINT_VERBOSE;
171192896Sjamie		while (optind < argc)
172194869Sjamie			add_param(argv[optind++], NULL, (size_t)0, NULL,
173194869Sjamie			    JP_USER);
174279347Sjamie	}
175192896Sjamie
176192896Sjamie	if (pflags & PRINT_SKIP) {
177195870Sjamie		/* Check for parameters with jailsys parents. */
178192896Sjamie		for (i = 0; i < nparams; i++) {
179194869Sjamie			if ((params[i].jp_flags & JP_USER) &&
180194869Sjamie			    (dot = strchr(params[i].jp_name, '.'))) {
181279348Sjamie				pname = alloca((dot - params[i].jp_name) + 1);
182279348Sjamie				strlcpy(pname, params[i].jp_name,
183279348Sjamie				    (dot - params[i].jp_name) + 1);
184279348Sjamie				param_parent[i] = add_param(pname,
185195870Sjamie				    NULL, (size_t)0, NULL, JP_OPT);
186192896Sjamie			}
187192896Sjamie		}
188192896Sjamie	}
189192896Sjamie
190194869Sjamie	/* Add the index key parameters. */
191192896Sjamie	if (jid != 0)
192194869Sjamie		add_param("jid", &jid, sizeof(jid), NULL, 0);
193192896Sjamie	else if (jname != NULL)
194194869Sjamie		add_param("name", jname, strlen(jname), NULL, 0);
195192896Sjamie	else
196194869Sjamie		add_param("lastjid", &lastjid, sizeof(lastjid), NULL, 0);
197192896Sjamie
198192896Sjamie	/* Print a header line if requested. */
199192896Sjamie	if (pflags & PRINT_VERBOSE)
200192896Sjamie		printf("   JID  Hostname                      Path\n"
201192896Sjamie		       "        Name                          State\n"
202192896Sjamie		       "        CPUSetID\n"
203192896Sjamie		       "        IP Address(es)\n");
204192896Sjamie	else if (pflags & PRINT_DEFAULT)
205250736Sdes		if (pflags & PRINT_JAIL_NAME)
206250736Sdes			printf(" JID             IP Address      "
207250736Sdes			    "Hostname                      Path\n");
208250736Sdes		else
209250736Sdes			printf("   JID  IP Address      "
210250736Sdes			    "Hostname                      Path\n");
211192896Sjamie	else if (pflags & PRINT_HEADER) {
212192896Sjamie		for (i = spc = 0; i < nparams; i++)
213194869Sjamie			if (params[i].jp_flags & JP_USER) {
214192896Sjamie				if (spc)
215192896Sjamie					putchar(' ');
216192896Sjamie				else
217192896Sjamie					spc = 1;
218194869Sjamie				fputs(params[i].jp_name, stdout);
219192896Sjamie			}
220192896Sjamie		putchar('\n');
221192896Sjamie	}
222192896Sjamie
223293290Sbdrewery	/* Fetch the jail(s) and print the parameters. */
224192896Sjamie	if (jid != 0 || jname != NULL) {
225194869Sjamie		if (print_jail(pflags, jflags) < 0)
226194869Sjamie			errx(1, "%s", jail_errmsg);
227186085Sbz	} else {
228192896Sjamie		for (lastjid = 0;
229192896Sjamie		     (lastjid = print_jail(pflags, jflags)) >= 0; )
230192896Sjamie			;
231194869Sjamie		if (errno != 0 && errno != ENOENT)
232194869Sjamie			errx(1, "%s", jail_errmsg);
233186085Sbz	}
234185435Sbz
235192896Sjamie	return (0);
236185435Sbz}
237185435Sbz
238192896Sjamiestatic int
239194869Sjamieadd_param(const char *name, void *value, size_t valuelen,
240194869Sjamie    struct jailparam *source, unsigned flags)
241185435Sbz{
242194869Sjamie	struct jailparam *param, *tparams;
243192896Sjamie	int i, tnparams;
244185435Sbz
245192896Sjamie	static int paramlistsize;
246185435Sbz
247192896Sjamie	/* The pseudo-parameter "all" scans the list of available parameters. */
248192896Sjamie	if (!strcmp(name, "all")) {
249194869Sjamie		tnparams = jailparam_all(&tparams);
250194869Sjamie		if (tnparams < 0)
251194869Sjamie			errx(1, "%s", jail_errmsg);
252194869Sjamie		qsort(tparams, (size_t)tnparams, sizeof(struct jailparam),
253194869Sjamie		    sort_param);
254194869Sjamie		for (i = 0; i < tnparams; i++)
255194869Sjamie			add_param(tparams[i].jp_name, NULL, (size_t)0,
256194869Sjamie			    tparams + i, flags);
257194869Sjamie		free(tparams);
258192896Sjamie		return -1;
259185435Sbz	}
260185435Sbz
261192896Sjamie	/* Check for repeat parameters. */
262192896Sjamie	for (i = 0; i < nparams; i++)
263194869Sjamie		if (!strcmp(name, params[i].jp_name)) {
264194869Sjamie			if (value != NULL && jailparam_import_raw(params + i,
265194869Sjamie			    value, valuelen) < 0)
266194869Sjamie				errx(1, "%s", jail_errmsg);
267194869Sjamie			params[i].jp_flags |= flags;
268194869Sjamie			if (source != NULL)
269194869Sjamie				jailparam_free(source, 1);
270192896Sjamie			return i;
271192896Sjamie		}
272185435Sbz
273192896Sjamie	/* Make sure there is room for the new param record. */
274192896Sjamie	if (!nparams) {
275192896Sjamie		paramlistsize = 32;
276192896Sjamie		params = malloc(paramlistsize * sizeof(*params));
277195870Sjamie		param_parent = malloc(paramlistsize * sizeof(*param_parent));
278195870Sjamie		if (params == NULL || param_parent == NULL)
279192896Sjamie			err(1, "malloc");
280192896Sjamie	} else if (nparams >= paramlistsize) {
281192896Sjamie		paramlistsize *= 2;
282192896Sjamie		params = realloc(params, paramlistsize * sizeof(*params));
283195870Sjamie		param_parent = realloc(param_parent,
284195870Sjamie		    paramlistsize * sizeof(*param_parent));
285195870Sjamie		if (params == NULL || param_parent == NULL)
286192896Sjamie			err(1, "realloc");
287192896Sjamie	}
288185435Sbz
289192896Sjamie	/* Look up the parameter. */
290195870Sjamie	param_parent[nparams] = -1;
291192896Sjamie	param = params + nparams++;
292194869Sjamie	if (source != NULL) {
293194869Sjamie		*param = *source;
294194869Sjamie		param->jp_flags |= flags;
295194869Sjamie		return param - params;
296192896Sjamie	}
297279348Sjamie	if (jailparam_init(param, name) < 0 ||
298279348Sjamie	    (value != NULL ? jailparam_import_raw(param, value, valuelen)
299194869Sjamie	     : jailparam_import(param, value)) < 0) {
300194869Sjamie		if (flags & JP_OPT) {
301192896Sjamie			nparams--;
302194869Sjamie			return (-1);
303192896Sjamie		}
304194869Sjamie		errx(1, "%s", jail_errmsg);
305192896Sjamie	}
306279348Sjamie	param->jp_flags = flags;
307192896Sjamie	return param - params;
308192896Sjamie}
309192896Sjamie
310192896Sjamiestatic int
311192896Sjamiesort_param(const void *a, const void *b)
312192896Sjamie{
313194869Sjamie	const struct jailparam *parama, *paramb;
314192896Sjamie	char *ap, *bp;
315186085Sbz
316192896Sjamie	/* Put top-level parameters first. */
317192896Sjamie	parama = a;
318192896Sjamie	paramb = b;
319194869Sjamie	ap = strchr(parama->jp_name, '.');
320194869Sjamie	bp = strchr(paramb->jp_name, '.');
321192896Sjamie	if (ap && !bp)
322192896Sjamie		return (1);
323192896Sjamie	if (bp && !ap)
324192896Sjamie		return (-1);
325194869Sjamie	return (strcmp(parama->jp_name, paramb->jp_name));
326185435Sbz}
327185435Sbz
328192896Sjamiestatic char *
329192896Sjamienoname(const char *name)
330185435Sbz{
331192896Sjamie	char *nname, *p;
332185435Sbz
333192896Sjamie	nname = malloc(strlen(name) + 3);
334192896Sjamie	if (nname == NULL)
335192896Sjamie		err(1, "malloc");
336192896Sjamie	p = strrchr(name, '.');
337192896Sjamie	if (p != NULL)
338192896Sjamie		sprintf(nname, "%.*s.no%s", (int)(p - name), name, p + 1);
339192896Sjamie	else
340192896Sjamie		sprintf(nname, "no%s", name);
341192896Sjamie	return nname;
342185435Sbz}
343185435Sbz
344192896Sjamiestatic char *
345192896Sjamienononame(const char *name)
346192896Sjamie{
347192896Sjamie	char *nname, *p;
348113277Smike
349192896Sjamie	p = strrchr(name, '.');
350192896Sjamie	if (strncmp(p ? p + 1 : name, "no", 2))
351192896Sjamie		return NULL;
352192896Sjamie	nname = malloc(strlen(name) - 1);
353192896Sjamie	if (nname == NULL)
354192896Sjamie		err(1, "malloc");
355192896Sjamie	if (p != NULL)
356192896Sjamie		sprintf(nname, "%.*s.%s", (int)(p - name), name, p + 3);
357192896Sjamie	else
358192896Sjamie		strcpy(nname, name + 2);
359192896Sjamie	return nname;
360192896Sjamie}
361185435Sbz
362192896Sjamiestatic int
363192896Sjamieprint_jail(int pflags, int jflags)
364192896Sjamie{
365192896Sjamie	char *nname;
366194869Sjamie	char **param_values;
367222465Sbz	int i, ai, jid, count, n, spc;
368192896Sjamie	char ipbuf[INET6_ADDRSTRLEN];
369113277Smike
370194869Sjamie	jid = jailparam_get(params, nparams, jflags);
371194869Sjamie	if (jid < 0)
372194869Sjamie		return jid;
373192896Sjamie	if (pflags & PRINT_VERBOSE) {
374192896Sjamie		printf("%6d  %-29.29s %.74s\n"
375192896Sjamie		       "%6s  %-29.29s %.74s\n"
376192896Sjamie		       "%6s  %-6d\n",
377194869Sjamie		    *(int *)params[0].jp_value,
378194869Sjamie		    (char *)params[1].jp_value,
379194869Sjamie		    (char *)params[2].jp_value,
380192896Sjamie		    "",
381194869Sjamie		    (char *)params[3].jp_value,
382194869Sjamie		    *(int *)params[4].jp_value ? "DYING" : "ACTIVE",
383192896Sjamie		    "",
384194869Sjamie		    *(int *)params[5].jp_value);
385222465Sbz		n = 6;
386222465Sbz#ifdef INET
387224841Sbz		if (ip4_ok && !strcmp(params[n].jp_name, "ip4.addr")) {
388222465Sbz			count = params[n].jp_valuelen / sizeof(struct in_addr);
389192896Sjamie			for (ai = 0; ai < count; ai++)
390222465Sbz				if (inet_ntop(AF_INET,
391222465Sbz				    &((struct in_addr *)params[n].jp_value)[ai],
392222465Sbz				    ipbuf, sizeof(ipbuf)) == NULL)
393222465Sbz					err(1, "inet_ntop");
394222465Sbz				else
395222465Sbz					printf("%6s  %-15.15s\n", "", ipbuf);
396222465Sbz			n++;
397222465Sbz		}
398222465Sbz#endif
399222465Sbz#ifdef INET6
400222465Sbz		if (ip6_ok && !strcmp(params[n].jp_name, "ip6.addr")) {
401222465Sbz			count = params[n].jp_valuelen / sizeof(struct in6_addr);
402222465Sbz			for (ai = 0; ai < count; ai++)
403194869Sjamie				if (inet_ntop(AF_INET6,
404222465Sbz				    &((struct in6_addr *)
405222465Sbz					params[n].jp_value)[ai],
406192896Sjamie				    ipbuf, sizeof(ipbuf)) == NULL)
407192896Sjamie					err(1, "inet_ntop");
408192896Sjamie				else
409196137Sbz					printf("%6s  %s\n", "", ipbuf);
410222465Sbz			n++;
411192896Sjamie		}
412222465Sbz#endif
413250736Sdes	} else if (pflags & PRINT_DEFAULT) {
414250736Sdes		if (pflags & PRINT_JAIL_NAME)
415250736Sdes			printf(" %-15s ", (char *)params[0].jp_value);
416250736Sdes		else
417250736Sdes			printf("%6d  ", *(int *)params[0].jp_value);
418250736Sdes		printf("%-15.15s %-29.29s %.74s\n",
419222465Sbz#ifdef INET
420222465Sbz		    (!ip4_ok || params[1].jp_valuelen == 0) ? "-"
421194869Sjamie		    : inet_ntoa(*(struct in_addr *)params[1].jp_value),
422232613Sbz		    (char *)params[2-!ip4_ok].jp_value,
423232613Sbz		    (char *)params[3-!ip4_ok].jp_value);
424222465Sbz#else
425223224Sbz		    "-",
426232613Sbz		    (char *)params[1].jp_value,
427232613Sbz		    (char *)params[2].jp_value);
428222465Sbz#endif
429250736Sdes	} else {
430194869Sjamie		param_values = alloca(nparams * sizeof(*param_values));
431194869Sjamie		for (i = 0; i < nparams; i++) {
432194869Sjamie			if (!(params[i].jp_flags & JP_USER))
433194869Sjamie				continue;
434194869Sjamie			param_values[i] = jailparam_export(params + i);
435194869Sjamie			if (param_values[i] == NULL)
436194869Sjamie				errx(1, "%s", jail_errmsg);
437194869Sjamie		}
438192896Sjamie		for (i = spc = 0; i < nparams; i++) {
439194869Sjamie			if (!(params[i].jp_flags & JP_USER))
440149081Spjd				continue;
441192896Sjamie			if ((pflags & PRINT_SKIP) &&
442194869Sjamie			    ((!(params[i].jp_ctltype &
443194869Sjamie				(CTLFLAG_WR | CTLFLAG_TUN))) ||
444195870Sjamie			     (param_parent[i] >= 0 &&
445195870Sjamie			      *(int *)params[param_parent[i]].jp_value !=
446195870Sjamie			      JAIL_SYS_NEW)))
447192896Sjamie				continue;
448192896Sjamie			if (spc)
449192896Sjamie				putchar(' ');
450192896Sjamie			else
451192896Sjamie				spc = 1;
452192896Sjamie			if (pflags & PRINT_NAMEVAL) {
453192896Sjamie				/*
454192896Sjamie				 * Generally "name=value", but for booleans
455192896Sjamie				 * either "name" or "noname".
456192896Sjamie				 */
457194869Sjamie				if (params[i].jp_flags &
458194869Sjamie				    (JP_BOOL | JP_NOBOOL)) {
459194869Sjamie					if (*(int *)params[i].jp_value)
460194869Sjamie						printf("%s", params[i].jp_name);
461192896Sjamie					else {
462194869Sjamie						nname = (params[i].jp_flags &
463194869Sjamie						    JP_NOBOOL) ?
464194869Sjamie						    nononame(params[i].jp_name)
465194869Sjamie						    : noname(params[i].jp_name);
466192896Sjamie						printf("%s", nname);
467192896Sjamie						free(nname);
468192896Sjamie					}
469194869Sjamie					continue;
470192896Sjamie				}
471194869Sjamie				printf("%s=", params[i].jp_name);
472149081Spjd			}
473194869Sjamie			if (params[i].jp_valuelen == 0) {
474192896Sjamie				if (pflags & PRINT_QUOTED)
475192896Sjamie					printf("\"\"");
476192896Sjamie				else if (!(pflags & PRINT_NAMEVAL))
477192896Sjamie					putchar('-');
478194869Sjamie			} else
479194869Sjamie				quoted_print(param_values[i]);
480113277Smike		}
481192896Sjamie		putchar('\n');
482194869Sjamie		for (i = 0; i < nparams; i++)
483194869Sjamie			if (params[i].jp_flags & JP_USER)
484194869Sjamie				free(param_values[i]);
485149081Spjd	}
486192896Sjamie	return (jid);
487192896Sjamie}
488113277Smike
489192896Sjamiestatic void
490194869Sjamiequoted_print(char *str)
491192896Sjamie{
492192896Sjamie	int c, qc;
493192896Sjamie	char *p = str;
494192896Sjamie
495192896Sjamie	/* An empty string needs quoting. */
496192896Sjamie	if (!*p) {
497192896Sjamie		fputs("\"\"", stdout);
498192896Sjamie		return;
499113277Smike	}
500192896Sjamie
501192896Sjamie	/*
502192896Sjamie	 * The value will be surrounded by quotes if it contains spaces
503192896Sjamie	 * or quotes.
504192896Sjamie	 */
505192896Sjamie	qc = strchr(p, '\'') ? '"'
506192896Sjamie	    : strchr(p, '"') ? '\''
507192896Sjamie	    : strchr(p, ' ') || strchr(p, '\t') ? '"'
508192896Sjamie	    : 0;
509192896Sjamie	if (qc)
510192896Sjamie		putchar(qc);
511194869Sjamie	while ((c = *p++)) {
512192896Sjamie		if (c == '\\' || c == qc)
513192896Sjamie			putchar('\\');
514192896Sjamie		putchar(c);
515185435Sbz	}
516192896Sjamie	if (qc)
517192896Sjamie		putchar(qc);
518113277Smike}
519