xref: /illumos-gate/usr/src/cmd/ttymon/ttymon.c (revision 0245b61fd282e95735b173b8d95be0d6688163b4)
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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved	*/
28 
29 #include <ctype.h>
30 #include <stdio_ext.h>
31 #include <stdlib.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <poll.h>
35 #include <string.h>
36 #include <signal.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/stropts.h>
40 #include <sys/resource.h>
41 #include <sys/termios.h>
42 #include <pwd.h>
43 #include <grp.h>
44 #include <unistd.h>
45 #include <ulimit.h>
46 #include <libdevinfo.h>
47 
48 #include "sac.h"
49 #include "ttymon.h"
50 #include "tmstruct.h"
51 #include "tmextern.h"
52 
53 static	int	Initialized;
54 
55 static	void	initialize(void);
56 static	void	open_all(void);
57 static	int	set_poll(struct pollfd *);
58 static	int	check_spawnlimit(struct pmtab *);
59 static	int	mod_ttydefs(void);
60 
61 void	open_device(struct pmtab *);
62 void	set_softcar(struct pmtab *);
63 
64 /*
65  *	ttymon	- a port monitor under SAC
66  *		- monitor ports, set terminal modes, baud rate
67  *		  and line discipline for the port
68  *		- invoke service on port if connection request received
69  *		- Usage: ttymon
70  *			 ttymon -g [options]
71  *			 Valid options are
72  *			 -h
73  *			 -d device
74  *			 -l ttylabel
75  *			 -t timeout
76  *			 -m modules
77  *			 -p prompt
78  *
79  *		- ttymon without args is invoked by SAC
80  *		- ttymon -g is invoked by process that needs to
81  *		  have login service on the fly
82  */
83 
84 int
85 main(int argc, char *argv[])
86 {
87 	int	nfds;
88 
89 	/*
90 	 * Only the superuser should execute this command.
91 	 */
92 	if (getuid() != 0)
93 		return (1);
94 
95 	/* remember original signal mask and dispositions */
96 	(void) sigprocmask(SIG_SETMASK, NULL, &Origmask);
97 	(void) sigaction(SIGINT, NULL, &Sigint);
98 	(void) sigaction(SIGALRM, NULL, &Sigalrm);
99 	(void) sigaction(SIGPOLL, NULL, &Sigpoll);
100 	(void) sigaction(SIGQUIT, NULL, &Sigquit);
101 	(void) sigaction(SIGCLD, NULL, &Sigcld);
102 	(void) sigaction(SIGTERM, NULL, &Sigterm);
103 #ifdef	DEBUG
104 	(void) sigaction(SIGUSR1, NULL, &Sigusr1);
105 	(void) sigaction(SIGUSR2, NULL, &Sigusr2);
106 #endif
107 
108 	/*
109 	 * SIGQUIT needs to be ignored. Otherwise, hitting ^\ from
110 	 * console kills ttymon.
111 	 */
112 	(void) signal(SIGQUIT, SIG_IGN);
113 
114 	if ((argc > 1) || (strcmp(lastname(argv[0]), "getty") == 0)) {
115 		ttymon_express(argc, argv);
116 		return (1);	/*NOTREACHED*/
117 	}
118 
119 	initialize();
120 
121 	for (;;) {
122 		nfds = set_poll(Pollp);
123 		if (!Reread_flag) {
124 			if (nfds > 0)
125 				do_poll(Pollp, nfds);
126 			else
127 				(void) pause();
128 		}
129 		/*
130 		 * READDB messages may arrive during poll or pause.
131 		 * So the flag needs to be checked again.
132 		 */
133 		if (Reread_flag) {
134 			Reread_flag = FALSE;
135 			re_read();
136 		}
137 		while (Retry) {
138 			Retry = FALSE;
139 			open_all();
140 		}
141 	}
142 }
143 
144 static	void
145 initialize(void)
146 {
147 	struct	pmtab	*tp;
148 	struct passwd *pwdp;
149 	struct	group	*gp;
150 	struct	rlimit rlimit;
151 
152 	Initialized = FALSE;
153 	/*
154 	 * get_environ() must be called first,
155 	 * otherwise we don't know where the log file is
156 	 */
157 	get_environ();
158 	openttymonlog();
159 	openpid();
160 	openpipes();
161 	setup_PCpipe();
162 
163 	log("PMTAG: %s", Tag);
164 	log("Starting state: %s",
165 	    (State == PM_ENABLED) ? "enabled" : "disabled");
166 
167 #ifdef	DEBUG
168 	opendebug(FALSE);
169 	debug("***** ttymon in initialize *****");
170 	log("debug mode is \t on");
171 #endif
172 
173 	catch_signals();
174 
175 	/* register to receive SIGPOLL when data comes to pmpipe */
176 	if (ioctl(Pfd, I_SETSIG, S_INPUT) < 0)
177 		fatal("I_SETSIG on pmpipe failed: %s", strerror(errno));
178 
179 	sacpoll(); /* this is needed because there may be data already */
180 
181 	Maxfiles = (int)ulimit(4, 0L);	/* get max number of open files */
182 	if (Maxfiles < 0)
183 		fatal("ulimit(4,0L) failed: %s", strerror(errno));
184 
185 	if (getrlimit(RLIMIT_NOFILE, &Rlimit) == -1)
186 		fatal("getrlimit failed: %s", strerror(errno));
187 
188 	rlimit.rlim_cur = rlimit.rlim_max = Rlimit.rlim_max;
189 	if (setrlimit(RLIMIT_NOFILE, &rlimit) == -1)
190 		fatal("setrlimit failed: %s", strerror(errno));
191 
192 	(void) enable_extended_FILE_stdio(-1, -1);
193 
194 	Maxfiles = rlimit.rlim_cur;
195 	Maxfds = Maxfiles - FILE_RESERVED;
196 
197 	log("max open files = %d", Maxfiles);
198 	log("max ports ttymon can monitor = %d", Maxfds);
199 
200 	read_pmtab();
201 
202 	/*
203 	 * setup poll array
204 	 *	- we allocate 10 extra pollfd so that
205 	 *	  we do not have to re-malloc when there is
206 	 *	  minor fluctuation in Nentries
207 	 */
208 	Npollfd = Nentries + 10;
209 	if (Npollfd > Maxfds)
210 		Npollfd = Maxfds;
211 	if ((Pollp = (struct pollfd *)
212 	    malloc((unsigned)(Npollfd * sizeof (struct pollfd))))
213 	    == (struct pollfd *)NULL)
214 		fatal("malloc for Pollp failed");
215 
216 	(void) mod_ttydefs();	/* just to initialize Mtime */
217 	if (check_version(TTYDEFS_VERS, TTYDEFS) != 0)
218 		fatal("check /etc/ttydefs version failed");
219 
220 	read_ttydefs(NULL, FALSE);
221 
222 	/* initialize global variables, Uucp_uid & Tty_gid */
223 	if ((pwdp = getpwnam(UUCP)) != NULL)
224 		Uucp_uid = pwdp->pw_uid;
225 	if ((gp = getgrnam(TTY)) == NULL)
226 		log("no group entry for <tty>, default is used");
227 	else
228 		Tty_gid = gp->gr_gid;
229 	endgrent();
230 	endpwent();
231 #ifdef	DEBUG
232 	debug("Uucp_uid = %u, Tty_gid = %u", Uucp_uid, Tty_gid);
233 #endif
234 
235 	log("Initialization Completed");
236 
237 	/* open the devices ttymon monitors */
238 	Retry = TRUE;
239 	while (Retry) {
240 		Retry = FALSE;
241 		for (tp = PMtab; tp; tp = tp->p_next) {
242 			if ((tp->p_status > 0) && (tp->p_fd == 0) &&
243 			    (tp->p_childpid == 0) &&
244 			    !(tp->p_ttyflags & I_FLAG) &&
245 			    (!((State == PM_DISABLED) &&
246 			    ((tp->p_dmsg == NULL)||(*(tp->p_dmsg) == '\0'))))) {
247 				open_device(tp);
248 				if (tp->p_fd > 0)
249 					got_carrier(tp);
250 			}
251 		}
252 	}
253 	Initialized = TRUE;
254 }
255 
256 static	void	free_defs(void);
257 
258 /*
259  *	open_all - open devices in pmtab if the entry is
260  *	         - valid, fd = 0, and pid = 0
261  */
262 static void
263 open_all(void)
264 {
265 	struct	pmtab	*tp;
266 	int	check_modtime;
267 	sigset_t cset;
268 	sigset_t tset;
269 
270 #ifdef	DEBUG
271 	debug("in open_all");
272 #endif
273 	check_modtime = TRUE;
274 
275 	for (tp = PMtab; tp; tp = tp->p_next) {
276 		if ((tp->p_status > 0) && (tp->p_fd == 0) &&
277 		    (tp->p_childpid == 0) &&
278 		    !(tp->p_ttyflags & I_FLAG) && (!((State == PM_DISABLED) &&
279 		    ((tp->p_dmsg == NULL)||(*(tp->p_dmsg) == '\0'))))) {
280 			/*
281 			 * if we have not check modification time and
282 			 * /etc/ttydefs was modified, need to re-read it
283 			 */
284 			if (check_modtime && mod_ttydefs()) {
285 				check_modtime = FALSE;
286 				(void) sigprocmask(SIG_SETMASK, NULL, &cset);
287 				tset = cset;
288 				(void) sigaddset(&tset, SIGCLD);
289 				(void) sigprocmask(SIG_SETMASK, &tset, NULL);
290 				free_defs();
291 #ifdef	DEBUG
292 				debug("/etc/ttydefs is modified, re-read it");
293 #endif
294 				read_ttydefs(NULL, FALSE);
295 				(void) sigprocmask(SIG_SETMASK, &cset, NULL);
296 			}
297 			open_device(tp);
298 			if (tp->p_fd > 0)
299 				got_carrier(tp);
300 		} else if (((tp->p_status == LOCKED) ||
301 		    (tp->p_status == SESSION) ||
302 		    (tp->p_status == UNACCESS)) &&
303 		    (tp->p_fd > 0) &&
304 		    (!((State == PM_DISABLED) &&
305 		    ((tp->p_dmsg == NULL)||(*(tp->p_dmsg) == '\0'))))) {
306 			if (check_modtime && mod_ttydefs()) {
307 				check_modtime = FALSE;
308 				(void) sigprocmask(SIG_SETMASK, NULL, &cset);
309 				tset = cset;
310 				(void) sigaddset(&tset, SIGCLD);
311 				(void) sigprocmask(SIG_SETMASK, &tset, NULL);
312 				free_defs();
313 #ifdef	DEBUG
314 				debug("/etc/ttydefs is modified, re-read it");
315 #endif
316 				read_ttydefs(NULL, FALSE);
317 				(void) sigprocmask(SIG_SETMASK, &cset, NULL);
318 			}
319 			tp->p_status = VALID;
320 			open_device(tp);
321 			if (tp->p_fd > 0)
322 				got_carrier(tp);
323 		}
324 	}
325 }
326 
327 void
328 set_softcar(struct pmtab *pmptr)
329 {
330 
331 	int fd, val = 0;
332 
333 #ifdef	DEBUG
334 	debug("in set_softcar");
335 #endif
336 	/*
337 	 * If soft carrier is not set one way or
338 	 * the other, leave it alone.
339 	 */
340 	if (*pmptr->p_softcar == '\0')
341 		return;
342 
343 	if (*pmptr->p_softcar == 'y')
344 		val = 1;
345 
346 	if ((fd = open(pmptr->p_device, O_RDONLY|O_NONBLOCK|O_NOCTTY)) < 0) {
347 		log("open (%s) failed: %s", pmptr->p_device, strerror(errno));
348 		return;
349 	}
350 
351 	if (ioctl(fd, TIOCSSOFTCAR, &val) < 0)
352 		log("set soft-carrier (%s) failed: %s", pmptr->p_device,
353 		    strerror(errno));
354 
355 	(void) close(fd);
356 }
357 
358 
359 /*
360  *	open_device(pmptr)	- open the device
361  *				- check device lock
362  *				- change owner of device
363  *				- push line disciplines
364  *				- set termio
365  */
366 
367 void
368 open_device(struct pmtab *pmptr)
369 {
370 	int	fd, tmpfd;
371 	struct	sigaction	sigact;
372 
373 #ifdef	DEBUG
374 	debug("in open_device");
375 #endif
376 
377 	if (pmptr->p_status == GETTY) {
378 		revokedevaccess(pmptr->p_device, 0, 0, 0);
379 
380 		if ((fd = open(pmptr->p_device, O_RDWR)) == -1)
381 			fatal("open (%s) failed: %s", pmptr->p_device,
382 			    strerror(errno));
383 
384 	} else {
385 		if (check_spawnlimit(pmptr) == -1) {
386 			pmptr->p_status = NOTVALID;
387 			log("service <%s> is respawning too rapidly",
388 			    pmptr->p_tag);
389 			return;
390 		}
391 		if (pmptr->p_fd > 0) { /* file already open */
392 			fd = pmptr->p_fd;
393 			pmptr->p_fd = 0;
394 		} else if ((fd = open(pmptr->p_device, O_RDWR|O_NONBLOCK))
395 		    == -1) {
396 			log("open (%s) failed: %s", pmptr->p_device,
397 			    strerror(errno));
398 			if ((errno ==  ENODEV) || (errno == EBUSY)) {
399 				pmptr->p_status = UNACCESS;
400 				Nlocked++;
401 				if (Nlocked == 1) {
402 					sigact.sa_flags = 0;
403 					sigact.sa_handler = sigalarm;
404 					(void) sigemptyset(&sigact.sa_mask);
405 					(void) sigaction(SIGALRM, &sigact,
406 					    NULL);
407 					(void) alarm(ALARMTIME);
408 				}
409 			} else
410 				Retry = TRUE;
411 			return;
412 		}
413 		/* set close-on-exec flag */
414 		if (fcntl(fd, F_SETFD, 1) == -1)
415 			fatal("F_SETFD fcntl failed: %s", strerror(errno));
416 
417 		if (tm_checklock(fd) != 0) {
418 			pmptr->p_status = LOCKED;
419 			(void) close(fd);
420 			Nlocked++;
421 			if (Nlocked == 1) {
422 				sigact.sa_flags = 0;
423 				sigact.sa_handler = sigalarm;
424 				(void) sigemptyset(&sigact.sa_mask);
425 				(void) sigaction(SIGALRM, &sigact, NULL);
426 				(void) alarm(ALARMTIME);
427 			}
428 			return;
429 		}
430 		if (check_session(fd) != 0) {
431 			if ((Initialized) && (pmptr->p_inservice != SESSION)) {
432 				log("Warning -- active session exists on <%s>",
433 				    pmptr->p_device);
434 			} else {
435 				/*
436 				 * this may happen if a service is running
437 				 * and ttymon dies and is restarted,
438 				 * or another process is running on the
439 				 * port.
440 				 */
441 				pmptr->p_status = SESSION;
442 				pmptr->p_inservice = 0;
443 				(void) close(fd);
444 				Nlocked++;
445 				if (Nlocked == 1) {
446 					sigact.sa_flags = 0;
447 					sigact.sa_handler = sigalarm;
448 					(void) sigemptyset(&sigact.sa_mask);
449 					(void) sigaction(SIGALRM, &sigact,
450 					    NULL);
451 					(void) alarm(ALARMTIME);
452 				}
453 				return;
454 			}
455 		}
456 		pmptr->p_inservice = 0;
457 	}
458 
459 	if (pmptr->p_ttyflags & H_FLAG) {
460 		/* drop DTR */
461 		(void) hang_up_line(fd);
462 		/*
463 		 * After hang_up_line, the stream is in STRHUP state.
464 		 * We need to do another open to reinitialize streams
465 		 * then we can close one fd
466 		 */
467 		if ((tmpfd = open(pmptr->p_device, O_RDWR|O_NONBLOCK)) == -1) {
468 			log("open (%s) failed: %s", pmptr->p_device,
469 			    strerror(errno));
470 			Retry = TRUE;
471 			(void) close(fd);
472 			return;
473 		}
474 		(void) close(tmpfd);
475 	}
476 
477 #ifdef DEBUG
478 	debug("open_device (%s), fd = %d", pmptr->p_device, fd);
479 #endif
480 
481 	/* Change ownership of the tty line to root/uucp and */
482 	/* set protections to only allow root/uucp to read the line. */
483 
484 	if (pmptr->p_ttyflags & (B_FLAG|C_FLAG))
485 		(void) fchown(fd, Uucp_uid, Tty_gid);
486 	else
487 		(void) fchown(fd, ROOTUID, Tty_gid);
488 	(void) fchmod(fd, 0620);
489 
490 	if ((pmptr->p_modules != NULL)&&(*(pmptr->p_modules) != '\0')) {
491 		if (push_linedisc(fd, pmptr->p_modules, pmptr->p_device)
492 		    == -1) {
493 			Retry = TRUE;
494 			(void) close(fd);
495 			return;
496 		}
497 	}
498 
499 	if (initial_termio(fd, pmptr) == -1)  {
500 		Retry = TRUE;
501 		(void) close(fd);
502 		return;
503 	}
504 
505 	(void) di_devperm_logout((const char *)pmptr->p_device);
506 	pmptr->p_fd = fd;
507 }
508 
509 /*
510  *	set_poll(fdp)	- put all fd's in a pollfd array
511  *			- set poll event to POLLIN and POLLMSG
512  *			- return number of fd to be polled
513  */
514 
515 static	int
516 set_poll(struct pollfd *fdp)
517 {
518 	struct	pmtab	*tp;
519 	int	nfd = 0;
520 
521 	for (tp = PMtab; tp; tp = tp->p_next) {
522 		if (tp->p_fd > 0)  {
523 			fdp->fd = tp->p_fd;
524 			fdp->events = POLLIN;
525 			fdp++;
526 			nfd++;
527 		}
528 	}
529 	return (nfd);
530 }
531 
532 /*
533  *	check_spawnlimit	- return 0 if spawnlimit is not reached
534  *				- otherwise return -1
535  */
536 static	int
537 check_spawnlimit(struct pmtab *pmptr)
538 {
539 	time_t	now;
540 
541 	(void) time(&now);
542 	if (pmptr->p_time == 0L)
543 		pmptr->p_time = now;
544 	if (pmptr->p_respawn >= SPAWN_LIMIT) {
545 		if ((now - pmptr->p_time) < SPAWN_INTERVAL) {
546 			pmptr->p_time = now;
547 			pmptr->p_respawn = 0;
548 			return (-1);
549 		}
550 		pmptr->p_time = now;
551 		pmptr->p_respawn = 0;
552 	}
553 	pmptr->p_respawn++;
554 	return (0);
555 }
556 
557 /*
558  * mod_ttydefs	- to check if /etc/ttydefs has been modified
559  *		- return TRUE if file modified
560  *		- otherwise, return FALSE
561  */
562 static	int
563 mod_ttydefs(void)
564 {
565 	struct	stat	statbuf;
566 
567 	if (stat(TTYDEFS, &statbuf) == -1) {
568 		/* if stat failed, don't bother reread ttydefs */
569 		return (FALSE);
570 	}
571 	if ((long)statbuf.st_mtime != Mtime) {
572 		Mtime = (long)statbuf.st_mtime;
573 		return (TRUE);
574 	}
575 	return (FALSE);
576 }
577 
578 /*
579  *	free_defs - free the Gdef table
580  */
581 static	void
582 free_defs(void)
583 {
584 	int	i;
585 	struct	Gdef	*tp;
586 	tp = &Gdef[0];
587 	for (i = 0; i < Ndefs; i++, tp++) {
588 		free(tp->g_id);
589 		free(tp->g_iflags);
590 		free(tp->g_fflags);
591 		free(tp->g_nextid);
592 		tp->g_id = NULL;
593 		tp->g_iflags = NULL;
594 		tp->g_fflags = NULL;
595 		tp->g_nextid = NULL;
596 	}
597 	Ndefs = 0;
598 }
599 
600 /*
601  * rebuild flags entry using speed from ttymode.
602  */
603 static char *
604 merge_flags(char *src, char *ttymode)
605 {
606 	char *data, *ptr, *flags;
607 
608 	/* copy speed entry */
609 	data = strsave(src);
610 	flags = strsave(ttymode);
611 	ptr = strchr(flags, ',');
612 	if (ptr == NULL) {	/* ttymode is corrupted */
613 		free(flags);
614 		return (data);
615 	}
616 	*ptr = '\0';
617 	ptr = flags;
618 	flags = strsave(flags);
619 	free(ptr);
620 
621 	/*
622 	 * The flags line is supposed to have stty keywords separated by space.
623 	 * We need to split up the keywords, replace the speed and
624 	 * reconstruct the flags line.
625 	 */
626 
627 	ptr = strtok(data, " \t");
628 	if (ptr == NULL) {
629 		free(data);
630 		return (flags);
631 	}
632 
633 	do {
634 		char *tmp;
635 
636 		/* skip speed */
637 		if (isdigit(*ptr))
638 			continue;
639 
640 		if (asprintf(&tmp, "%s %s", flags, ptr) <= 0) {
641 			/* should we complain? */
642 			break;
643 		}
644 		free(flags);
645 		flags = tmp;
646 	} while ((ptr = strtok(NULL, " \t")) != NULL);
647 
648 	free(data);
649 	return (flags);
650 }
651 
652 /*
653  * struct Gdef *get_speed(ttylabel)
654  *	- search "/etc/ttydefs" for speed and term. specification
655  *	  using "ttylabel". If "ttylabel" is NULL, default
656  *	  to DEFAULT
657  *	- for /dev/console, if we are in fact using serial console,
658  *	  use ttyX-mode value to get speed. This allows us to use
659  *	  the value set for serial console either from firmware (or BMC sol),
660  *	  or boot loader default.
661  * arg:	  ttylabel - label/id of speed settings.
662  */
663 
664 struct Gdef *
665 get_speed(struct pmtab *pmptr)
666 {
667 	static struct Gdef serial = { 0 };
668 	struct Gdef *sp;
669 	char *ttylabel = pmptr->p_ttylabel;
670 
671 	if ((ttylabel != NULL) && (*ttylabel != '\0')) {
672 		if ((sp = find_def(ttylabel)) == NULL) {
673 			log("unable to find <%s> in \"%s\"", ttylabel, TTYDEFS);
674 			sp = &DEFAULT; /* use default */
675 		}
676 	} else {
677 		sp = &DEFAULT; /* use default */
678 	}
679 
680 	/*
681 	 * if this is not /dev/console or /dev/console is not using serial,
682 	 * we are done.
683 	 */
684 	if (pmptr->p_ttymode == NULL ||
685 	    strcmp(pmptr->p_device, "/dev/console") != 0)
686 		return (sp);
687 
688 	/* is entry for serial set up? */
689 	if (serial.g_id == NULL) {
690 		/*
691 		 * Copy data from sp, except we need to update inital and
692 		 * final flags.
693 		 */
694 		serial.g_id = strsave(sp->g_id);
695 		serial.g_iflags = merge_flags(sp->g_iflags, pmptr->p_ttymode);
696 		serial.g_fflags = merge_flags(sp->g_fflags, pmptr->p_ttymode);
697 		serial.g_autobaud = sp->g_autobaud;
698 		serial.g_nextid = strsave(sp->g_nextid);
699 	}
700 	return (&serial);
701 }
702 
703 /*
704  * setup_PCpipe()	- setup the pipe between Parent and Children
705  *			- the pipe is used for a tmchild to send its
706  *			  pid to inform ttymon that it is about to
707  *			  invoke service
708  *			- the pipe also serves as a mean for tmchild
709  *			  to detect failure of ttymon
710  */
711 void
712 setup_PCpipe(void)
713 {
714 	int	flag = 0;
715 
716 	if (pipe(PCpipe) == -1)
717 		fatal("pipe() failed: %s", strerror(errno));
718 
719 	/* set close-on-exec flag */
720 	if (fcntl(PCpipe[0], F_SETFD, 1) == -1)
721 		fatal("F_SETFD fcntl failed: %s", strerror(errno));
722 
723 	if (fcntl(PCpipe[1], F_SETFD, 1) == -1)
724 		fatal("F_SETFD fcntl failed: %s", strerror(errno));
725 
726 	/* set O_NONBLOCK flag */
727 	if (fcntl(PCpipe[0], F_GETFL, flag) == -1)
728 		fatal("F_GETFL failed: %s", strerror(errno));
729 
730 	flag |= O_NONBLOCK;
731 	if (fcntl(PCpipe[0], F_SETFL, flag) == -1)
732 		fatal("F_SETFL failed: %s", strerror(errno));
733 
734 	/* set message discard mode */
735 	if (ioctl(PCpipe[0], I_SRDOPT, RMSGD) == -1)
736 		fatal("I_SRDOPT RMSGD failed: %s", strerror(errno));
737 
738 	/* register to receive SIGPOLL when data come */
739 	if (ioctl(PCpipe[0], I_SETSIG, S_INPUT) == -1)
740 		fatal("I_SETSIG S_INPUT failed: %s", strerror(errno));
741 
742 #ifdef	DEBUG
743 	log("PCpipe[0]\t = %d", PCpipe[0]);
744 	log("PCpipe[1]\t = %d", PCpipe[1]);
745 #endif
746 }
747