mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 01:18:00 +00:00
98200d1bfe
DIT, for 'Data-Independent Timing', is a bit you can set in the processor state on sufficiently new Arm CPUs, which promises that a long list of instructions will deliberately avoid varying their timing based on the input register values. Just what you want for keeping your constant-time crypto primitives constant-time. As far as I'm aware, no CPU has _yet_ implemented any data-dependent optimisations, so DIT is a safety precaution against them doing so in future. It would be embarrassing to be caught without it if a future CPU does do that, so we now turn on DIT in the PuTTY process state. I've put a call to the new enable_dit() function at the start of every main() and WinMain() belonging to a program that might do cryptography (even testcrypt, in case someone uses it for something!), and in case I missed one there, also added a second call at the first moment that any cryptography-using part of the code looks as if it might become active: when an instance of the SSH protocol object is configured, when the system PRNG is initialised, and when selecting any cryptographic authentication protocol in an HTTP or SOCKS proxy connection. With any luck those precautions between them should ensure it's on whenever we need it. Arm's own recommendation is that you should carefully choose the granularity at which you enable and disable DIT: there's a potential time cost to turning it on and off (I'm not sure what, but plausibly something of the order of a pipeline flush), so it's a performance hit to do it _inside_ each individual crypto function, but if CPUs start supporting significant data-dependent optimisation in future, then it will also become a noticeable performance hit to just leave it on across the whole process. So you'd like to do it somewhere in the middle: for example, you might turn on DIT once around the whole process of verifying and decrypting an SSH packet, instead of once for decryption and once for MAC. With all respect to that recommendation as a strategy for maximum performance, I'm not following it here. I turn on DIT at the start of the PuTTY process, and then leave it on. Rationale: 1. PuTTY is not otherwise a performance-critical application: it's not likely to max out your CPU for any purpose _other_ than cryptography. The most CPU-intensive non-cryptographic thing I can imagine a PuTTY process doing is the complicated computation of font rendering in the terminal, and that will normally be cached (you don't recompute each glyph from its outline and hints for every time you display it). 2. I think a bigger risk lies in accidental side channels from having DIT turned off when it should have been on. I can imagine lots of causes for that. Missing a crypto operation in some unswept corner of the code; confusing control flow (like my coroutine macros) jumping with DIT clear into the middle of a region of code that expected DIT to have been set at the beginning; having a reference counter of DIT requests and getting it out of sync. In a more sophisticated programming language, it might be possible to avoid the risk in #2 by cleverness with the type system. For example, in Rust, you could have a zero-sized type that acts as a proof token for DIT being enabled (it would be constructed by a function that also sets DIT, have a Drop implementation that clears DIT, and be !Send so you couldn't use it in a thread other than the one where DIT was set), and then you could require all the actual crypto functions to take a DitToken as an extra parameter, at zero runtime cost. Then "oops I forgot to set DIT around this piece of crypto" would become a compile error. Even so, you'd have to take some care with coroutine-structured code (what happens if a Rust async function yields while holding a DIT token?) and with nesting (if you have two DIT tokens, you don't want dropping the inner one to clear DIT while the outer one is still there to wrongly convince callees that it's set). Maybe in Rust you could get this all to work reliably. But not in C! DIT is an optional feature of the Arm architecture, so we must first test to see if it's supported. This is done the same way as we already do for the various Arm crypto accelerators: on ELF-based systems, check the appropriate bit in the 'hwcap' words in the ELF aux vector; on Mac, look for an appropriate sysctl flag. On Windows I don't know of a way to query the DIT feature, _or_ of a way to write the necessary enabling instruction in an MSVC-compatible way. I've _heard_ that it might not be necessary, because Windows might just turn on DIT unconditionally and leave it on, in an even more extreme version of my own strategy. I don't have a source for that - I heard it by word of mouth - but I _hope_ it's true, because that would suit me very well! Certainly I can't write code to enable DIT without knowing (a) how to do it, (b) how to know if it's safe. Nonetheless, I've put the enable_dit() call in all the right places in the Windows main programs as well as the Unix and cross-platform code, so that if I later find out that I _can_ put in an explicit enable of DIT in some way, I'll only have to arrange to set HAVE_ARM_DIT and compile the enable_dit() function appropriately.
681 lines
20 KiB
C
681 lines
20 KiB
C
/*
|
|
* main-gtk-simple.c: the common main-program code between the
|
|
* straight-up Unix PuTTY and pterm, which they do not share with the
|
|
* multi-session main-gtk-application.c.
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
#include <locale.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <gtk/gtk.h>
|
|
#if !GTK_CHECK_VERSION(3,0,0)
|
|
#include <gdk/gdkkeysyms.h>
|
|
#endif
|
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
|
#include <gtk/gtkimmodule.h>
|
|
#endif
|
|
|
|
#define MAY_REFER_TO_GTK_IN_HEADERS
|
|
|
|
#include "putty.h"
|
|
#include "ssh.h"
|
|
#include "terminal.h"
|
|
#include "gtkcompat.h"
|
|
#include "unifont.h"
|
|
#include "gtkmisc.h"
|
|
|
|
#ifndef NOT_X_WINDOWS
|
|
#include <gdk/gdkx.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
#include <X11/Xatom.h>
|
|
#include "x11misc.h"
|
|
#endif
|
|
|
|
static char *progname, **gtkargvstart;
|
|
static int ngtkargs;
|
|
|
|
static const char *app_name = "pterm";
|
|
|
|
char *x_get_default(const char *key)
|
|
{
|
|
#ifndef NOT_X_WINDOWS
|
|
Display *disp;
|
|
if ((disp = get_x11_display()) == NULL)
|
|
return NULL;
|
|
return XGetDefault(disp, app_name, key);
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
void fork_and_exec_self(int fd_to_close, ...)
|
|
{
|
|
/*
|
|
* Re-execing ourself is not an exact science under Unix. I do
|
|
* the best I can by using /proc/self/exe if available and by
|
|
* assuming argv[0] can be found on $PATH if not.
|
|
*
|
|
* Note that we also have to reconstruct the elements of the
|
|
* original argv which gtk swallowed, since the user wants the
|
|
* new session to appear on the same X display as the old one.
|
|
*/
|
|
char **args;
|
|
va_list ap;
|
|
int i, n;
|
|
int pid;
|
|
|
|
/*
|
|
* Collect the arguments with which to re-exec ourself.
|
|
*/
|
|
va_start(ap, fd_to_close);
|
|
n = 2; /* progname and terminating NULL */
|
|
n += ngtkargs;
|
|
while (va_arg(ap, char *) != NULL)
|
|
n++;
|
|
va_end(ap);
|
|
|
|
args = snewn(n, char *);
|
|
args[0] = progname;
|
|
args[n-1] = NULL;
|
|
for (i = 0; i < ngtkargs; i++)
|
|
args[i+1] = gtkargvstart[i];
|
|
|
|
i++;
|
|
va_start(ap, fd_to_close);
|
|
while ((args[i++] = va_arg(ap, char *)) != NULL);
|
|
va_end(ap);
|
|
|
|
assert(i == n);
|
|
|
|
/*
|
|
* Do the double fork.
|
|
*/
|
|
pid = fork();
|
|
if (pid < 0) {
|
|
perror("fork");
|
|
sfree(args);
|
|
return;
|
|
}
|
|
|
|
if (pid == 0) {
|
|
int pid2 = fork();
|
|
if (pid2 < 0) {
|
|
perror("fork");
|
|
_exit(1);
|
|
} else if (pid2 > 0) {
|
|
/*
|
|
* First child has successfully forked second child. My
|
|
* Work Here Is Done. Note the use of _exit rather than
|
|
* exit: the latter appears to cause destroy messages
|
|
* to be sent to the X server. I suspect gtk uses
|
|
* atexit.
|
|
*/
|
|
_exit(0);
|
|
}
|
|
|
|
/*
|
|
* If we reach here, we are the second child, so we now
|
|
* actually perform the exec.
|
|
*/
|
|
if (fd_to_close >= 0)
|
|
close(fd_to_close);
|
|
|
|
execv("/proc/self/exe", args);
|
|
execvp(progname, args);
|
|
perror("exec");
|
|
_exit(127);
|
|
|
|
} else {
|
|
int status;
|
|
sfree(args);
|
|
waitpid(pid, &status, 0);
|
|
}
|
|
|
|
}
|
|
|
|
void launch_duplicate_session(Conf *conf)
|
|
{
|
|
/*
|
|
* For this feature we must marshal conf and (possibly) pty_argv
|
|
* into a byte stream, create a pipe, and send this byte stream
|
|
* to the child through the pipe.
|
|
*/
|
|
int i, ret;
|
|
strbuf *serialised;
|
|
char option[80];
|
|
int pipefd[2];
|
|
|
|
if (pipe(pipefd) < 0) {
|
|
perror("pipe");
|
|
return;
|
|
}
|
|
|
|
serialised = strbuf_new();
|
|
|
|
conf_serialise(BinarySink_UPCAST(serialised), conf);
|
|
if (use_pty_argv && pty_argv)
|
|
for (i = 0; pty_argv[i]; i++)
|
|
put_asciz(serialised, pty_argv[i]);
|
|
|
|
sprintf(option, "---[%d,%zu]", pipefd[0], serialised->len);
|
|
noncloexec(pipefd[0]);
|
|
fork_and_exec_self(pipefd[1], option, NULL);
|
|
close(pipefd[0]);
|
|
|
|
i = ret = 0;
|
|
while (i < serialised->len &&
|
|
(ret = write(pipefd[1], serialised->s + i,
|
|
serialised->len - i)) > 0)
|
|
i += ret;
|
|
if (ret < 0)
|
|
perror("write to pipe");
|
|
close(pipefd[1]);
|
|
strbuf_free(serialised);
|
|
}
|
|
|
|
void launch_new_session(void)
|
|
{
|
|
fork_and_exec_self(-1, NULL);
|
|
}
|
|
|
|
void launch_saved_session(const char *str)
|
|
{
|
|
fork_and_exec_self(-1, "-load", str, NULL);
|
|
}
|
|
|
|
int read_dupsession_data(Conf *conf, char *arg)
|
|
{
|
|
int fd, i, ret, size;
|
|
char *data;
|
|
BinarySource src[1];
|
|
|
|
if (sscanf(arg, "---[%d,%d]", &fd, &size) != 2) {
|
|
fprintf(stderr, "%s: malformed magic argument `%s'\n", appname, arg);
|
|
exit(1);
|
|
}
|
|
|
|
data = snewn(size, char);
|
|
i = ret = 0;
|
|
while (i < size && (ret = read(fd, data + i, size - i)) > 0)
|
|
i += ret;
|
|
if (ret < 0) {
|
|
perror("read from pipe");
|
|
exit(1);
|
|
} else if (i < size) {
|
|
fprintf(stderr, "%s: unexpected EOF in Duplicate Session data\n",
|
|
appname);
|
|
exit(1);
|
|
}
|
|
|
|
BinarySource_BARE_INIT(src, data, size);
|
|
if (!conf_deserialise(conf, src)) {
|
|
fprintf(stderr, "%s: malformed Duplicate Session data\n", appname);
|
|
exit(1);
|
|
}
|
|
if (use_pty_argv) {
|
|
int pty_argc = 0;
|
|
size_t argv_startpos = src->pos;
|
|
|
|
while (get_asciz(src), !get_err(src))
|
|
pty_argc++;
|
|
|
|
src->err = BSE_NO_ERROR;
|
|
|
|
if (pty_argc > 0) {
|
|
src->pos = argv_startpos;
|
|
|
|
pty_argv = snewn(pty_argc + 1, char *);
|
|
pty_argv[pty_argc] = NULL;
|
|
for (i = 0; i < pty_argc; i++)
|
|
pty_argv[i] = dupstr(get_asciz(src));
|
|
}
|
|
}
|
|
|
|
if (get_err(src) || get_avail(src) > 0) {
|
|
fprintf(stderr, "%s: malformed Duplicate Session data\n", appname);
|
|
exit(1);
|
|
}
|
|
|
|
sfree(data);
|
|
return 0;
|
|
}
|
|
|
|
static void help(FILE *fp) {
|
|
if (fprintf(fp,
|
|
"pterm option summary:\n"
|
|
"\n"
|
|
" --display DISPLAY Specify X display to use (note '--')\n"
|
|
" -name PREFIX Prefix when looking up resources (default: pterm)\n"
|
|
" -fn FONT Normal text font\n"
|
|
" -fb FONT Bold text font\n"
|
|
" -geometry GEOMETRY Position and size of window (size in characters)\n"
|
|
" -sl LINES Number of lines of scrollback\n"
|
|
" -fg COLOUR, -bg COLOUR Foreground/background colour\n"
|
|
" -bfg COLOUR, -bbg COLOUR Foreground/background bold colour\n"
|
|
" -cfg COLOUR, -bfg COLOUR Foreground/background cursor colour\n"
|
|
" -T TITLE Window title\n"
|
|
" -ut, +ut Do(default) or do not update utmp\n"
|
|
" -ls, +ls Do(default) or do not make shell a login shell\n"
|
|
" -sb, +sb Do(default) or do not display a scrollbar\n"
|
|
" -log PATH, -sessionlog PATH Log all output to a file\n"
|
|
" -nethack Map numeric keypad to hjklyubn direction keys\n"
|
|
" -xrm RESOURCE-STRING Set an X resource\n"
|
|
" -e COMMAND [ARGS...] Execute command (consumes all remaining args)\n"
|
|
) < 0 || fflush(fp) < 0) {
|
|
perror("output error");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static void version(FILE *fp) {
|
|
char *buildinfo_text = buildinfo("\n");
|
|
if (fprintf(fp, "%s: %s\n%s\n", appname, ver, buildinfo_text) < 0 ||
|
|
fflush(fp) < 0) {
|
|
perror("output error");
|
|
exit(1);
|
|
}
|
|
sfree(buildinfo_text);
|
|
}
|
|
|
|
static const char *geometry_string;
|
|
|
|
void cmdline_error(const char *p, ...)
|
|
{
|
|
va_list ap;
|
|
fprintf(stderr, "%s: ", appname);
|
|
va_start(ap, p);
|
|
vfprintf(stderr, p, ap);
|
|
va_end(ap);
|
|
fputc('\n', stderr);
|
|
exit(1);
|
|
}
|
|
|
|
void window_setup_error(const char *errmsg)
|
|
{
|
|
fprintf(stderr, "%s: %s\n", appname, errmsg);
|
|
exit(1);
|
|
}
|
|
|
|
bool do_cmdline(int argc, char **argv, bool do_everything, Conf *conf)
|
|
{
|
|
bool err = false;
|
|
|
|
/*
|
|
* Macros to make argument handling easier.
|
|
*
|
|
* Note that because they need to call `continue', they cannot be
|
|
* contained in the usual do {...} while (0) wrapper to make them
|
|
* syntactically single statements. I use the alternative if (1)
|
|
* {...} else ((void)0).
|
|
*/
|
|
#define EXPECTS_ARG if (1) { \
|
|
if (!nextarg) { \
|
|
err = true; \
|
|
fprintf(stderr, "%s: %s expects an argument\n", appname, p); \
|
|
continue; \
|
|
} else { \
|
|
arglistpos++; \
|
|
} \
|
|
} else ((void)0)
|
|
#define SECOND_PASS_ONLY if (1) { \
|
|
if (!do_everything) \
|
|
continue; \
|
|
} else ((void)0)
|
|
|
|
CmdlineArgList *arglist = cmdline_arg_list_from_argv(argc, argv);
|
|
size_t arglistpos = 0;
|
|
while (arglist->args[arglistpos]) {
|
|
CmdlineArg *arg = arglist->args[arglistpos++];
|
|
CmdlineArg *nextarg = arglist->args[arglistpos];
|
|
const char *p = cmdline_arg_to_str(arg);
|
|
const char *val = cmdline_arg_to_str(nextarg);
|
|
int ret;
|
|
|
|
/*
|
|
* Shameless cheating. Debian requires all X terminal
|
|
* emulators to support `-T title'; but
|
|
* cmdline_process_param will eat -T (it means no-pty) and
|
|
* complain that pterm doesn't support it. So, in pterm
|
|
* only, we convert -T into -title.
|
|
*/
|
|
if ((cmdline_tooltype & TOOLTYPE_NONNETWORK) &&
|
|
!strcmp(p, "-T"))
|
|
p = "-title";
|
|
|
|
ret = cmdline_process_param(
|
|
arg, nextarg, do_everything ? 1 : -1, conf);
|
|
|
|
if (ret == -2) {
|
|
cmdline_error("option \"%s\" requires an argument", p);
|
|
} else if (ret == 2) {
|
|
arglistpos++;
|
|
continue;
|
|
} else if (ret == 1) {
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(p, "-fn") || !strcmp(p, "-font")) {
|
|
FontSpec *fs;
|
|
EXPECTS_ARG;
|
|
SECOND_PASS_ONLY;
|
|
fs = fontspec_new(val);
|
|
conf_set_fontspec(conf, CONF_font, fs);
|
|
fontspec_free(fs);
|
|
|
|
} else if (!strcmp(p, "-fb")) {
|
|
FontSpec *fs;
|
|
EXPECTS_ARG;
|
|
SECOND_PASS_ONLY;
|
|
fs = fontspec_new(val);
|
|
conf_set_fontspec(conf, CONF_boldfont, fs);
|
|
fontspec_free(fs);
|
|
|
|
} else if (!strcmp(p, "-fw")) {
|
|
FontSpec *fs;
|
|
EXPECTS_ARG;
|
|
SECOND_PASS_ONLY;
|
|
fs = fontspec_new(val);
|
|
conf_set_fontspec(conf, CONF_widefont, fs);
|
|
fontspec_free(fs);
|
|
|
|
} else if (!strcmp(p, "-fwb")) {
|
|
FontSpec *fs;
|
|
EXPECTS_ARG;
|
|
SECOND_PASS_ONLY;
|
|
fs = fontspec_new(val);
|
|
conf_set_fontspec(conf, CONF_wideboldfont, fs);
|
|
fontspec_free(fs);
|
|
|
|
} else if (!strcmp(p, "-cs")) {
|
|
EXPECTS_ARG;
|
|
SECOND_PASS_ONLY;
|
|
conf_set_str(conf, CONF_line_codepage, val);
|
|
|
|
} else if (!strcmp(p, "-geometry")) {
|
|
EXPECTS_ARG;
|
|
SECOND_PASS_ONLY;
|
|
geometry_string = val;
|
|
} else if (!strcmp(p, "-sl")) {
|
|
EXPECTS_ARG;
|
|
SECOND_PASS_ONLY;
|
|
conf_set_int(conf, CONF_savelines, atoi(val));
|
|
|
|
} else if (!strcmp(p, "-fg") || !strcmp(p, "-bg") ||
|
|
!strcmp(p, "-bfg") || !strcmp(p, "-bbg") ||
|
|
!strcmp(p, "-cfg") || !strcmp(p, "-cbg")) {
|
|
EXPECTS_ARG;
|
|
SECOND_PASS_ONLY;
|
|
|
|
{
|
|
#if GTK_CHECK_VERSION(3,0,0)
|
|
GdkRGBA rgba;
|
|
bool success = gdk_rgba_parse(&rgba, val);
|
|
#else
|
|
GdkColor col;
|
|
bool success = gdk_color_parse(val, &col);
|
|
#endif
|
|
|
|
if (!success) {
|
|
err = true;
|
|
fprintf(stderr, "%s: unable to parse colour \"%s\"\n",
|
|
appname, val);
|
|
} else {
|
|
#if GTK_CHECK_VERSION(3,0,0)
|
|
int r = rgba.red * 255;
|
|
int g = rgba.green * 255;
|
|
int b = rgba.blue * 255;
|
|
#else
|
|
int r = col.red / 256;
|
|
int g = col.green / 256;
|
|
int b = col.blue / 256;
|
|
#endif
|
|
|
|
int index;
|
|
index = (!strcmp(p, "-fg") ? 0 :
|
|
!strcmp(p, "-bg") ? 2 :
|
|
!strcmp(p, "-bfg") ? 1 :
|
|
!strcmp(p, "-bbg") ? 3 :
|
|
!strcmp(p, "-cfg") ? 4 :
|
|
!strcmp(p, "-cbg") ? 5 : -1);
|
|
assert(index != -1);
|
|
|
|
conf_set_int_int(conf, CONF_colours, index*3+0, r);
|
|
conf_set_int_int(conf, CONF_colours, index*3+1, g);
|
|
conf_set_int_int(conf, CONF_colours, index*3+2, b);
|
|
}
|
|
}
|
|
|
|
} else if (use_pty_argv && !strcmp(p, "-e")) {
|
|
/* This option swallows all further arguments. */
|
|
if (!do_everything)
|
|
break;
|
|
|
|
if (nextarg) {
|
|
pty_argv = cmdline_arg_remainder(nextarg);
|
|
break; /* finished command-line processing */
|
|
} else
|
|
err = true, fprintf(stderr, "%s: -e expects an argument\n",
|
|
appname);
|
|
|
|
} else if (!strcmp(p, "-title")) {
|
|
EXPECTS_ARG;
|
|
SECOND_PASS_ONLY;
|
|
conf_set_str(conf, CONF_wintitle, val);
|
|
|
|
} else if (!strcmp(p, "-log")) {
|
|
EXPECTS_ARG;
|
|
SECOND_PASS_ONLY;
|
|
Filename *fn = cmdline_arg_to_filename(nextarg);
|
|
conf_set_filename(conf, CONF_logfilename, fn);
|
|
conf_set_int(conf, CONF_logtype, LGTYP_DEBUG);
|
|
filename_free(fn);
|
|
|
|
} else if (!strcmp(p, "-ut-") || !strcmp(p, "+ut")) {
|
|
SECOND_PASS_ONLY;
|
|
conf_set_bool(conf, CONF_stamp_utmp, false);
|
|
|
|
} else if (!strcmp(p, "-ut")) {
|
|
SECOND_PASS_ONLY;
|
|
conf_set_bool(conf, CONF_stamp_utmp, true);
|
|
|
|
} else if (!strcmp(p, "-ls-") || !strcmp(p, "+ls")) {
|
|
SECOND_PASS_ONLY;
|
|
conf_set_bool(conf, CONF_login_shell, false);
|
|
|
|
} else if (!strcmp(p, "-ls")) {
|
|
SECOND_PASS_ONLY;
|
|
conf_set_bool(conf, CONF_login_shell, true);
|
|
|
|
} else if (!strcmp(p, "-nethack")) {
|
|
SECOND_PASS_ONLY;
|
|
conf_set_bool(conf, CONF_nethack_keypad, true);
|
|
|
|
} else if (!strcmp(p, "-sb-") || !strcmp(p, "+sb")) {
|
|
SECOND_PASS_ONLY;
|
|
conf_set_bool(conf, CONF_scrollbar, false);
|
|
|
|
} else if (!strcmp(p, "-sb")) {
|
|
SECOND_PASS_ONLY;
|
|
conf_set_bool(conf, CONF_scrollbar, true);
|
|
|
|
} else if (!strcmp(p, "-name")) {
|
|
EXPECTS_ARG;
|
|
app_name = val;
|
|
|
|
} else if (!strcmp(p, "-xrm")) {
|
|
EXPECTS_ARG;
|
|
provide_xrm_string(val, appname);
|
|
|
|
} else if (!strcmp(p, "-help") || !strcmp(p, "--help")) {
|
|
help(stdout);
|
|
exit(0);
|
|
|
|
} else if (!strcmp(p, "-version") || !strcmp(p, "--version")) {
|
|
version(stdout);
|
|
exit(0);
|
|
|
|
} else if (!strcmp(p, "-pgpfp")) {
|
|
pgp_fingerprints();
|
|
exit(0);
|
|
|
|
} else if (has_ca_config_box &&
|
|
(!strcmp(p, "-host-ca") || !strcmp(p, "--host-ca") ||
|
|
!strcmp(p, "-host_ca") || !strcmp(p, "--host_ca"))) {
|
|
show_ca_config_box_synchronously();
|
|
exit(0);
|
|
|
|
} else if (p[0] != '-') {
|
|
/* Non-option arguments not handled by cmdline.c are errors. */
|
|
if (do_everything) {
|
|
err = true;
|
|
fprintf(stderr, "%s: unexpected non-option argument '%s'\n",
|
|
appname, p);
|
|
}
|
|
|
|
} else {
|
|
err = true;
|
|
fprintf(stderr, "%s: unrecognized option '%s'\n", appname, p);
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
GtkWidget *make_gtk_toplevel_window(GtkFrontend *frontend)
|
|
{
|
|
return gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
}
|
|
|
|
const bool buildinfo_gtk_relevant = true;
|
|
|
|
struct post_initial_config_box_ctx {
|
|
Conf *conf;
|
|
const char *geometry_string;
|
|
};
|
|
|
|
static void post_initial_config_box(void *vctx, int result)
|
|
{
|
|
struct post_initial_config_box_ctx ctx =
|
|
*(struct post_initial_config_box_ctx *)vctx;
|
|
sfree(vctx);
|
|
|
|
if (result > 0) {
|
|
new_session_window(ctx.conf, ctx.geometry_string);
|
|
} else {
|
|
/* In this main(), which only runs one session in total, a
|
|
* negative result from the initial config box means we simply
|
|
* terminate. */
|
|
conf_free(ctx.conf);
|
|
gtk_main_quit();
|
|
}
|
|
}
|
|
|
|
void session_window_closed(void)
|
|
{
|
|
gtk_main_quit();
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
Conf *conf;
|
|
bool need_config_box;
|
|
|
|
setlocale(LC_CTYPE, "");
|
|
enable_dit();
|
|
|
|
/* Call the function in ux{putty,pterm}.c to do app-type
|
|
* specific setup */
|
|
setup(true); /* true means we are a one-session process */
|
|
|
|
progname = argv[0];
|
|
|
|
/*
|
|
* Copy the original argv before letting gtk_init fiddle with
|
|
* it. It will be required later.
|
|
*/
|
|
{
|
|
int i, oldargc;
|
|
gtkargvstart = snewn(argc-1, char *);
|
|
for (i = 1; i < argc; i++)
|
|
gtkargvstart[i-1] = dupstr(argv[i]);
|
|
oldargc = argc;
|
|
gtk_init(&argc, &argv);
|
|
ngtkargs = oldargc - argc;
|
|
}
|
|
|
|
conf = conf_new();
|
|
|
|
gtkcomm_setup();
|
|
|
|
/*
|
|
* Block SIGPIPE: if we attempt Duplicate Session or similar and
|
|
* it falls over in some way, we certainly don't want SIGPIPE
|
|
* terminating the main pterm/PuTTY. However, we'll have to
|
|
* unblock it again when pterm forks.
|
|
*/
|
|
block_signal(SIGPIPE, true);
|
|
|
|
if (argc > 1 && !strncmp(argv[1], "---", 3)) {
|
|
read_dupsession_data(conf, argv[1]);
|
|
/* Splatter this argument so it doesn't clutter a ps listing */
|
|
smemclr(argv[1], strlen(argv[1]));
|
|
|
|
assert(!dup_check_launchable || conf_launchable(conf));
|
|
need_config_box = false;
|
|
} else {
|
|
if (do_cmdline(argc, argv, false, conf))
|
|
exit(1); /* pre-defaults pass to get -class */
|
|
do_defaults(NULL, conf);
|
|
if (do_cmdline(argc, argv, true, conf))
|
|
exit(1); /* post-defaults, do everything */
|
|
|
|
cmdline_run_saved(conf);
|
|
|
|
if (cmdline_tooltype & TOOLTYPE_HOST_ARG)
|
|
need_config_box = !cmdline_host_ok(conf);
|
|
else
|
|
need_config_box = false;
|
|
}
|
|
|
|
if (need_config_box) {
|
|
/*
|
|
* Put up the initial config box, which will pass the provided
|
|
* parameters (with conf updated) to new_session_window() when
|
|
* (if) the user selects Open. Or it might close without
|
|
* creating a session window, if the user selects Cancel. Or
|
|
* it might just create the session window immediately if this
|
|
* is a pterm-style app which doesn't have an initial config
|
|
* box at all.
|
|
*/
|
|
struct post_initial_config_box_ctx *ctx =
|
|
snew(struct post_initial_config_box_ctx);
|
|
ctx->conf = conf;
|
|
ctx->geometry_string = geometry_string;
|
|
initial_config_box(conf, post_initial_config_box, ctx);
|
|
} else {
|
|
/*
|
|
* No initial config needed; just create the session window
|
|
* now.
|
|
*/
|
|
new_session_window(conf, geometry_string);
|
|
}
|
|
|
|
gtk_main();
|
|
|
|
return 0;
|
|
}
|