1/*
2 * clone.c - start a forked instance of the current shell on a new terminal
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 1997 Zolt�n Hidv�gi
7 * All rights reserved.
8 *
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
14 *
15 * In no event shall Zolt�n Hidv�gi or the Zsh Development Group be liable
16 * to any party for direct, indirect, special, incidental, or consequential
17 * damages arising out of the use of this software and its documentation,
18 * even if Zolt�n Hidv�gi and the Zsh Development Group have been advised of
19 * the possibility of such damage.
20 *
21 * Zolt�n Hidv�gi and the Zsh Development Group specifically disclaim any
22 * warranties, including, but not limited to, the implied warranties of
23 * merchantability and fitness for a particular purpose.  The software
24 * provided hereunder is on an "as is" basis, and Zolt�n Hidv�gi and the
25 * Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
27 *
28 */
29
30/*
31 * The clone builtin can be used to start a forked instance of the current
32 * shell on a new terminal.  The only argument to the builtin is the name
33 * of the new terminal.  In the new shell the PID, PPID and TTY parameters
34 * are changed appropriately.  $! is set to zero in the new instance of the
35 * shell and to the pid of the new instance in the original shell.
36 *
37 */
38
39#include "clone.mdh"
40#include "clone.pro"
41
42/**/
43static int
44bin_clone(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
45{
46    int ttyfd, pid, cttyfd;
47
48    unmetafy(*args, NULL);
49    ttyfd = open(*args, O_RDWR|O_NOCTTY);
50    if (ttyfd < 0) {
51	zwarnnam(nam, "%s: %e", *args, errno);
52	return 1;
53    }
54    pid = fork();
55    if (!pid) {
56	clearjobtab(0);
57	ppid = getppid();
58	mypid = getpid();
59#ifdef HAVE_SETSID
60	if (setsid() != mypid)
61	    zwarnnam(nam, "failed to create new session: %e", errno);
62#elif defined(TIOCNOTTY)
63	    if (ioctl(SHTTY, TIOCNOTTY, 0))
64	    zwarnnam(*args, "%e", errno);
65	    setpgrp(0L, mypid);
66#endif
67	dup2(ttyfd,0);
68	dup2(ttyfd,1);
69	dup2(ttyfd,2);
70	if (ttyfd > 2)
71	    close(ttyfd);
72	closem(0);
73	close(coprocin);
74	close(coprocout);
75	/* Acquire a controlling terminal */
76	cttyfd = open(*args, O_RDWR);
77	if (cttyfd == -1)
78	    zwarnnam(nam, "%e", errno);
79	else {
80#ifdef TIOCSCTTY
81	    ioctl(cttyfd, TIOCSCTTY, 0);
82#endif
83	    close(cttyfd);
84	}
85	/* check if we acquired the tty successfully */
86	cttyfd = open("/dev/tty", O_RDWR);
87	if (cttyfd == -1)
88	    zwarnnam(nam, "could not make %s my controlling tty, job control "
89		     "disabled", *args);
90	else
91	    close(cttyfd);
92
93	/* Clear mygrp so that acquire_pgrp() gets the new process group.
94	 * (acquire_pgrp() is called from init_io()) */
95	mypgrp = 0;
96	init_io();
97	setsparam("TTY", ztrdup(ttystrname));
98    }
99    close(ttyfd);
100    if (pid < 0) {
101	zerrnam(nam, "fork failed: %e", errno);
102	return 1;
103    }
104    lastpid = pid;
105    return 0;
106}
107
108static struct builtin bintab[] = {
109    BUILTIN("clone", 0, bin_clone, 1, 1, 0, NULL, NULL),
110};
111
112static struct features module_features = {
113    bintab, sizeof(bintab)/sizeof(*bintab),
114    NULL, 0,
115    NULL, 0,
116    NULL, 0,
117    0
118};
119
120/**/
121int
122setup_(UNUSED(Module m))
123{
124    return 0;
125}
126
127/**/
128int
129features_(Module m, char ***features)
130{
131    *features = featuresarray(m, &module_features);
132    return 0;
133}
134
135/**/
136int
137enables_(Module m, int **enables)
138{
139    return handlefeatures(m, &module_features, enables);
140}
141
142/**/
143int
144boot_(UNUSED(Module m))
145{
146    return 0;
147}
148
149/**/
150int
151cleanup_(Module m)
152{
153    return setfeatureenables(m, &module_features, NULL);
154}
155
156/**/
157int
158finish_(UNUSED(Module m))
159{
160    return 0;
161}
162