xref: /freebsd/usr.sbin/rpc.lockd/kern.c (revision 4d65a7c6951cea0333f1a0c1b32c38489cdfa6c5)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1997 Berkeley Software Design, Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Berkeley Software Design Inc's name may not be used to endorse or
15  *    promote products derived from this software without specific prior
16  *    written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  *      from BSDI kern.c,v 1.2 1998/11/25 22:38:27 don Exp
31  */
32 
33 #include <sys/param.h>
34 #include <sys/mount.h>
35 #include <sys/queue.h>
36 #include <sys/socket.h>
37 #include <sys/stat.h>
38 
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41 
42 #include <err.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <paths.h>
46 #include <pwd.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <syslog.h>
51 #include <unistd.h>
52 #include <netdb.h>
53 
54 #include "nlm_prot.h"
55 #include <nfs/nfsproto.h>
56 #include <nfs/nfs_lock.h>
57 
58 #include "lockd.h"
59 #include "lockd_lock.h"
60 #include <nfsclient/nfs.h>
61 
62 #define DAEMON_USERNAME	"daemon"
63 
64 /* Lock request owner. */
65 typedef struct __owner {
66 	pid_t	 pid;				/* Process ID. */
67 	time_t	 tod;				/* Time-of-day. */
68 } OWNER;
69 static OWNER owner;
70 
71 static char hostname[MAXHOSTNAMELEN + 1];	/* Hostname. */
72 static int devfd;
73 
74 static void	client_cleanup(void);
75 static const char *from_addr(struct sockaddr *);
76 int	lock_request(LOCKD_MSG *);
77 static void	set_auth(CLIENT *cl, struct xucred *ucred);
78 void	show(LOCKD_MSG *);
79 int	test_request(LOCKD_MSG *);
80 int	unlock_request(LOCKD_MSG *);
81 
82 static int
nfslockdans(int vers,struct lockd_ans * ansp)83 nfslockdans(int vers, struct lockd_ans *ansp)
84 {
85 
86 	ansp->la_vers = vers;
87 	return (write(devfd, ansp, sizeof *ansp) <= 0);
88 }
89 
90 /*
91  * will break because fifo needs to be repopened when EOF'd
92  */
93 #define lockd_seteuid(uid)	seteuid(uid)
94 
95 #define d_calls (debug_level > 1)
96 #define d_args (debug_level > 2)
97 
98 static const char *
from_addr(struct sockaddr * saddr)99 from_addr(struct sockaddr *saddr)
100 {
101 	static char inet_buf[INET6_ADDRSTRLEN];
102 
103 	if (getnameinfo(saddr, saddr->sa_len, inet_buf, sizeof(inet_buf),
104 			NULL, 0, NI_NUMERICHOST) == 0)
105 		return inet_buf;
106 	return "???";
107 }
108 
109 void
client_cleanup(void)110 client_cleanup(void)
111 {
112 	(void)lockd_seteuid(0);
113 	exit(-1);
114 }
115 
116 /*
117  * client_request --
118  *	Loop around messages from the kernel, forwarding them off to
119  *	NLM servers.
120  */
121 pid_t
client_request(void)122 client_request(void)
123 {
124 	LOCKD_MSG msg;
125 	int nr, ret;
126 	pid_t child;
127 	uid_t daemon_uid;
128 	struct passwd *pw;
129 
130 	/* Open the dev . */
131 	devfd = open(_PATH_DEV _PATH_NFSLCKDEV, O_RDWR | O_NONBLOCK);
132 	if (devfd < 0) {
133 		syslog(LOG_ERR, "open: %s: %m", _PATH_NFSLCKDEV);
134 		goto err;
135 	}
136 
137 	signal(SIGPIPE, SIG_IGN);
138 
139 	/*
140 	 * Create a separate process, the client code is really a separate
141 	 * daemon that shares a lot of code.
142 	 */
143 	switch (child = fork()) {
144 	case -1:
145 		err(1, "fork");
146 	case 0:
147 		setproctitle("client");
148 		break;
149 	default:
150 		setproctitle("server");
151 		return (child);
152 	}
153 
154 	signal(SIGHUP, (sig_t)client_cleanup);
155 	signal(SIGTERM, (sig_t)client_cleanup);
156 
157 	/* Setup. */
158 	(void)time(&owner.tod);
159 	owner.pid = getpid();
160 	(void)gethostname(hostname, sizeof(hostname) - 1);
161 
162 	pw = getpwnam(DAEMON_USERNAME);
163 	if (pw == NULL) {
164 		syslog(LOG_ERR, "getpwnam: %s: %m", DAEMON_USERNAME);
165 		goto err;
166 	}
167 	daemon_uid = pw->pw_uid;
168 	/* drop our root privileges */
169 	(void)lockd_seteuid(daemon_uid);
170 
171 	for (;;) {
172 		/* Read the fixed length message. */
173 		if ((nr = read(devfd, &msg, sizeof(msg))) == sizeof(msg)) {
174 			if (d_args)
175 				show(&msg);
176 
177 			if (msg.lm_version != LOCKD_MSG_VERSION) {
178 				syslog(LOG_ERR,
179 				    "unknown msg type: %d", msg.lm_version);
180 			}
181 			/*
182 			 * Send it to the NLM server and don't grant the lock
183 			 * if we fail for any reason.
184 			 */
185 			switch (msg.lm_fl.l_type) {
186 			case F_RDLCK:
187 			case F_WRLCK:
188 				if (msg.lm_getlk)
189 					ret = test_request(&msg);
190 				else
191 					ret = lock_request(&msg);
192 				break;
193 			case F_UNLCK:
194 				ret = unlock_request(&msg);
195 				break;
196 			default:
197 				ret = 1;
198 				syslog(LOG_ERR,
199 				    "unknown lock type: %d", msg.lm_fl.l_type);
200 				break;
201 			}
202 			if (ret) {
203 				struct lockd_ans ans;
204 
205 				ans.la_msg_ident = msg.lm_msg_ident;
206 				ans.la_errno = EHOSTUNREACH;
207 
208 				if (nfslockdans(LOCKD_ANS_VERSION, &ans)) {
209 					syslog((errno == EPIPE ? LOG_INFO :
210 						LOG_ERR), "process %lu: %m",
211 						(u_long)msg.lm_msg_ident.pid);
212 				}
213 			}
214 		} else if (nr == -1) {
215 			if (errno != EAGAIN) {
216 				syslog(LOG_ERR, "read: %s: %m", _PATH_NFSLCKDEV);
217 				goto err;
218 			}
219 		} else if (nr != 0) {
220 			syslog(LOG_ERR,
221 			    "%s: discard %d bytes", _PATH_NFSLCKDEV, nr);
222 		}
223 	}
224 
225 	/* Reached only on error. */
226 err:
227 	(void)lockd_seteuid(0);
228 	_exit (1);
229 }
230 
231 void
set_auth(CLIENT * cl,struct xucred * xucred)232 set_auth(CLIENT *cl, struct xucred *xucred)
233 {
234 	int ngroups;
235 
236 	ngroups = xucred->cr_ngroups - 1;
237 	if (ngroups > NGRPS)
238 		ngroups = NGRPS;
239         if (cl->cl_auth != NULL)
240                 cl->cl_auth->ah_ops->ah_destroy(cl->cl_auth);
241         cl->cl_auth = authunix_create(hostname,
242                         xucred->cr_uid,
243                         xucred->cr_groups[0],
244                         ngroups,
245                         &xucred->cr_groups[1]);
246 }
247 
248 
249 /*
250  * test_request --
251  *	Convert a lock LOCKD_MSG into an NLM request, and send it off.
252  */
253 int
test_request(LOCKD_MSG * msg)254 test_request(LOCKD_MSG *msg)
255 {
256 	CLIENT *cli;
257 	struct timeval timeout = {0, 0};	/* No timeout, no response. */
258 	char dummy;
259 
260 	if (d_calls)
261 		syslog(LOG_DEBUG, "test request: %s: %s to %s",
262 		    msg->lm_nfsv3 ? "V4" : "V1/3",
263 		    msg->lm_fl.l_type == F_WRLCK ? "write" : "read",
264 		    from_addr((struct sockaddr *)&msg->lm_addr));
265 
266 	if (msg->lm_nfsv3) {
267 		struct nlm4_testargs arg4;
268 
269 		arg4.cookie.n_bytes = (char *)&msg->lm_msg_ident;
270 		arg4.cookie.n_len = sizeof(msg->lm_msg_ident);
271 		arg4.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
272 		arg4.alock.caller_name = hostname;
273 		arg4.alock.fh.n_bytes = (char *)&msg->lm_fh;
274 		arg4.alock.fh.n_len = msg->lm_fh_len;
275 		arg4.alock.oh.n_bytes = (char *)&owner;
276 		arg4.alock.oh.n_len = sizeof(owner);
277 		arg4.alock.svid = msg->lm_msg_ident.pid;
278 		arg4.alock.l_offset = msg->lm_fl.l_start;
279 		arg4.alock.l_len = msg->lm_fl.l_len;
280 
281 		if ((cli = get_client(
282 		    (struct sockaddr *)&msg->lm_addr,
283 		    NLM_VERS4)) == NULL)
284 			return (1);
285 
286 		set_auth(cli, &msg->lm_cred);
287 		(void)clnt_call(cli, NLM_TEST_MSG,
288 		    (xdrproc_t)xdr_nlm4_testargs, &arg4,
289 		    (xdrproc_t)xdr_void, &dummy, timeout);
290 	} else {
291 		struct nlm_testargs arg;
292 
293 		arg.cookie.n_bytes = (char *)&msg->lm_msg_ident;
294 		arg.cookie.n_len = sizeof(msg->lm_msg_ident);
295 		arg.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
296 		arg.alock.caller_name = hostname;
297 		arg.alock.fh.n_bytes = (char *)&msg->lm_fh;
298 		arg.alock.fh.n_len = msg->lm_fh_len;
299 		arg.alock.oh.n_bytes = (char *)&owner;
300 		arg.alock.oh.n_len = sizeof(owner);
301 		arg.alock.svid = msg->lm_msg_ident.pid;
302 		arg.alock.l_offset = msg->lm_fl.l_start;
303 		arg.alock.l_len = msg->lm_fl.l_len;
304 
305 		if ((cli = get_client(
306 		    (struct sockaddr *)&msg->lm_addr,
307 		    NLM_VERS)) == NULL)
308 			return (1);
309 
310 		set_auth(cli, &msg->lm_cred);
311 		(void)clnt_call(cli, NLM_TEST_MSG,
312 		    (xdrproc_t)xdr_nlm_testargs, &arg,
313 		    (xdrproc_t)xdr_void, &dummy, timeout);
314 	}
315 	return (0);
316 }
317 
318 /*
319  * lock_request --
320  *	Convert a lock LOCKD_MSG into an NLM request, and send it off.
321  */
322 int
lock_request(LOCKD_MSG * msg)323 lock_request(LOCKD_MSG *msg)
324 {
325 	CLIENT *cli;
326 	struct nlm4_lockargs arg4;
327 	struct nlm_lockargs arg;
328 	struct timeval timeout = {0, 0};	/* No timeout, no response. */
329 	char dummy;
330 
331 	if (d_calls)
332 		syslog(LOG_DEBUG, "lock request: %s: %s to %s",
333 		    msg->lm_nfsv3 ? "V4" : "V1/3",
334 		    msg->lm_fl.l_type == F_WRLCK ? "write" : "read",
335 		    from_addr((struct sockaddr *)&msg->lm_addr));
336 
337 	if (msg->lm_nfsv3) {
338 		arg4.cookie.n_bytes = (char *)&msg->lm_msg_ident;
339 		arg4.cookie.n_len = sizeof(msg->lm_msg_ident);
340 		arg4.block = msg->lm_wait ? 1 : 0;
341 		arg4.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
342 		arg4.alock.caller_name = hostname;
343 		arg4.alock.fh.n_bytes = (char *)&msg->lm_fh;
344 		arg4.alock.fh.n_len = msg->lm_fh_len;
345 		arg4.alock.oh.n_bytes = (char *)&owner;
346 		arg4.alock.oh.n_len = sizeof(owner);
347 		arg4.alock.svid = msg->lm_msg_ident.pid;
348 		arg4.alock.l_offset = msg->lm_fl.l_start;
349 		arg4.alock.l_len = msg->lm_fl.l_len;
350 		arg4.reclaim = 0;
351 		arg4.state = nsm_state;
352 
353 		if ((cli = get_client(
354 		    (struct sockaddr *)&msg->lm_addr,
355 		    NLM_VERS4)) == NULL)
356 			return (1);
357 
358 		set_auth(cli, &msg->lm_cred);
359 		(void)clnt_call(cli, NLM_LOCK_MSG,
360 		    (xdrproc_t)xdr_nlm4_lockargs, &arg4,
361 		    (xdrproc_t)xdr_void, &dummy, timeout);
362 	} else {
363 		arg.cookie.n_bytes = (char *)&msg->lm_msg_ident;
364 		arg.cookie.n_len = sizeof(msg->lm_msg_ident);
365 		arg.block = msg->lm_wait ? 1 : 0;
366 		arg.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
367 		arg.alock.caller_name = hostname;
368 		arg.alock.fh.n_bytes = (char *)&msg->lm_fh;
369 		arg.alock.fh.n_len = msg->lm_fh_len;
370 		arg.alock.oh.n_bytes = (char *)&owner;
371 		arg.alock.oh.n_len = sizeof(owner);
372 		arg.alock.svid = msg->lm_msg_ident.pid;
373 		arg.alock.l_offset = msg->lm_fl.l_start;
374 		arg.alock.l_len = msg->lm_fl.l_len;
375 		arg.reclaim = 0;
376 		arg.state = nsm_state;
377 
378 		if ((cli = get_client(
379 		    (struct sockaddr *)&msg->lm_addr,
380 		    NLM_VERS)) == NULL)
381 			return (1);
382 
383 		set_auth(cli, &msg->lm_cred);
384 		(void)clnt_call(cli, NLM_LOCK_MSG,
385 		    (xdrproc_t)xdr_nlm_lockargs, &arg,
386 		    (xdrproc_t)xdr_void, &dummy, timeout);
387 	}
388 	return (0);
389 }
390 
391 /*
392  * unlock_request --
393  *	Convert an unlock LOCKD_MSG into an NLM request, and send it off.
394  */
395 int
unlock_request(LOCKD_MSG * msg)396 unlock_request(LOCKD_MSG *msg)
397 {
398 	CLIENT *cli;
399 	struct nlm4_unlockargs arg4;
400 	struct nlm_unlockargs arg;
401 	struct timeval timeout = {0, 0};	/* No timeout, no response. */
402 	char dummy;
403 
404 	if (d_calls)
405 		syslog(LOG_DEBUG, "unlock request: %s: to %s",
406 		    msg->lm_nfsv3 ? "V4" : "V1/3",
407 		    from_addr((struct sockaddr *)&msg->lm_addr));
408 
409 	if (msg->lm_nfsv3) {
410 		arg4.cookie.n_bytes = (char *)&msg->lm_msg_ident;
411 		arg4.cookie.n_len = sizeof(msg->lm_msg_ident);
412 		arg4.alock.caller_name = hostname;
413 		arg4.alock.fh.n_bytes = (char *)&msg->lm_fh;
414 		arg4.alock.fh.n_len = msg->lm_fh_len;
415 		arg4.alock.oh.n_bytes = (char *)&owner;
416 		arg4.alock.oh.n_len = sizeof(owner);
417 		arg4.alock.svid = msg->lm_msg_ident.pid;
418 		arg4.alock.l_offset = msg->lm_fl.l_start;
419 		arg4.alock.l_len = msg->lm_fl.l_len;
420 
421 		if ((cli = get_client(
422 		    (struct sockaddr *)&msg->lm_addr,
423 		    NLM_VERS4)) == NULL)
424 			return (1);
425 
426 		set_auth(cli, &msg->lm_cred);
427 		(void)clnt_call(cli, NLM_UNLOCK_MSG,
428 		    (xdrproc_t)xdr_nlm4_unlockargs, &arg4,
429 		    (xdrproc_t)xdr_void, &dummy, timeout);
430 	} else {
431 		arg.cookie.n_bytes = (char *)&msg->lm_msg_ident;
432 		arg.cookie.n_len = sizeof(msg->lm_msg_ident);
433 		arg.alock.caller_name = hostname;
434 		arg.alock.fh.n_bytes = (char *)&msg->lm_fh;
435 		arg.alock.fh.n_len = msg->lm_fh_len;
436 		arg.alock.oh.n_bytes = (char *)&owner;
437 		arg.alock.oh.n_len = sizeof(owner);
438 		arg.alock.svid = msg->lm_msg_ident.pid;
439 		arg.alock.l_offset = msg->lm_fl.l_start;
440 		arg.alock.l_len = msg->lm_fl.l_len;
441 
442 		if ((cli = get_client(
443 		    (struct sockaddr *)&msg->lm_addr,
444 		    NLM_VERS)) == NULL)
445 			return (1);
446 
447 		set_auth(cli, &msg->lm_cred);
448 		(void)clnt_call(cli, NLM_UNLOCK_MSG,
449 		    (xdrproc_t)xdr_nlm_unlockargs, &arg,
450 		    (xdrproc_t)xdr_void, &dummy, timeout);
451 	}
452 
453 	return (0);
454 }
455 
456 int
lock_answer(int pid,netobj * netcookie,int result,int * pid_p,int version)457 lock_answer(int pid, netobj *netcookie, int result, int *pid_p, int version)
458 {
459 	struct lockd_ans ans;
460 
461 	if (netcookie->n_len != sizeof(ans.la_msg_ident)) {
462 		if (pid == -1) {	/* we're screwed */
463 			syslog(LOG_ERR, "inedible nlm cookie");
464 			return -1;
465 		}
466 		ans.la_msg_ident.pid = pid;
467 		ans.la_msg_ident.msg_seq = -1;
468 	} else {
469 		memcpy(&ans.la_msg_ident, netcookie->n_bytes,
470 		    sizeof(ans.la_msg_ident));
471 	}
472 
473 	if (d_calls)
474 		syslog(LOG_DEBUG, "lock answer: pid %lu: %s %d",
475 		    (unsigned long)ans.la_msg_ident.pid,
476 		    version == NLM_VERS4 ? "nlmv4" : "nlmv3",
477 		    result);
478 
479 	ans.la_set_getlk_pid = 0;
480 	if (version == NLM_VERS4)
481 		switch (result) {
482 		case nlm4_granted:
483 			ans.la_errno = 0;
484 			break;
485 		default:
486 			ans.la_errno = EACCES;
487 			break;
488 		case nlm4_denied:
489 			if (pid_p == NULL)
490 				ans.la_errno = EAGAIN;
491 			else {
492 				/* this is an answer to a nlm_test msg */
493 				ans.la_set_getlk_pid = 1;
494 				ans.la_getlk_pid = *pid_p;
495 				ans.la_errno = 0;
496 			}
497 			break;
498 		case nlm4_denied_nolocks:
499 			ans.la_errno = EAGAIN;
500 			break;
501 		case nlm4_blocked:
502 			return -1;
503 			/* NOTREACHED */
504 		case nlm4_denied_grace_period:
505 			ans.la_errno = EAGAIN;
506 			break;
507 		case nlm4_deadlck:
508 			ans.la_errno = EDEADLK;
509 			break;
510 		case nlm4_rofs:
511 			ans.la_errno = EROFS;
512 			break;
513 		case nlm4_stale_fh:
514 			ans.la_errno = ESTALE;
515 			break;
516 		case nlm4_fbig:
517 			ans.la_errno = EFBIG;
518 			break;
519 		case nlm4_failed:
520 			ans.la_errno = EACCES;
521 			break;
522 		}
523 	else
524 		switch (result) {
525 		case nlm_granted:
526 			ans.la_errno = 0;
527 			break;
528 		default:
529 			ans.la_errno = EACCES;
530 			break;
531 		case nlm_denied:
532 			if (pid_p == NULL)
533 				ans.la_errno = EAGAIN;
534 			else {
535 				/* this is an answer to a nlm_test msg */
536 				ans.la_set_getlk_pid = 1;
537 				ans.la_getlk_pid = *pid_p;
538 				ans.la_errno = 0;
539 			}
540 			break;
541 		case nlm_denied_nolocks:
542 			ans.la_errno = EAGAIN;
543 			break;
544 		case nlm_blocked:
545 			return -1;
546 			/* NOTREACHED */
547 		case nlm_denied_grace_period:
548 			ans.la_errno = EAGAIN;
549 			break;
550 		case nlm_deadlck:
551 			ans.la_errno = EDEADLK;
552 			break;
553 		}
554 
555 	if (nfslockdans(LOCKD_ANS_VERSION, &ans)) {
556 		syslog(((errno == EPIPE || errno == ESRCH) ?
557 			LOG_INFO : LOG_ERR),
558 			"process %lu: %m", (u_long)ans.la_msg_ident.pid);
559 		return -1;
560 	}
561 	return 0;
562 }
563 
564 /*
565  * show --
566  *	Display the contents of a kernel LOCKD_MSG structure.
567  */
568 void
show(LOCKD_MSG * mp)569 show(LOCKD_MSG *mp)
570 {
571 	static char hex[] = "0123456789abcdef";
572 	size_t len;
573 	u_int8_t *p, *t, buf[NFS_SMALLFH*3+1];
574 
575 	syslog(LOG_DEBUG, "process ID: %lu\n", (long)mp->lm_msg_ident.pid);
576 
577 	for (t = buf, p = (u_int8_t *)mp->lm_fh,
578 	    len = mp->lm_fh_len;
579 	    len > 0; ++p, --len) {
580 		*t++ = '\\';
581 		*t++ = hex[(*p & 0xf0) >> 4];
582 		*t++ = hex[*p & 0x0f];
583 	}
584 	*t = '\0';
585 
586 	syslog(LOG_DEBUG, "fh_len %d, fh %s\n", (int)mp->lm_fh_len, buf);
587 
588 	/* Show flock structure. */
589 	syslog(LOG_DEBUG, "start %llu; len %llu; pid %lu; type %d; whence %d\n",
590 	    (unsigned long long)mp->lm_fl.l_start,
591 	    (unsigned long long)mp->lm_fl.l_len, (u_long)mp->lm_fl.l_pid,
592 	    mp->lm_fl.l_type, mp->lm_fl.l_whence);
593 
594 	/* Show wait flag. */
595 	syslog(LOG_DEBUG, "wait was %s\n", mp->lm_wait ? "set" : "not set");
596 }
597