Skip to content

Commit

Permalink
New timing infrastructure. There's a new function schedule_timer()
Browse files Browse the repository at this point in the history
which pretty much any module can call to request a call-back in the
future. So terminal.c can do its own handling of blinking, visual
bells and deferred screen updates, without having to rely on
term_update() being called 50 times a second (fixes: pterm-timer);
and ssh.c and telnet.c both invoke a new module pinger.c which takes
care of sending keepalives, so they get sent uniformly in all front
ends (fixes: plink-keepalives, unix-keepalives).

[originally from svn r4906]
[this svn revision also touched putty-wishlist]
  • Loading branch information
sgtatham committed Nov 27, 2004
1 parent d609e1f commit 7ecf135
Show file tree
Hide file tree
Showing 30 changed files with 1,106 additions and 366 deletions.
15 changes: 7 additions & 8 deletions Recipe
Original file line number Diff line number Diff line change
Expand Up @@ -182,14 +182,15 @@ GUITERM = TERMINAL window windlg winctrls sizetip winucs winprint

# Same thing on Unix.
UXTERM = TERMINAL pterm uxcfg gtkdlg gtkcols gtkpanel uxucs uxprint xkeysym
+ timing

# Non-SSH back ends (putty, puttytel, plink).
NONSSH = telnet raw rlogin ldisc
NONSSH = telnet raw rlogin ldisc pinger

# SSH back end (putty, plink, pscp, psftp).
SSH = ssh sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf
+ sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd
+ sshaes sshsh512 sshbn wildcard
+ sshaes sshsh512 sshbn wildcard pinger
WINSSH = SSH winnoise winpgntc
UXSSH = SSH uxnoise uxagentc
MACSSH = SSH macnoise
Expand All @@ -199,12 +200,10 @@ SFTP = sftp int64 logging

# Miscellaneous objects appearing in all the network utilities (not
# Pageant or PuTTYgen).
WINMISC = misc version winstore settings tree234 winnet proxy cmdline
+ windefs winmisc pproxy
UXMISC = misc version uxstore settings tree234 uxsel uxnet proxy cmdline
+ uxmisc uxproxy
MACMISC = misc version macstore settings tree234 macnet mtcpnet otnet proxy
+ macmisc macabout pproxy
MISC = timing misc version settings tree234 proxy
WINMISC = MISC winstore winnet cmdline windefs winmisc pproxy
UXMISC = MISC uxstore uxsel uxnet cmdline uxmisc uxproxy
MACMISC = MISC macstore macnet mtcpnet otnet macmisc macabout pproxy

# Character set library, for use in pterm.
CHARSET = sbcsdat slookup sbcs utf8 toucs fromucs xenc mimeenc macenc localenc
Expand Down
20 changes: 11 additions & 9 deletions doc/udp.but
Original file line number Diff line number Diff line change
Expand Up @@ -75,21 +75,23 @@ Some ports of PuTTY - notably the in-progress Mac port - are
constrained by the operating system to run as a single process
potentially managing multiple sessions.

Therefore, the platform-independent parts of PuTTY use \e{hardly
any} global variables. The very few that do exist, such as
\c{flags}, are tolerated because they are not specific to a
particular login session: instead, they define properties that are
expected to apply equally to \e{all} the sessions run by a single
PuTTY process. Any data that is specific to a particular network
session is stored in dynamically allocated data structures, and
pointers to these structures are passed around between functions.
Therefore, the platform-independent parts of PuTTY never use global
variables to store per-session data. The global variables that do
exist are tolerated because they are not specific to a particular
login session: \c{flags} defines properties that are expected to
apply equally to \e{all} the sessions run by a single PuTTY process,
the random number state in \cw{sshrand.c} and the timer list in
\cw{timing.c} serve all sessions equally, and so on. But most data
is specific to a particular network session, and is therefore stored
in dynamically allocated data structures, and pointers to these
structures are passed around between functions.

Platform-specific code can reverse this decision if it likes. The
Windows code, for historical reasons, stores most of its data as
global variables. That's OK, because \e{on Windows} we know there is
only one session per PuTTY process, so it's safe to do that. But
changes to the platform-independent code should avoid introducing
any more global variables than already exist.
global variables, unless they are genuinely cross-session.

\H{udp-pure-c} C, not C++

Expand Down
3 changes: 1 addition & 2 deletions mac/macterm.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* $Id: macterm.c,v 1.78 2004/10/14 16:42:43 simon Exp $ */
/* $Id$ */
/*
* Copyright (c) 1999 Simon Tatham
* Copyright (c) 1999, 2002 Ben Harris
Expand Down Expand Up @@ -314,7 +314,6 @@ void mac_pollterm(void)
Session *s;

for (s = sesslist; s != NULL; s = s->next) {
term_out(s->term);
term_update(s->term);
}
}
Expand Down
23 changes: 23 additions & 0 deletions misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,29 @@ char *dupvprintf(const char *fmt, va_list ap)
}
}

/*
* Read an entire line of text from a file. Return a buffer
* malloced to be as big as necessary (caller must free).
*/
char *fgetline(FILE *fp)
{
char *ret = snewn(512, char);
int size = 512, len = 0;
while (fgets(ret + len, size - len, fp)) {
len += strlen(ret + len);
if (ret[len-1] == '\n')
break; /* got a newline, we're done */
size = len + 512;
ret = sresize(ret, size, char);
}
if (len == 0) { /* first fgets returned NULL */
sfree(ret);
return NULL;
}
ret[len] = '\0';
return ret;
}

/* ----------------------------------------------------------------------
* Base64 encoding routine. This is required in public-key writing
* but also in HTTP proxy handling, so it's centralised here.
Expand Down
3 changes: 3 additions & 0 deletions misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "puttymem.h"

#include <stdio.h> /* for FILE * */
#include <stdarg.h> /* for va_list */

#ifndef FALSE
Expand All @@ -20,6 +21,8 @@ char *dupcat(const char *s1, ...);
char *dupprintf(const char *fmt, ...);
char *dupvprintf(const char *fmt, va_list ap);

char *fgetline(FILE *fp);

void base64_encode_atom(unsigned char *data, int n, char *out);

struct bufchain_granule;
Expand Down
71 changes: 71 additions & 0 deletions pinger.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* pinger.c: centralised module that deals with sending TS_PING
* keepalives, to avoid replicating this code in multiple backends.
*/

#include "putty.h"

struct pinger_tag {
int interval;
int pending;
long next;
Backend *back;
void *backhandle;
};

static void pinger_schedule(Pinger pinger);

static void pinger_timer(void *ctx, long now)
{
Pinger pinger = (Pinger)ctx;

if (pinger->pending && now - pinger->next >= 0) {
pinger->back->special(pinger->backhandle, TS_PING);
pinger->pending = FALSE;
pinger_schedule(pinger);
}
}

static void pinger_schedule(Pinger pinger)
{
int next;

if (!pinger->interval) {
pinger->pending = FALSE; /* cancel any pending ping */
return;
}

next = schedule_timer(pinger->interval * TICKSPERSEC,
pinger_timer, pinger);
if (!pinger->pending || next < pinger->next) {
pinger->next = next;
pinger->pending = TRUE;
}
}

Pinger pinger_new(Config *cfg, Backend *back, void *backhandle)
{
Pinger pinger = snew(struct pinger_tag);

pinger->interval = cfg->ping_interval;
pinger->pending = FALSE;
pinger->back = back;
pinger->backhandle = backhandle;
pinger_schedule(pinger);

return pinger;
}

void pinger_reconfig(Pinger pinger, Config *oldcfg, Config *newcfg)
{
if (oldcfg->ping_interval != newcfg->ping_interval) {
pinger->interval = newcfg->ping_interval;
pinger_schedule(pinger);
}
}

void pinger_free(Pinger pinger)
{
expire_timer_context(pinger);
sfree(pinger);
}
50 changes: 20 additions & 30 deletions psftp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1361,45 +1361,34 @@ static int sftp_cmd_help(struct sftp_command *cmd)
struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags)
{
char *line;
int linelen, linesize;
struct sftp_command *cmd;
char *p, *q, *r;
int quoting;

if ((mode == 0) || (modeflags & 1)) {
printf("psftp> ");
}
fflush(stdout);

cmd = snew(struct sftp_command);
cmd->words = NULL;
cmd->nwords = 0;
cmd->wordssize = 0;

line = NULL;
linesize = linelen = 0;
while (1) {
int len;
char *ret;

linesize += 512;
line = sresize(line, linesize, char);
ret = fgets(line + linelen, linesize - linelen, fp);

if (!ret || (linelen == 0 && line[0] == '\0')) {
cmd->obey = sftp_cmd_quit;
if ((mode == 0) || (modeflags & 1))
printf("quit\n");
return cmd; /* eof */
}
len = linelen + strlen(line + linelen);
linelen += len;
if (line[linelen - 1] == '\n') {
linelen--;
line[linelen] = '\0';
break;
}

if (fp) {
if (modeflags & 1)
printf("psftp> ");
line = fgetline(fp);
} else {
line = ssh_sftp_get_cmdline("psftp> ");
}

if (!line || !*line) {
cmd->obey = sftp_cmd_quit;
if ((mode == 0) || (modeflags & 1))
printf("quit\n");
return cmd; /* eof */
}

line[strcspn(line, "\r\n")] = '\0';

if (modeflags & 1) {
printf("%s\n", line);
}
Expand Down Expand Up @@ -1464,7 +1453,8 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags)
}
}

sfree(line);
sfree(line);

/*
* Now parse the first word and assign a function.
*/
Expand Down Expand Up @@ -1551,7 +1541,7 @@ void do_sftp(int mode, int modeflags, char *batchfile)
*/
while (1) {
struct sftp_command *cmd;
cmd = sftp_getcmd(stdin, 0, 0);
cmd = sftp_getcmd(NULL, 0, 0);
if (!cmd)
break;
ret = cmd->obey(cmd);
Expand Down
6 changes: 6 additions & 0 deletions psftp.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ void get_file_times(char *filename, unsigned long *mtime,
*/
int ssh_sftp_loop_iteration(void);

/*
* Read a command line for PSFTP from standard input. Caller must
* free.
*/
char *ssh_sftp_get_cmdline(char *prompt);

/*
* The main program in psftp.c. Called from main() in the platform-
* specific code, after doing any platform-specific initialisation.
Expand Down
44 changes: 44 additions & 0 deletions putty.h
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,7 @@ void ldisc_update(void *frontend, int echo, int edit);
* shutdown. */
void update_specials_menu(void *frontend);
int from_backend(void *frontend, int is_stderr, const char *data, int len);
void notify_remote_exit(void *frontend);
#define OPTIMISE_IS_SCROLL 1

void set_iconic(void *frontend, int iconic);
Expand Down Expand Up @@ -744,6 +745,14 @@ int random_byte(void);
void random_get_savedata(void **data, int *len);
extern int random_active;

/*
* Exports from pinger.c.
*/
typedef struct pinger_tag *Pinger;
Pinger pinger_new(Config *cfg, Backend *back, void *backhandle);
void pinger_reconfig(Pinger, Config *oldcfg, Config *newcfg);
void pinger_free(Pinger);

/*
* Exports from misc.c.
*/
Expand Down Expand Up @@ -895,4 +904,39 @@ int filename_is_null(Filename fn);
char *get_username(void); /* return value needs freeing */
char *get_random_data(int bytes); /* used in cmdgen.c */

/*
* Exports and imports from timing.c.
*
* schedule_timer() asks the front end to schedule a callback to a
* timer function in a given number of ticks. The returned value is
* the time (in ticks since an arbitrary offset) at which the
* callback can be expected. This value will also be passed as the
* `now' parameter to the callback function. Hence, you can (for
* example) schedule an event at a particular time by calling
* schedule_timer() and storing the return value in your context
* structure as the time when that event is due. The first time a
* callback function gives you that value or more as `now', you do
* the thing.
*
* expire_timer_context() drops all current timers associated with
* a given value of ctx (for when you're about to free ctx).
*
* run_timers() is called from the front end when it has reason to
* think some timers have reached their moment, or when it simply
* needs to know how long to wait next. We pass it the time we
* think it is. It returns TRUE and places the time when the next
* timer needs to go off in `next', or alternatively it returns
* FALSE if there are no timers at all pending.
*
* timer_change_notify() must be supplied by the front end; it
* notifies the front end that a new timer has been added to the
* list which is sooner than any existing ones. It provides the
* time when that timer needs to go off.
*/
typedef void (*timer_fn_t)(void *ctx, long now);
long schedule_timer(int ticks, timer_fn_t fn, void *ctx);
void expire_timer_context(void *ctx);
int run_timers(long now, long *next);
void timer_change_notify(long next);

#endif
1 change: 1 addition & 0 deletions raw.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ static int raw_closing(Plug plug, const char *error_msg, int error_code,
if (raw->s) {
sk_close(raw->s);
raw->s = NULL;
notify_remote_exit(raw->frontend);
}
if (error_msg) {
/* A socket error has occurred. */
Expand Down
1 change: 1 addition & 0 deletions rlogin.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ static int rlogin_closing(Plug plug, const char *error_msg, int error_code,
if (rlogin->s) {
sk_close(rlogin->s);
rlogin->s = NULL;
notify_remote_exit(rlogin->frontend);
}
if (error_msg) {
/* A socket error has occurred. */
Expand Down
Loading

0 comments on commit 7ecf135

Please sign in to comment.