162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <stddef.h>
762306a36Sopenharmony_ci#include <stdio.h>
862306a36Sopenharmony_ci#include <stdlib.h>
962306a36Sopenharmony_ci#include <unistd.h>
1062306a36Sopenharmony_ci#include <errno.h>
1162306a36Sopenharmony_ci#include <string.h>
1262306a36Sopenharmony_ci#include <termios.h>
1362306a36Sopenharmony_ci#include "chan_user.h"
1462306a36Sopenharmony_ci#include <os.h>
1562306a36Sopenharmony_ci#include <um_malloc.h>
1662306a36Sopenharmony_ci#include "xterm.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistruct xterm_chan {
1962306a36Sopenharmony_ci	int pid;
2062306a36Sopenharmony_ci	int helper_pid;
2162306a36Sopenharmony_ci	int chan_fd;
2262306a36Sopenharmony_ci	char *title;
2362306a36Sopenharmony_ci	int device;
2462306a36Sopenharmony_ci	int raw;
2562306a36Sopenharmony_ci	struct termios tt;
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic void *xterm_init(char *str, int device, const struct chan_opts *opts)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct xterm_chan *data;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	data = uml_kmalloc(sizeof(*data), UM_GFP_KERNEL);
3362306a36Sopenharmony_ci	if (data == NULL)
3462306a36Sopenharmony_ci		return NULL;
3562306a36Sopenharmony_ci	*data = ((struct xterm_chan) { .pid 		= -1,
3662306a36Sopenharmony_ci				       .helper_pid 	= -1,
3762306a36Sopenharmony_ci				       .chan_fd		= -1,
3862306a36Sopenharmony_ci				       .device 		= device,
3962306a36Sopenharmony_ci				       .title 		= opts->xterm_title,
4062306a36Sopenharmony_ci				       .raw  		= opts->raw } );
4162306a36Sopenharmony_ci	return data;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/* Only changed by xterm_setup, which is a setup */
4562306a36Sopenharmony_cistatic char *terminal_emulator = CONFIG_XTERM_CHAN_DEFAULT_EMULATOR;
4662306a36Sopenharmony_cistatic char *title_switch = "-T";
4762306a36Sopenharmony_cistatic char *exec_switch = "-e";
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic int __init xterm_setup(char *line, int *add)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	*add = 0;
5262306a36Sopenharmony_ci	terminal_emulator = line;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	line = strchr(line, ',');
5562306a36Sopenharmony_ci	if (line == NULL)
5662306a36Sopenharmony_ci		return 0;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	*line++ = '\0';
5962306a36Sopenharmony_ci	if (*line)
6062306a36Sopenharmony_ci		title_switch = line;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	line = strchr(line, ',');
6362306a36Sopenharmony_ci	if (line == NULL)
6462306a36Sopenharmony_ci		return 0;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	*line++ = '\0';
6762306a36Sopenharmony_ci	if (*line)
6862306a36Sopenharmony_ci		exec_switch = line;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	return 0;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci__uml_setup("xterm=", xterm_setup,
7462306a36Sopenharmony_ci"xterm=<terminal emulator>,<title switch>,<exec switch>\n"
7562306a36Sopenharmony_ci"    Specifies an alternate terminal emulator to use for the debugger,\n"
7662306a36Sopenharmony_ci"    consoles, and serial lines when they are attached to the xterm channel.\n"
7762306a36Sopenharmony_ci"    The values are the terminal emulator binary, the switch it uses to set\n"
7862306a36Sopenharmony_ci"    its title, and the switch it uses to execute a subprocess,\n"
7962306a36Sopenharmony_ci"    respectively.  The title switch must have the form '<switch> title',\n"
8062306a36Sopenharmony_ci"    not '<switch>=title'.  Similarly, the exec switch must have the form\n"
8162306a36Sopenharmony_ci"    '<switch> command arg1 arg2 ...'.\n"
8262306a36Sopenharmony_ci"    The default values are 'xterm=" CONFIG_XTERM_CHAN_DEFAULT_EMULATOR
8362306a36Sopenharmony_ci     ",-T,-e'.\n"
8462306a36Sopenharmony_ci"    Values for gnome-terminal are 'xterm=gnome-terminal,-t,-x'.\n\n"
8562306a36Sopenharmony_ci);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic int xterm_open(int input, int output, int primary, void *d,
8862306a36Sopenharmony_ci		      char **dev_out)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct xterm_chan *data = d;
9162306a36Sopenharmony_ci	int pid, fd, new, err;
9262306a36Sopenharmony_ci	char title[256], file[] = "/tmp/xterm-pipeXXXXXX";
9362306a36Sopenharmony_ci	char *argv[] = { terminal_emulator, title_switch, title, exec_switch,
9462306a36Sopenharmony_ci			 OS_LIB_PATH "/uml/port-helper", "-uml-socket",
9562306a36Sopenharmony_ci			 file, NULL };
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (access(argv[4], X_OK) < 0)
9862306a36Sopenharmony_ci		argv[4] = "port-helper";
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	/*
10162306a36Sopenharmony_ci	 * Check that DISPLAY is set, this doesn't guarantee the xterm
10262306a36Sopenharmony_ci	 * will work but w/o it we can be pretty sure it won't.
10362306a36Sopenharmony_ci	 */
10462306a36Sopenharmony_ci	if (getenv("DISPLAY") == NULL) {
10562306a36Sopenharmony_ci		printk(UM_KERN_ERR "xterm_open: $DISPLAY not set.\n");
10662306a36Sopenharmony_ci		return -ENODEV;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	/*
11062306a36Sopenharmony_ci	 * This business of getting a descriptor to a temp file,
11162306a36Sopenharmony_ci	 * deleting the file and closing the descriptor is just to get
11262306a36Sopenharmony_ci	 * a known-unused name for the Unix socket that we really
11362306a36Sopenharmony_ci	 * want.
11462306a36Sopenharmony_ci	 */
11562306a36Sopenharmony_ci	fd = mkstemp(file);
11662306a36Sopenharmony_ci	if (fd < 0) {
11762306a36Sopenharmony_ci		err = -errno;
11862306a36Sopenharmony_ci		printk(UM_KERN_ERR "xterm_open : mkstemp failed, errno = %d\n",
11962306a36Sopenharmony_ci		       errno);
12062306a36Sopenharmony_ci		return err;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (unlink(file)) {
12462306a36Sopenharmony_ci		err = -errno;
12562306a36Sopenharmony_ci		printk(UM_KERN_ERR "xterm_open : unlink failed, errno = %d\n",
12662306a36Sopenharmony_ci		       errno);
12762306a36Sopenharmony_ci		close(fd);
12862306a36Sopenharmony_ci		return err;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci	close(fd);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	fd = os_create_unix_socket(file, sizeof(file), 1);
13362306a36Sopenharmony_ci	if (fd < 0) {
13462306a36Sopenharmony_ci		printk(UM_KERN_ERR "xterm_open : create_unix_socket failed, "
13562306a36Sopenharmony_ci		       "errno = %d\n", -fd);
13662306a36Sopenharmony_ci		return fd;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	sprintf(title, data->title, data->device);
14062306a36Sopenharmony_ci	pid = run_helper(NULL, NULL, argv);
14162306a36Sopenharmony_ci	if (pid < 0) {
14262306a36Sopenharmony_ci		err = pid;
14362306a36Sopenharmony_ci		printk(UM_KERN_ERR "xterm_open : run_helper failed, "
14462306a36Sopenharmony_ci		       "errno = %d\n", -err);
14562306a36Sopenharmony_ci		goto out_close1;
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	err = os_set_fd_block(fd, 0);
14962306a36Sopenharmony_ci	if (err < 0) {
15062306a36Sopenharmony_ci		printk(UM_KERN_ERR "xterm_open : failed to set descriptor "
15162306a36Sopenharmony_ci		       "non-blocking, err = %d\n", -err);
15262306a36Sopenharmony_ci		goto out_kill;
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	data->chan_fd = fd;
15662306a36Sopenharmony_ci	new = xterm_fd(fd, &data->helper_pid);
15762306a36Sopenharmony_ci	if (new < 0) {
15862306a36Sopenharmony_ci		err = new;
15962306a36Sopenharmony_ci		printk(UM_KERN_ERR "xterm_open : os_rcv_fd failed, err = %d\n",
16062306a36Sopenharmony_ci		       -err);
16162306a36Sopenharmony_ci		goto out_kill;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	err = os_set_fd_block(new, 0);
16562306a36Sopenharmony_ci	if (err) {
16662306a36Sopenharmony_ci		printk(UM_KERN_ERR "xterm_open : failed to set xterm "
16762306a36Sopenharmony_ci		       "descriptor non-blocking, err = %d\n", -err);
16862306a36Sopenharmony_ci		goto out_close2;
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	CATCH_EINTR(err = tcgetattr(new, &data->tt));
17262306a36Sopenharmony_ci	if (err) {
17362306a36Sopenharmony_ci		new = err;
17462306a36Sopenharmony_ci		goto out_close2;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	if (data->raw) {
17862306a36Sopenharmony_ci		err = raw(new);
17962306a36Sopenharmony_ci		if (err) {
18062306a36Sopenharmony_ci			new = err;
18162306a36Sopenharmony_ci			goto out_close2;
18262306a36Sopenharmony_ci		}
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	unlink(file);
18662306a36Sopenharmony_ci	data->pid = pid;
18762306a36Sopenharmony_ci	*dev_out = NULL;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	return new;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci out_close2:
19262306a36Sopenharmony_ci	close(new);
19362306a36Sopenharmony_ci out_kill:
19462306a36Sopenharmony_ci	os_kill_process(pid, 1);
19562306a36Sopenharmony_ci out_close1:
19662306a36Sopenharmony_ci	close(fd);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return err;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic void xterm_close(int fd, void *d)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	struct xterm_chan *data = d;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (data->pid != -1)
20662306a36Sopenharmony_ci		os_kill_process(data->pid, 1);
20762306a36Sopenharmony_ci	data->pid = -1;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (data->helper_pid != -1)
21062306a36Sopenharmony_ci		os_kill_process(data->helper_pid, 0);
21162306a36Sopenharmony_ci	data->helper_pid = -1;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (data->chan_fd != -1)
21462306a36Sopenharmony_ci		os_close_file(data->chan_fd);
21562306a36Sopenharmony_ci	os_close_file(fd);
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ciconst struct chan_ops xterm_ops = {
21962306a36Sopenharmony_ci	.type		= "xterm",
22062306a36Sopenharmony_ci	.init		= xterm_init,
22162306a36Sopenharmony_ci	.open		= xterm_open,
22262306a36Sopenharmony_ci	.close		= xterm_close,
22362306a36Sopenharmony_ci	.read		= generic_read,
22462306a36Sopenharmony_ci	.write		= generic_write,
22562306a36Sopenharmony_ci	.console_write	= generic_console_write,
22662306a36Sopenharmony_ci	.window_size	= generic_window_size,
22762306a36Sopenharmony_ci	.free		= generic_free,
22862306a36Sopenharmony_ci	.winch		= 1,
22962306a36Sopenharmony_ci};
230