1/*
2 * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
3 * Licensed under the GPL
4 */
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <unistd.h>
9#include <string.h>
10#include <errno.h>
11#include <termios.h>
12#include <signal.h>
13#include <sched.h>
14#include <sys/socket.h>
15#include "kern_util.h"
16#include "chan_user.h"
17#include "user.h"
18#include "os.h"
19#include "xterm.h"
20
21struct xterm_chan {
22	int pid;
23	int helper_pid;
24	char *title;
25	int device;
26	int raw;
27	struct termios tt;
28	unsigned long stack;
29	int direct_rcv;
30};
31
32/* Not static because it's called directly by the tt mode gdb code */
33void *xterm_init(char *str, int device, const struct chan_opts *opts)
34{
35	struct xterm_chan *data;
36
37	data = malloc(sizeof(*data));
38	if(data == NULL) return(NULL);
39	*data = ((struct xterm_chan) { .pid 		= -1,
40				       .helper_pid 	= -1,
41				       .device 		= device,
42				       .title 		= opts->xterm_title,
43				       .raw  		= opts->raw,
44				       .stack 		= opts->tramp_stack,
45				       .direct_rcv 	= !opts->in_kernel } );
46	return(data);
47}
48
49/* Only changed by xterm_setup, which is a setup */
50static char *terminal_emulator = "xterm";
51static char *title_switch = "-T";
52static char *exec_switch = "-e";
53
54static int __init xterm_setup(char *line, int *add)
55{
56	*add = 0;
57	terminal_emulator = line;
58
59	line = strchr(line, ',');
60	if(line == NULL) return(0);
61	*line++ = '\0';
62	if(*line) title_switch = line;
63
64	line = strchr(line, ',');
65	if(line == NULL) return(0);
66	*line++ = '\0';
67	if(*line) exec_switch = line;
68
69	return(0);
70}
71
72__uml_setup("xterm=", xterm_setup,
73"xterm=<terminal emulator>,<title switch>,<exec switch>\n"
74"    Specifies an alternate terminal emulator to use for the debugger,\n"
75"    consoles, and serial lines when they are attached to the xterm channel.\n"
76"    The values are the terminal emulator binary, the switch it uses to set\n"
77"    its title, and the switch it uses to execute a subprocess,\n"
78"    respectively.  The title switch must have the form '<switch> title',\n"
79"    not '<switch>=title'.  Similarly, the exec switch must have the form\n"
80"    '<switch> command arg1 arg2 ...'.\n"
81"    The default values are 'xterm=xterm,-T,-e'.  Values for gnome-terminal\n"
82"    are 'xterm=gnome-terminal,-t,-x'.\n\n"
83);
84
85int xterm_open(int input, int output, int primary, void *d,
86		      char **dev_out)
87{
88	struct xterm_chan *data = d;
89	unsigned long stack;
90	int pid, fd, new, err;
91	char title[256], file[] = "/tmp/xterm-pipeXXXXXX";
92	char *argv[] = { terminal_emulator, title_switch, title, exec_switch,
93			 "/usr/lib/uml/port-helper", "-uml-socket",
94			 file, NULL };
95
96	if(os_access(argv[4], OS_ACC_X_OK) < 0)
97		argv[4] = "port-helper";
98
99	/* Check that DISPLAY is set, this doesn't guarantee the xterm
100	 * will work but w/o it we can be pretty sure it won't. */
101	if (!getenv("DISPLAY")) {
102		printk("xterm_open: $DISPLAY not set.\n");
103		return -ENODEV;
104	}
105
106	fd = mkstemp(file);
107	if(fd < 0){
108		err = -errno;
109		printk("xterm_open : mkstemp failed, errno = %d\n", errno);
110		return err;
111	}
112
113	if(unlink(file)){
114		err = -errno;
115		printk("xterm_open : unlink failed, errno = %d\n", errno);
116		return err;
117	}
118	os_close_file(fd);
119
120	fd = os_create_unix_socket(file, sizeof(file), 1);
121	if(fd < 0){
122		printk("xterm_open : create_unix_socket failed, errno = %d\n",
123		       -fd);
124		return(fd);
125	}
126
127	sprintf(title, data->title, data->device);
128	stack = data->stack;
129	pid = run_helper(NULL, NULL, argv, &stack);
130	if(pid < 0){
131		printk("xterm_open : run_helper failed, errno = %d\n", -pid);
132		return(pid);
133	}
134
135	if (data->direct_rcv) {
136		new = os_rcv_fd(fd, &data->helper_pid);
137	} else {
138		err = os_set_fd_block(fd, 0);
139		if(err < 0){
140			printk("xterm_open : failed to set descriptor "
141			       "non-blocking, err = %d\n", -err);
142			return(err);
143		}
144		new = xterm_fd(fd, &data->helper_pid);
145	}
146	if(new < 0){
147		printk("xterm_open : os_rcv_fd failed, err = %d\n", -new);
148		goto out;
149	}
150
151	CATCH_EINTR(err = tcgetattr(new, &data->tt));
152	if(err){
153		new = err;
154		goto out;
155	}
156
157	if(data->raw){
158		err = raw(new);
159		if(err){
160			new = err;
161			goto out;
162		}
163	}
164
165	data->pid = pid;
166	*dev_out = NULL;
167 out:
168	unlink(file);
169	return(new);
170}
171
172/* Not static because it's called directly by the tt mode gdb code */
173void xterm_close(int fd, void *d)
174{
175	struct xterm_chan *data = d;
176
177	if(data->pid != -1)
178		os_kill_process(data->pid, 1);
179	data->pid = -1;
180	if(data->helper_pid != -1)
181		os_kill_process(data->helper_pid, 0);
182	data->helper_pid = -1;
183	os_close_file(fd);
184}
185
186static void xterm_free(void *d)
187{
188	free(d);
189}
190
191const struct chan_ops xterm_ops = {
192	.type		= "xterm",
193	.init		= xterm_init,
194	.open		= xterm_open,
195	.close		= xterm_close,
196	.read		= generic_read,
197	.write		= generic_write,
198	.console_write	= generic_console_write,
199	.window_size	= generic_window_size,
200	.free		= xterm_free,
201	.winch		= 1,
202};
203
204/*
205 * Overrides for Emacs so that we follow Linus's tabbing style.
206 * Emacs will notice this stuff at the end of the file and automatically
207 * adjust the settings for this buffer only.  This must remain at the end
208 * of the file.
209 * ---------------------------------------------------------------------------
210 * Local variables:
211 * c-file-style: "linux"
212 * End:
213 */
214