Imported Upstream version 1.15.1
[deb_xorg-server.git] / hw / xfree86 / os-support / bsd / bsd_init.c
CommitLineData
a09e091a
JB
1/*
2 * Copyright 1992 by Rich Murphey <Rich@Rice.edu>
3 * Copyright 1993 by David Wexelblat <dwex@goblin.org>
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation, and that the names of Rich Murphey and David Wexelblat
10 * not be used in advertising or publicity pertaining to distribution of
11 * the software without specific, written prior permission. Rich Murphey and
12 * David Wexelblat make no representations about the suitability of this
13 * software for any purpose. It is provided "as is" without express or
14 * implied warranty.
15 *
16 * RICH MURPHEY AND DAVID WEXELBLAT DISCLAIM ALL WARRANTIES WITH REGARD TO
17 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
18 * FITNESS, IN NO EVENT SHALL RICH MURPHEY OR DAVID WEXELBLAT BE LIABLE FOR
19 * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
20 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
21 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 *
24 */
25
26#ifdef HAVE_XORG_CONFIG_H
27#include <xorg-config.h>
28#endif
29
30#include <X11/X.h>
31
32#include "compiler.h"
33
34#include "xf86.h"
35#include "xf86Priv.h"
36#include "xf86_OSlib.h"
37
38#include <sys/utsname.h>
39#include <sys/ioctl.h>
40#include <stdlib.h>
41#include <errno.h>
42
43static Bool KeepTty = FALSE;
44
45#ifdef PCCONS_SUPPORT
46static int devConsoleFd = -1;
47#endif
48#if defined (SYSCONS_SUPPORT) || defined (PCVT_SUPPORT)
49static int VTnum = -1;
50static int initialVT = -1;
51#endif
52
53#ifdef PCCONS_SUPPORT
54/* Stock 0.1 386bsd pccons console driver interface */
55#define PCCONS_CONSOLE_DEV1 "/dev/ttyv0"
56#define PCCONS_CONSOLE_DEV2 "/dev/vga"
57#define PCCONS_CONSOLE_MODE O_RDWR|O_NDELAY
58#endif
59
60#ifdef SYSCONS_SUPPORT
61/* The FreeBSD 1.1 version syscons driver uses /dev/ttyv0 */
62#define SYSCONS_CONSOLE_DEV1 "/dev/ttyv0"
63#define SYSCONS_CONSOLE_DEV2 "/dev/vga"
64#define SYSCONS_CONSOLE_MODE O_RDWR|O_NDELAY
65#endif
66
67#ifdef PCVT_SUPPORT
68/* Hellmuth Michaelis' pcvt driver */
69#ifndef __OpenBSD__
70#define PCVT_CONSOLE_DEV "/dev/ttyv0"
71#else
72#define PCVT_CONSOLE_DEV "/dev/ttyC0"
73#endif
74#define PCVT_CONSOLE_MODE O_RDWR|O_NDELAY
75#endif
76
77#if defined(WSCONS_SUPPORT) && defined(__NetBSD__)
78/* NetBSD's new console driver */
79#define WSCONS_PCVT_COMPAT_CONSOLE_DEV "/dev/ttyE0"
80#endif
81
82#ifdef __GLIBC__
83#define setpgrp setpgid
84#endif
85
86#define CHECK_DRIVER_MSG \
87 "Check your kernel's console driver configuration and /dev entries"
88
89static char *supported_drivers[] = {
90#ifdef PCCONS_SUPPORT
91 "pccons (with X support)",
92#endif
93#ifdef SYSCONS_SUPPORT
94 "syscons",
95#endif
96#ifdef PCVT_SUPPORT
97 "pcvt",
98#endif
99#ifdef WSCONS_SUPPORT
100 "wscons",
101#endif
102};
103
104/*
105 * Functions to probe for the existance of a supported console driver.
106 * Any function returns either a valid file descriptor (driver probed
107 * succesfully), -1 (driver not found), or uses FatalError() if the
108 * driver was found but proved to not support the required mode to run
109 * an X server.
110 */
111
112typedef int (*xf86ConsOpen_t) (void);
113
114#ifdef PCCONS_SUPPORT
115static int xf86OpenPccons(void);
116#endif /* PCCONS_SUPPORT */
117
118#ifdef SYSCONS_SUPPORT
119static int xf86OpenSyscons(void);
120#endif /* SYSCONS_SUPPORT */
121
122#ifdef PCVT_SUPPORT
123static int xf86OpenPcvt(void);
124#endif /* PCVT_SUPPORT */
125
126#ifdef WSCONS_SUPPORT
127static int xf86OpenWScons(void);
128#endif
129
130/*
131 * The sequence of the driver probes is important; start with the
132 * driver that is best distinguishable, and end with the most generic
133 * driver. (Otherwise, pcvt would also probe as syscons, and either
134 * pcvt or syscons might succesfully probe as pccons.)
135 */
136static xf86ConsOpen_t xf86ConsTab[] = {
137#ifdef PCVT_SUPPORT
138 xf86OpenPcvt,
139#endif
140#ifdef SYSCONS_SUPPORT
141 xf86OpenSyscons,
142#endif
143#ifdef PCCONS_SUPPORT
144 xf86OpenPccons,
145#endif
146#ifdef WSCONS_SUPPORT
147 xf86OpenWScons,
148#endif
149 (xf86ConsOpen_t) NULL
150};
151
152void
153xf86OpenConsole()
154{
155 int i, fd = -1;
156 xf86ConsOpen_t *driver;
157
158#if defined (SYSCONS_SUPPORT) || defined (PCVT_SUPPORT)
159 int result;
160
161#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
162 struct utsname uts;
163#endif
164 vtmode_t vtmode;
165#endif
166
167 if (serverGeneration == 1) {
168 /* check if we are run with euid==0 */
169 if (geteuid() != 0) {
170 FatalError("xf86OpenConsole: Server must be suid root");
171 }
172
173 if (!KeepTty) {
174 /*
175 * detaching the controlling tty solves problems of kbd character
176 * loss. This is not interesting for CO driver, because it is
177 * exclusive.
178 */
179 setpgrp(0, getpid());
180 if ((i = open("/dev/tty", O_RDWR)) >= 0) {
181 ioctl(i, TIOCNOTTY, (char *) 0);
182 close(i);
183 }
184 }
185
186 /* detect which driver we are running on */
187 for (driver = xf86ConsTab; *driver; driver++) {
188 if ((fd = (*driver) ()) >= 0)
189 break;
190 }
191
192 /* Check that a supported console driver was found */
193 if (fd < 0) {
194 char cons_drivers[80] = { 0, };
195 for (i = 0; i < sizeof(supported_drivers) / sizeof(char *); i++) {
196 if (i) {
197 strcat(cons_drivers, ", ");
198 }
199 strcat(cons_drivers, supported_drivers[i]);
200 }
201 FatalError
202 ("%s: No console driver found\n\tSupported drivers: %s\n\t%s",
203 "xf86OpenConsole", cons_drivers, CHECK_DRIVER_MSG);
204 }
205 xf86Info.consoleFd = fd;
206
207 switch (xf86Info.consType) {
208#ifdef PCCONS_SUPPORT
209 case PCCONS:
210 if (ioctl(xf86Info.consoleFd, CONSOLE_X_MODE_ON, 0) < 0) {
211 FatalError("%s: CONSOLE_X_MODE_ON failed (%s)\n%s",
212 "xf86OpenConsole", strerror(errno),
213 CHECK_DRIVER_MSG);
214 }
215 /*
216 * Hack to prevent keyboard hanging when syslogd closes
217 * /dev/console
218 */
219 if ((devConsoleFd = open("/dev/console", O_WRONLY, 0)) < 0) {
220 xf86Msg(X_WARNING,
221 "xf86OpenConsole: couldn't open /dev/console (%s)\n",
222 strerror(errno));
223 }
224 break;
225#endif
226#if defined (SYSCONS_SUPPORT) || defined (PCVT_SUPPORT)
227 case SYSCONS:
228 /* as of FreeBSD 2.2.8, syscons driver does not need the #1 vt
229 * switching anymore. Here we check for FreeBSD 3.1 and up.
230 * Add cases for other *BSD that behave the same.
231 */
232#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
233 uname(&uts);
234 i = atof(uts.release) * 100;
235 if (i >= 310)
236 goto acquire_vt;
237#endif
238 /* otherwise fall through */
239 case PCVT:
240#if !(defined(__NetBSD__) && (__NetBSD_Version__ >= 200000000))
241 /*
242 * First activate the #1 VT. This is a hack to allow a server
243 * to be started while another one is active. There should be
244 * a better way.
245 */
246 if (initialVT != 1) {
247
248 if (ioctl(xf86Info.consoleFd, VT_ACTIVATE, 1) != 0) {
249 xf86Msg(X_WARNING, "xf86OpenConsole: VT_ACTIVATE failed\n");
250 }
251 sleep(1);
252 }
253#endif
254 acquire_vt:
255 if (!xf86Info.ShareVTs) {
256 /*
257 * now get the VT
258 */
259 SYSCALL(result =
260 ioctl(xf86Info.consoleFd, VT_ACTIVATE, xf86Info.vtno));
261 if (result != 0) {
262 xf86Msg(X_WARNING, "xf86OpenConsole: VT_ACTIVATE failed\n");
263 }
264 SYSCALL(result =
265 ioctl(xf86Info.consoleFd, VT_WAITACTIVE,
266 xf86Info.vtno));
267 if (result != 0) {
268 xf86Msg(X_WARNING,
269 "xf86OpenConsole: VT_WAITACTIVE failed\n");
270 }
271
272 signal(SIGUSR1, xf86VTRequest);
273
274 vtmode.mode = VT_PROCESS;
275 vtmode.relsig = SIGUSR1;
276 vtmode.acqsig = SIGUSR1;
277 vtmode.frsig = SIGUSR1;
278 if (ioctl(xf86Info.consoleFd, VT_SETMODE, &vtmode) < 0) {
279 FatalError("xf86OpenConsole: VT_SETMODE VT_PROCESS failed");
280 }
281#if !defined(__OpenBSD__) && !defined(USE_DEV_IO) && !defined(USE_I386_IOPL)
282 if (ioctl(xf86Info.consoleFd, KDENABIO, 0) < 0) {
283 FatalError("xf86OpenConsole: KDENABIO failed (%s)",
284 strerror(errno));
285 }
286#endif
287 if (ioctl(xf86Info.consoleFd, KDSETMODE, KD_GRAPHICS) < 0) {
288 FatalError("xf86OpenConsole: KDSETMODE KD_GRAPHICS failed");
289 }
290 }
291 else { /* xf86Info.ShareVTs */
292 close(xf86Info.consoleFd);
293 }
294 break;
295#endif /* SYSCONS_SUPPORT || PCVT_SUPPORT */
296#ifdef WSCONS_SUPPORT
297 case WSCONS:
298 /* Nothing to do */
299 break;
300#endif
301 }
302 }
303 else {
304 /* serverGeneration != 1 */
305#if defined (SYSCONS_SUPPORT) || defined (PCVT_SUPPORT)
306 if (!xf86Info.ShareVTs &&
307 (xf86Info.consType == SYSCONS || xf86Info.consType == PCVT)) {
308 if (ioctl(xf86Info.consoleFd, VT_ACTIVATE, xf86Info.vtno) != 0) {
309 xf86Msg(X_WARNING, "xf86OpenConsole: VT_ACTIVATE failed\n");
310 }
311 }
312#endif /* SYSCONS_SUPPORT || PCVT_SUPPORT */
313 }
314 return;
315}
316
317#ifdef PCCONS_SUPPORT
318
319static int
320xf86OpenPccons()
321{
322 int fd = -1;
323
324 if ((fd = open(PCCONS_CONSOLE_DEV1, PCCONS_CONSOLE_MODE, 0))
325 >= 0 || (fd = open(PCCONS_CONSOLE_DEV2, PCCONS_CONSOLE_MODE, 0))
326 >= 0) {
327 if (ioctl(fd, CONSOLE_X_MODE_OFF, 0) < 0) {
328 FatalError("%s: CONSOLE_X_MODE_OFF failed (%s)\n%s\n%s",
329 "xf86OpenPccons",
330 strerror(errno),
331 "Was expecting pccons driver with X support",
332 CHECK_DRIVER_MSG);
333 }
334 xf86Info.consType = PCCONS;
335 xf86Msg(X_PROBED, "Using pccons driver with X support\n");
336 }
337 return fd;
338}
339
340#endif /* PCCONS_SUPPORT */
341
342#ifdef SYSCONS_SUPPORT
343
344static int
345xf86OpenSyscons()
346{
347 int fd = -1;
348 vtmode_t vtmode;
349 char vtname[12];
350 long syscons_version;
351 MessageType from;
352
353 /* Check for syscons */
354 if ((fd = open(SYSCONS_CONSOLE_DEV1, SYSCONS_CONSOLE_MODE, 0)) >= 0
355 || (fd = open(SYSCONS_CONSOLE_DEV2, SYSCONS_CONSOLE_MODE, 0)) >= 0) {
356 if (ioctl(fd, VT_GETMODE, &vtmode) >= 0) {
357 /* Get syscons version */
358 if (ioctl(fd, CONS_GETVERS, &syscons_version) < 0) {
359 syscons_version = 0;
360 }
361
362 xf86Info.vtno = VTnum;
363 from = X_CMDLINE;
364
365#ifdef VT_GETACTIVE
366 if (ioctl(fd, VT_GETACTIVE, &initialVT) < 0)
367 initialVT = -1;
368#endif
369 if (xf86Info.ShareVTs)
370 xf86Info.vtno = initialVT;
371
372 if (xf86Info.vtno == -1) {
373 /*
374 * For old syscons versions (<0x100), VT_OPENQRY returns
375 * the current VT rather than the next free VT. In this
376 * case, the server gets started on the current VT instead
377 * of the next free VT.
378 */
379
380#if 0
381 /* check for the fixed VT_OPENQRY */
382 if (syscons_version >= 0x100) {
383#endif
384 if (ioctl(fd, VT_OPENQRY, &xf86Info.vtno) < 0) {
385 /* No free VTs */
386 xf86Info.vtno = -1;
387 }
388#if 0
389 }
390#endif
391
392 if (xf86Info.vtno == -1) {
393 /*
394 * All VTs are in use. If initialVT was found, use it.
395 */
396 if (initialVT != -1) {
397 xf86Info.vtno = initialVT;
398 }
399 else {
400 if (syscons_version >= 0x100) {
401 FatalError("%s: Cannot find a free VT",
402 "xf86OpenSyscons");
403 }
404 /* Should no longer reach here */
405 FatalError("%s: %s %s\n\t%s %s",
406 "xf86OpenSyscons",
407 "syscons versions prior to 1.0 require",
408 "either the",
409 "server's stdin be a VT",
410 "or the use of the vtxx server option");
411 }
412 }
413 from = X_PROBED;
414 }
415
416 close(fd);
417 snprintf(vtname, sizeof(vtname), "/dev/ttyv%01x",
418 xf86Info.vtno - 1);
419 if ((fd = open(vtname, SYSCONS_CONSOLE_MODE, 0)) < 0) {
420 FatalError("xf86OpenSyscons: Cannot open %s (%s)",
421 vtname, strerror(errno));
422 }
423 if (ioctl(fd, VT_GETMODE, &vtmode) < 0) {
424 FatalError("xf86OpenSyscons: VT_GETMODE failed");
425 }
426 xf86Info.consType = SYSCONS;
427 xf86Msg(X_PROBED, "Using syscons driver with X support");
428 if (syscons_version >= 0x100) {
429 xf86ErrorF(" (version %ld.%ld)\n", syscons_version >> 8,
430 syscons_version & 0xFF);
431 }
432 else {
433 xf86ErrorF(" (version 0.x)\n");
434 }
435 xf86Msg(from, "using VT number %d\n\n", xf86Info.vtno);
436 }
437 else {
438 /* VT_GETMODE failed, probably not syscons */
439 close(fd);
440 fd = -1;
441 }
442 }
443 return fd;
444}
445
446#endif /* SYSCONS_SUPPORT */
447
448#ifdef PCVT_SUPPORT
449
450static int
451xf86OpenPcvt()
452{
453 /* This looks much like syscons, since pcvt is API compatible */
454 int fd = -1;
455 vtmode_t vtmode;
456 char vtname[12], *vtprefix;
457 struct pcvtid pcvt_version;
458
459#ifndef __OpenBSD__
460 vtprefix = "/dev/ttyv";
461#else
462 vtprefix = "/dev/ttyC";
463#endif
464
465 fd = open(PCVT_CONSOLE_DEV, PCVT_CONSOLE_MODE, 0);
466#ifdef WSCONS_PCVT_COMPAT_CONSOLE_DEV
467 if (fd < 0) {
468 fd = open(WSCONS_PCVT_COMPAT_CONSOLE_DEV, PCVT_CONSOLE_MODE, 0);
469 vtprefix = "/dev/ttyE";
470 }
471#endif
472 if (fd >= 0) {
473 if (ioctl(fd, VGAPCVTID, &pcvt_version) >= 0) {
474 if (ioctl(fd, VT_GETMODE, &vtmode) < 0) {
475 FatalError("%s: VT_GETMODE failed\n%s%s\n%s",
476 "xf86OpenPcvt",
477 "Found pcvt driver but X11 seems to be",
478 " not supported.", CHECK_DRIVER_MSG);
479 }
480
481 xf86Info.vtno = VTnum;
482
483 if (ioctl(fd, VT_GETACTIVE, &initialVT) < 0)
484 initialVT = -1;
485
486 if (xf86Info.vtno == -1) {
487 if (ioctl(fd, VT_OPENQRY, &xf86Info.vtno) < 0) {
488 /* No free VTs */
489 xf86Info.vtno = -1;
490 }
491
492 if (xf86Info.vtno == -1) {
493 /*
494 * All VTs are in use. If initialVT was found, use it.
495 */
496 if (initialVT != -1) {
497 xf86Info.vtno = initialVT;
498 }
499 else {
500 FatalError("%s: Cannot find a free VT", "xf86OpenPcvt");
501 }
502 }
503 }
504
505 close(fd);
506 snprintf(vtname, sizeof(vtname), "%s%01x", vtprefix,
507 xf86Info.vtno - 1);
508 if ((fd = open(vtname, PCVT_CONSOLE_MODE, 0)) < 0) {
509 ErrorF("xf86OpenPcvt: Cannot open %s (%s)",
510 vtname, strerror(errno));
511 xf86Info.vtno = initialVT;
512 snprintf(vtname, sizeof(vtname), "%s%01x", vtprefix,
513 xf86Info.vtno - 1);
514 if ((fd = open(vtname, PCVT_CONSOLE_MODE, 0)) < 0) {
515 FatalError("xf86OpenPcvt: Cannot open %s (%s)",
516 vtname, strerror(errno));
517 }
518 }
519 if (ioctl(fd, VT_GETMODE, &vtmode) < 0) {
520 FatalError("xf86OpenPcvt: VT_GETMODE failed");
521 }
522 xf86Info.consType = PCVT;
523#ifdef WSCONS_SUPPORT
524 xf86Msg(X_PROBED,
525 "Using wscons driver on %s in pcvt compatibility mode "
526 "(version %d.%d)\n", vtname,
527 pcvt_version.rmajor, pcvt_version.rminor);
528#else
529 xf86Msg(X_PROBED, "Using pcvt driver (version %d.%d)\n",
530 pcvt_version.rmajor, pcvt_version.rminor);
531#endif
532 }
533 else {
534 /* Not pcvt */
535 close(fd);
536 fd = -1;
537 }
538 }
539 return fd;
540}
541
542#endif /* PCVT_SUPPORT */
543
544#ifdef WSCONS_SUPPORT
545
546static int
547xf86OpenWScons()
548{
549 int fd = -1;
550 int mode = WSDISPLAYIO_MODE_MAPPED;
551 int i;
552 char ttyname[16];
553
554 /* XXX Is this ok? */
555 for (i = 0; i < 8; i++) {
556#if defined(__NetBSD__)
557 snprintf(ttyname, sizeof(ttyname), "/dev/ttyE%d", i);
558#elif defined(__OpenBSD__)
559 snprintf(ttyname, sizeof(ttyname), "/dev/ttyC%x", i);
560#endif
561 if ((fd = open(ttyname, 2)) != -1)
562 break;
563 }
564 if (fd != -1) {
565 if (ioctl(fd, WSDISPLAYIO_SMODE, &mode) < 0) {
566 FatalError("%s: WSDISPLAYIO_MODE_MAPPED failed (%s)\n%s",
567 "xf86OpenConsole", strerror(errno), CHECK_DRIVER_MSG);
568 }
569 xf86Info.consType = WSCONS;
570 xf86Msg(X_PROBED, "Using wscons driver\n");
571 }
572 return fd;
573}
574
575#endif /* WSCONS_SUPPORT */
576
577void
578xf86CloseConsole()
579{
580#if defined(SYSCONS_SUPPORT) || defined(PCVT_SUPPORT)
581 struct vt_mode VT;
582#endif
583
584 if (xf86Info.ShareVTs)
585 return;
586
587 switch (xf86Info.consType) {
588#ifdef PCCONS_SUPPORT
589 case PCCONS:
590 ioctl(xf86Info.consoleFd, CONSOLE_X_MODE_OFF, 0);
591 break;
592#endif /* PCCONS_SUPPORT */
593#if defined (SYSCONS_SUPPORT) || defined (PCVT_SUPPORT)
594 case SYSCONS:
595 case PCVT:
596 ioctl(xf86Info.consoleFd, KDSETMODE, KD_TEXT); /* Back to text mode */
597 if (ioctl(xf86Info.consoleFd, VT_GETMODE, &VT) != -1) {
598 VT.mode = VT_AUTO;
599 ioctl(xf86Info.consoleFd, VT_SETMODE, &VT); /* dflt vt handling */
600 }
601#if !defined(__OpenBSD__) && !defined(USE_DEV_IO) && !defined(USE_I386_IOPL)
602 if (ioctl(xf86Info.consoleFd, KDDISABIO, 0) < 0) {
603 xf86FatalError("xf86CloseConsole: KDDISABIO failed (%s)",
604 strerror(errno));
605 }
606#endif
607 if (initialVT != -1)
608 ioctl(xf86Info.consoleFd, VT_ACTIVATE, initialVT);
609 break;
610#endif /* SYSCONS_SUPPORT || PCVT_SUPPORT */
611#ifdef WSCONS_SUPPORT
612 case WSCONS:
613 {
614 int mode = WSDISPLAYIO_MODE_EMUL;
615
616 ioctl(xf86Info.consoleFd, WSDISPLAYIO_SMODE, &mode);
617 break;
618 }
619#endif
620 }
621
622 close(xf86Info.consoleFd);
623#ifdef PCCONS_SUPPORT
624 if (devConsoleFd >= 0)
625 close(devConsoleFd);
626#endif
627 return;
628}
629
630int
631xf86ProcessArgument(int argc, char *argv[], int i)
632{
633 /*
634 * Keep server from detaching from controlling tty. This is useful
635 * when debugging (so the server can receive keyboard signals.
636 */
637 if (!strcmp(argv[i], "-keeptty")) {
638 KeepTty = TRUE;
639 return 1;
640 }
641#if defined (SYSCONS_SUPPORT) || defined (PCVT_SUPPORT)
642 if ((argv[i][0] == 'v') && (argv[i][1] == 't')) {
643 if (sscanf(argv[i], "vt%2d", &VTnum) == 0 || VTnum < 1 || VTnum > 12) {
644 UseMsg();
645 VTnum = -1;
646 return 0;
647 }
648 return 1;
649 }
650#endif /* SYSCONS_SUPPORT || PCVT_SUPPORT */
651 return 0;
652}
653
654void
655xf86UseMsg()
656{
657#if defined (SYSCONS_SUPPORT) || defined (PCVT_SUPPORT)
658 ErrorF("vtXX use the specified VT number (1-12)\n");
659#endif /* SYSCONS_SUPPORT || PCVT_SUPPORT */
660 ErrorF("-keeptty ");
661 ErrorF("don't detach controlling tty (for debugging only)\n");
662 return;
663}